Skip to content

Presidio Analyzer API Reference

Objects at the top of the presidio-analyzer package

presidio_analyzer.AnalyzerEngine

Entry point for Presidio Analyzer.

Orchestrating the detection of PII entities and all related logic.

PARAMETER DESCRIPTION
registry

instance of type RecognizerRegistry

TYPE: RecognizerRegistry DEFAULT: None

nlp_engine

instance of type NlpEngine (for example SpacyNlpEngine)

TYPE: NlpEngine DEFAULT: None

app_tracer

instance of type AppTracer, used to trace the logic used during each request for interpretability reasons.

TYPE: AppTracer DEFAULT: None

log_decision_process

bool, defines whether the decision process within the analyzer should be logged or not.

TYPE: bool DEFAULT: False

default_score_threshold

Minimum confidence value for detected entities to be returned

TYPE: float DEFAULT: 0

supported_languages

List of possible languages this engine could be run on. Used for loading the right NLP models and recognizers for these languages.

TYPE: List[str] DEFAULT: None

context_aware_enhancer

instance of type ContextAwareEnhancer for enhancing confidence score based on context words, (LemmaContextAwareEnhancer will be created by default if None passed)

TYPE: Optional[ContextAwareEnhancer] DEFAULT: None

METHOD DESCRIPTION
get_recognizers

Return a list of PII recognizers currently loaded.

get_supported_entities

Return a list of the entities that can be detected.

analyze

Find PII entities in text using different PII recognizers for a given language.

Source code in presidio_analyzer/analyzer_engine.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
class AnalyzerEngine:
    """
    Entry point for Presidio Analyzer.

    Orchestrating the detection of PII entities and all related logic.

    :param registry: instance of type RecognizerRegistry
    :param nlp_engine: instance of type NlpEngine
    (for example SpacyNlpEngine)
    :param app_tracer: instance of type AppTracer, used to trace the logic
    used during each request for interpretability reasons.
    :param log_decision_process: bool,
    defines whether the decision process within the analyzer should be logged or not.
    :param default_score_threshold: Minimum confidence value
    for detected entities to be returned
    :param supported_languages: List of possible languages this engine could be run on.
    Used for loading the right NLP models and recognizers for these languages.
    :param context_aware_enhancer: instance of type ContextAwareEnhancer for enhancing
    confidence score based on context words, (LemmaContextAwareEnhancer will be created
    by default if None passed)
    """

    def __init__(
        self,
        registry: RecognizerRegistry = None,
        nlp_engine: NlpEngine = None,
        app_tracer: AppTracer = None,
        log_decision_process: bool = False,
        default_score_threshold: float = 0,
        supported_languages: List[str] = None,
        context_aware_enhancer: Optional[ContextAwareEnhancer] = None,
    ):
        if not supported_languages:
            supported_languages = ["en"]

        if not nlp_engine:
            logger.info("nlp_engine not provided, creating default.")
            provider = NlpEngineProvider()
            nlp_engine = provider.create_engine()

        if not app_tracer:
            app_tracer = AppTracer()
        self.app_tracer = app_tracer

        self.supported_languages = supported_languages

        self.nlp_engine = nlp_engine
        if not self.nlp_engine.is_loaded():
            self.nlp_engine.load()

        if not registry:
            logger.info("registry not provided, creating default.")
            provider = RecognizerRegistryProvider(
                registry_configuration={"supported_languages": self.supported_languages}
            )
            registry = provider.create_recognizer_registry()
            registry.add_nlp_recognizer(nlp_engine=self.nlp_engine)
        else:
            if Counter(registry.supported_languages) != Counter(
                self.supported_languages
            ):
                raise ValueError(
                    f"Misconfigured engine, supported languages have to be consistent"
                    f"registry.supported_languages: {registry.supported_languages}, "
                    f"analyzer_engine.supported_languages: {self.supported_languages}"
                )

        # added to support the previous interface
        if not registry.recognizers:
            registry.load_predefined_recognizers(
                nlp_engine=self.nlp_engine, languages=self.supported_languages
            )

        self.registry = registry

        self.log_decision_process = log_decision_process
        self.default_score_threshold = default_score_threshold

        if not context_aware_enhancer:
            logger.debug(
                "context aware enhancer not provided, creating default"
                + " lemma based enhancer."
            )
            context_aware_enhancer = LemmaContextAwareEnhancer()

        self.context_aware_enhancer = context_aware_enhancer

    def get_recognizers(self, language: Optional[str] = None) -> List[EntityRecognizer]:
        """
        Return a list of PII recognizers currently loaded.

        :param language: Return the recognizers supporting a given language.
        :return: List of [Recognizer] as a RecognizersAllResponse
        """
        if not language:
            languages = self.supported_languages
        else:
            languages = [language]

        recognizers = []
        for language in languages:
            logger.info(f"Fetching all recognizers for language {language}")
            recognizers.extend(
                self.registry.get_recognizers(language=language, all_fields=True)
            )

        return list(set(recognizers))

    def get_supported_entities(self, language: Optional[str] = None) -> List[str]:
        """
        Return a list of the entities that can be detected.

        :param language: Return only entities supported in a specific language.
        :return: List of entity names
        """
        recognizers = self.get_recognizers(language=language)
        supported_entities = []
        for recognizer in recognizers:
            supported_entities.extend(recognizer.get_supported_entities())

        return list(set(supported_entities))

    def analyze(
        self,
        text: str,
        language: str,
        entities: Optional[List[str]] = None,
        correlation_id: Optional[str] = None,
        score_threshold: Optional[float] = None,
        return_decision_process: Optional[bool] = False,
        ad_hoc_recognizers: Optional[List[EntityRecognizer]] = None,
        context: Optional[List[str]] = None,
        allow_list: Optional[List[str]] = None,
        allow_list_match: Optional[str] = "exact",
        regex_flags: Optional[int] = re.DOTALL | re.MULTILINE | re.IGNORECASE,
        nlp_artifacts: Optional[NlpArtifacts] = None,
    ) -> List[RecognizerResult]:
        """
        Find PII entities in text using different PII recognizers for a given language.

        :param text: the text to analyze
        :param language: the language of the text
        :param entities: List of PII entities that should be looked for in the text.
        If entities=None then all entities are looked for.
        :param correlation_id: cross call ID for this request
        :param score_threshold: A minimum value for which
        to return an identified entity
        :param return_decision_process: Whether the analysis decision process steps
        returned in the response.
        :param ad_hoc_recognizers: List of recognizers which will be used only
        for this specific request.
        :param context: List of context words to enhance confidence score if matched
        with the recognized entity's recognizer context
        :param allow_list: List of words that the user defines as being allowed to keep
        in the text
        :param allow_list_match: How the allow_list should be interpreted; either as "exact" or as "regex".
        - If `regex`, results which match with any regex condition in the allow_list would be allowed and not be returned as potential PII.
        - if `exact`, results which exactly match any value in the allow_list would be allowed and not be returned as potential PII.
        :param regex_flags: regex flags to be used for when allow_list_match is "regex"
        :param nlp_artifacts: precomputed NlpArtifacts
        :return: an array of the found entities in the text

        :Example:

        ```python
        from presidio_analyzer import AnalyzerEngine

        # Set up the engine, loads the NLP module (spaCy model by default)
        # and other PII recognizers
        analyzer = AnalyzerEngine()

        # Call analyzer to get results
        results = analyzer.analyze(text='My phone number is 212-555-5555', entities=['PHONE_NUMBER'], language='en')
        print(results)
        ```

        """  # noqa: E501

        all_fields = not entities

        recognizers = self.registry.get_recognizers(
            language=language,
            entities=entities,
            all_fields=all_fields,
            ad_hoc_recognizers=ad_hoc_recognizers,
        )

        if all_fields:
            # Since all_fields=True, list all entities by iterating
            # over all recognizers
            entities = self.get_supported_entities(language=language)

        # run the nlp pipeline over the given text, store the results in
        # a NlpArtifacts instance
        if not nlp_artifacts:
            nlp_artifacts = self.nlp_engine.process_text(text, language)

        if self.log_decision_process:
            self.app_tracer.trace(
                correlation_id, "nlp artifacts:" + nlp_artifacts.to_json()
            )

        results = []
        for recognizer in recognizers:
            # Lazy loading of the relevant recognizers
            if not recognizer.is_loaded:
                recognizer.load()
                recognizer.is_loaded = True

            # analyze using the current recognizer and append the results
            current_results = recognizer.analyze(
                text=text, entities=entities, nlp_artifacts=nlp_artifacts
            )
            if current_results:
                # add recognizer name to recognition metadata inside results
                # if not exists
                self.__add_recognizer_id_if_not_exists(current_results, recognizer)
                results.extend(current_results)

        results = self._enhance_using_context(
            text, results, nlp_artifacts, recognizers, context
        )

        if self.log_decision_process:
            self.app_tracer.trace(
                correlation_id,
                json.dumps([str(result.to_dict()) for result in results]),
            )

        # Remove duplicates or low score results
        results = EntityRecognizer.remove_duplicates(results)
        results = self.__remove_low_scores(results, score_threshold)

        if allow_list:
            results = self._remove_allow_list(
                results, allow_list, text, regex_flags, allow_list_match
            )

        if not return_decision_process:
            results = self.__remove_decision_process(results)

        return results

    def _enhance_using_context(
        self,
        text: str,
        raw_results: List[RecognizerResult],
        nlp_artifacts: NlpArtifacts,
        recognizers: List[EntityRecognizer],
        context: Optional[List[str]] = None,
    ) -> List[RecognizerResult]:
        """
        Enhance confidence score using context words.

        :param text: The actual text that was analyzed
        :param raw_results: Recognizer results which didn't take
                            context into consideration
        :param nlp_artifacts: The nlp artifacts contains elements
                              such as lemmatized tokens for better
                              accuracy of the context enhancement process
        :param recognizers: the list of recognizers
        :param context: list of context words
        """
        results = []

        for recognizer in recognizers:
            recognizer_results = [
                r
                for r in raw_results
                if r.recognition_metadata[RecognizerResult.RECOGNIZER_IDENTIFIER_KEY]
                == recognizer.id
            ]
            other_recognizer_results = [
                r
                for r in raw_results
                if r.recognition_metadata[RecognizerResult.RECOGNIZER_IDENTIFIER_KEY]
                != recognizer.id
            ]

            # enhance score using context in recognizer level if implemented
            recognizer_results = recognizer.enhance_using_context(
                text=text,
                # each recognizer will get access to all recognizer results
                # to allow related entities contex enhancement
                raw_recognizer_results=recognizer_results,
                other_raw_recognizer_results=other_recognizer_results,
                nlp_artifacts=nlp_artifacts,
                context=context,
            )

            results.extend(recognizer_results)

        # Update results in case surrounding words or external context are relevant to
        # the context words.
        results = self.context_aware_enhancer.enhance_using_context(
            text=text,
            raw_results=results,
            nlp_artifacts=nlp_artifacts,
            recognizers=recognizers,
            context=context,
        )

        return results

    def __remove_low_scores(
        self, results: List[RecognizerResult], score_threshold: float = None
    ) -> List[RecognizerResult]:
        """
        Remove results for which the confidence is lower than the threshold.

        :param results: List of RecognizerResult
        :param score_threshold: float value for minimum possible confidence
        :return: List[RecognizerResult]
        """
        if score_threshold is None:
            score_threshold = self.default_score_threshold

        new_results = [result for result in results if result.score >= score_threshold]
        return new_results

    @staticmethod
    def _remove_allow_list(
        results: List[RecognizerResult],
        allow_list: List[str],
        text: str,
        regex_flags: Optional[int],
        allow_list_match: str,
    ) -> List[RecognizerResult]:
        """
        Remove results which are part of the allow list.

        :param results: List of RecognizerResult
        :param allow_list: list of allowed terms
        :param text: the text to analyze
        :param regex_flags: regex flags to be used for when allow_list_match is "regex"
        :param allow_list_match: How the allow_list
        should be interpreted; either as "exact" or as "regex"
        :return: List[RecognizerResult]
        """
        new_results = []
        if allow_list_match == "regex":
            allow_list = [term for term in allow_list if term]
            if not allow_list:
                return list(results)
            pattern = "|".join(allow_list)
            re_compiled = re.compile(pattern, flags=regex_flags)

            for result in results:
                word = text[result.start : result.end]

                # if the word is not specified to be allowed, keep in the PII entities
                try:
                    if not re_compiled.search(word, timeout=REGEX_TIMEOUT_SECONDS):
                        new_results.append(result)
                except TimeoutError:
                    logger.warning(
                        "Allow list regex timed out after %s seconds"
                        " (word length: %d), keeping result.",
                        REGEX_TIMEOUT_SECONDS,
                        len(word),
                        exc_info=True,
                    )
                    new_results.append(result)
        elif allow_list_match == "exact":
            for result in results:
                word = text[result.start : result.end]

                # if the word is not specified to be allowed, keep in the PII entities
                if word not in allow_list:
                    new_results.append(result)
        else:
            raise ValueError(
                "allow_list_match must either be set to 'exact' or 'regex'."
            )

        return new_results

    @staticmethod
    def __add_recognizer_id_if_not_exists(
        results: List[RecognizerResult], recognizer: EntityRecognizer
    ) -> None:
        """Ensure recognition metadata with recognizer id existence.

        Ensure recognizer result list contains recognizer id inside recognition
        metadata dictionary, and if not create it. recognizer_id is needed
        for context aware enhancement.

        :param results: List of RecognizerResult
        :param recognizer: Entity recognizer
        """
        for result in results:
            if not result.recognition_metadata:
                result.recognition_metadata = dict()
            if (
                RecognizerResult.RECOGNIZER_IDENTIFIER_KEY
                not in result.recognition_metadata
            ):
                result.recognition_metadata[
                    RecognizerResult.RECOGNIZER_IDENTIFIER_KEY
                ] = recognizer.id
            if RecognizerResult.RECOGNIZER_NAME_KEY not in result.recognition_metadata:
                result.recognition_metadata[RecognizerResult.RECOGNIZER_NAME_KEY] = (
                    recognizer.name
                )

    @staticmethod
    def __remove_decision_process(
        results: List[RecognizerResult],
    ) -> List[RecognizerResult]:
        """Remove decision process / analysis explanation from response."""

        for result in results:
            result.analysis_explanation = None

        return results

get_recognizers

get_recognizers(language: Optional[str] = None) -> List[EntityRecognizer]

Return a list of PII recognizers currently loaded.

PARAMETER DESCRIPTION
language

Return the recognizers supporting a given language.

TYPE: Optional[str] DEFAULT: None

RETURNS DESCRIPTION
List[EntityRecognizer]

List of [Recognizer] as a RecognizersAllResponse

Source code in presidio_analyzer/analyzer_engine.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def get_recognizers(self, language: Optional[str] = None) -> List[EntityRecognizer]:
    """
    Return a list of PII recognizers currently loaded.

    :param language: Return the recognizers supporting a given language.
    :return: List of [Recognizer] as a RecognizersAllResponse
    """
    if not language:
        languages = self.supported_languages
    else:
        languages = [language]

    recognizers = []
    for language in languages:
        logger.info(f"Fetching all recognizers for language {language}")
        recognizers.extend(
            self.registry.get_recognizers(language=language, all_fields=True)
        )

    return list(set(recognizers))

get_supported_entities

get_supported_entities(language: Optional[str] = None) -> List[str]

Return a list of the entities that can be detected.

PARAMETER DESCRIPTION
language

Return only entities supported in a specific language.

TYPE: Optional[str] DEFAULT: None

RETURNS DESCRIPTION
List[str]

List of entity names

Source code in presidio_analyzer/analyzer_engine.py
136
137
138
139
140
141
142
143
144
145
146
147
148
def get_supported_entities(self, language: Optional[str] = None) -> List[str]:
    """
    Return a list of the entities that can be detected.

    :param language: Return only entities supported in a specific language.
    :return: List of entity names
    """
    recognizers = self.get_recognizers(language=language)
    supported_entities = []
    for recognizer in recognizers:
        supported_entities.extend(recognizer.get_supported_entities())

    return list(set(supported_entities))

analyze

analyze(
    text: str,
    language: str,
    entities: Optional[List[str]] = None,
    correlation_id: Optional[str] = None,
    score_threshold: Optional[float] = None,
    return_decision_process: Optional[bool] = False,
    ad_hoc_recognizers: Optional[List[EntityRecognizer]] = None,
    context: Optional[List[str]] = None,
    allow_list: Optional[List[str]] = None,
    allow_list_match: Optional[str] = "exact",
    regex_flags: Optional[int] = re.DOTALL | re.MULTILINE | re.IGNORECASE,
    nlp_artifacts: Optional[NlpArtifacts] = None,
) -> List[RecognizerResult]

Find PII entities in text using different PII recognizers for a given language.

:Example:

from presidio_analyzer import AnalyzerEngine

# Set up the engine, loads the NLP module (spaCy model by default)
# and other PII recognizers
analyzer = AnalyzerEngine()

# Call analyzer to get results
results = analyzer.analyze(text='My phone number is 212-555-5555', entities=['PHONE_NUMBER'], language='en')
print(results)
PARAMETER DESCRIPTION
text

the text to analyze

TYPE: str

language

the language of the text

TYPE: str

entities

List of PII entities that should be looked for in the text. If entities=None then all entities are looked for.

TYPE: Optional[List[str]] DEFAULT: None

correlation_id

cross call ID for this request

TYPE: Optional[str] DEFAULT: None

score_threshold

A minimum value for which to return an identified entity

TYPE: Optional[float] DEFAULT: None

return_decision_process

Whether the analysis decision process steps returned in the response.

TYPE: Optional[bool] DEFAULT: False

ad_hoc_recognizers

List of recognizers which will be used only for this specific request.

TYPE: Optional[List[EntityRecognizer]] DEFAULT: None

context

List of context words to enhance confidence score if matched with the recognized entity's recognizer context

TYPE: Optional[List[str]] DEFAULT: None

allow_list

List of words that the user defines as being allowed to keep in the text

TYPE: Optional[List[str]] DEFAULT: None

allow_list_match

How the allow_list should be interpreted; either as "exact" or as "regex". - If regex, results which match with any regex condition in the allow_list would be allowed and not be returned as potential PII. - if exact, results which exactly match any value in the allow_list would be allowed and not be returned as potential PII.

TYPE: Optional[str] DEFAULT: 'exact'

regex_flags

regex flags to be used for when allow_list_match is "regex"

TYPE: Optional[int] DEFAULT: DOTALL | MULTILINE | IGNORECASE

nlp_artifacts

precomputed NlpArtifacts

TYPE: Optional[NlpArtifacts] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]

an array of the found entities in the text

Source code in presidio_analyzer/analyzer_engine.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
def analyze(
    self,
    text: str,
    language: str,
    entities: Optional[List[str]] = None,
    correlation_id: Optional[str] = None,
    score_threshold: Optional[float] = None,
    return_decision_process: Optional[bool] = False,
    ad_hoc_recognizers: Optional[List[EntityRecognizer]] = None,
    context: Optional[List[str]] = None,
    allow_list: Optional[List[str]] = None,
    allow_list_match: Optional[str] = "exact",
    regex_flags: Optional[int] = re.DOTALL | re.MULTILINE | re.IGNORECASE,
    nlp_artifacts: Optional[NlpArtifacts] = None,
) -> List[RecognizerResult]:
    """
    Find PII entities in text using different PII recognizers for a given language.

    :param text: the text to analyze
    :param language: the language of the text
    :param entities: List of PII entities that should be looked for in the text.
    If entities=None then all entities are looked for.
    :param correlation_id: cross call ID for this request
    :param score_threshold: A minimum value for which
    to return an identified entity
    :param return_decision_process: Whether the analysis decision process steps
    returned in the response.
    :param ad_hoc_recognizers: List of recognizers which will be used only
    for this specific request.
    :param context: List of context words to enhance confidence score if matched
    with the recognized entity's recognizer context
    :param allow_list: List of words that the user defines as being allowed to keep
    in the text
    :param allow_list_match: How the allow_list should be interpreted; either as "exact" or as "regex".
    - If `regex`, results which match with any regex condition in the allow_list would be allowed and not be returned as potential PII.
    - if `exact`, results which exactly match any value in the allow_list would be allowed and not be returned as potential PII.
    :param regex_flags: regex flags to be used for when allow_list_match is "regex"
    :param nlp_artifacts: precomputed NlpArtifacts
    :return: an array of the found entities in the text

    :Example:

    ```python
    from presidio_analyzer import AnalyzerEngine

    # Set up the engine, loads the NLP module (spaCy model by default)
    # and other PII recognizers
    analyzer = AnalyzerEngine()

    # Call analyzer to get results
    results = analyzer.analyze(text='My phone number is 212-555-5555', entities=['PHONE_NUMBER'], language='en')
    print(results)
    ```

    """  # noqa: E501

    all_fields = not entities

    recognizers = self.registry.get_recognizers(
        language=language,
        entities=entities,
        all_fields=all_fields,
        ad_hoc_recognizers=ad_hoc_recognizers,
    )

    if all_fields:
        # Since all_fields=True, list all entities by iterating
        # over all recognizers
        entities = self.get_supported_entities(language=language)

    # run the nlp pipeline over the given text, store the results in
    # a NlpArtifacts instance
    if not nlp_artifacts:
        nlp_artifacts = self.nlp_engine.process_text(text, language)

    if self.log_decision_process:
        self.app_tracer.trace(
            correlation_id, "nlp artifacts:" + nlp_artifacts.to_json()
        )

    results = []
    for recognizer in recognizers:
        # Lazy loading of the relevant recognizers
        if not recognizer.is_loaded:
            recognizer.load()
            recognizer.is_loaded = True

        # analyze using the current recognizer and append the results
        current_results = recognizer.analyze(
            text=text, entities=entities, nlp_artifacts=nlp_artifacts
        )
        if current_results:
            # add recognizer name to recognition metadata inside results
            # if not exists
            self.__add_recognizer_id_if_not_exists(current_results, recognizer)
            results.extend(current_results)

    results = self._enhance_using_context(
        text, results, nlp_artifacts, recognizers, context
    )

    if self.log_decision_process:
        self.app_tracer.trace(
            correlation_id,
            json.dumps([str(result.to_dict()) for result in results]),
        )

    # Remove duplicates or low score results
    results = EntityRecognizer.remove_duplicates(results)
    results = self.__remove_low_scores(results, score_threshold)

    if allow_list:
        results = self._remove_allow_list(
            results, allow_list, text, regex_flags, allow_list_match
        )

    if not return_decision_process:
        results = self.__remove_decision_process(results)

    return results

presidio_analyzer.analyzer_engine_provider.AnalyzerEngineProvider

Utility function for loading Presidio Analyzer.

Use this class to load presidio analyzer engine from a yaml file

PARAMETER DESCRIPTION
analyzer_engine_conf_file

the path to the analyzer configuration file

TYPE: Optional[Union[Path, str]] DEFAULT: None

nlp_engine_conf_file

the path to the nlp engine configuration file

TYPE: Optional[Union[Path, str]] DEFAULT: None

recognizer_registry_conf_file

the path to the recognizer registry configuration file

TYPE: Optional[Union[Path, str]] DEFAULT: None

METHOD DESCRIPTION
get_configuration

Retrieve analyzer engine configuration from the provided file.

create_engine

Load Presidio Analyzer from yaml configuration file.

Source code in presidio_analyzer/analyzer_engine_provider.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
class AnalyzerEngineProvider:
    """
    Utility function for loading Presidio Analyzer.

    Use this class to load presidio analyzer engine from a yaml file

    :param analyzer_engine_conf_file: the path to the analyzer configuration file
    :param nlp_engine_conf_file: the path to the nlp engine configuration file
    :param recognizer_registry_conf_file: the path to the recognizer
    registry configuration file
    """

    def __init__(
        self,
        analyzer_engine_conf_file: Optional[Union[Path, str]] = None,
        nlp_engine_conf_file: Optional[Union[Path, str]] = None,
        recognizer_registry_conf_file: Optional[Union[Path, str]] = None,
    ):
        if analyzer_engine_conf_file:
            ConfigurationValidator.validate_file_path(analyzer_engine_conf_file)
        if nlp_engine_conf_file:
            ConfigurationValidator.validate_file_path(nlp_engine_conf_file)
        if recognizer_registry_conf_file:
            ConfigurationValidator.validate_file_path(recognizer_registry_conf_file)

        self.configuration = self.get_configuration(conf_file=analyzer_engine_conf_file)
        self.nlp_engine_conf_file = nlp_engine_conf_file
        self.recognizer_registry_conf_file = recognizer_registry_conf_file

    def get_configuration(
        self, conf_file: Optional[Union[Path, str]]
    ) -> Union[Dict[str, Any]]:
        """Retrieve analyzer engine configuration from the provided file."""

        if not conf_file:
            default_conf_file = self._get_full_conf_path()
            with open(default_conf_file) as file:
                configuration = yaml.safe_load(file)
            logger.info(
                f"Analyzer Engine configuration file "
                f"not provided. Using {default_conf_file}."
            )
        else:
            try:
                logger.info(f"Reading analyzer configuration from {conf_file}")
                with open(conf_file) as file:
                    configuration = yaml.safe_load(file)
            except OSError:
                logger.warning(
                    f"configuration file {conf_file} not found.  "
                    f"Using default config."
                )
                with open(self._get_full_conf_path()) as file:
                    configuration = yaml.safe_load(file)
            except Exception:
                logger.warning(
                    f"Failed to parse file {conf_file}, resorting to default"
                )
                with open(self._get_full_conf_path()) as file:
                    configuration = yaml.safe_load(file)

        ConfigurationValidator.validate_analyzer_configuration(configuration)
        logger.debug("Analyzer configuration validation passed")

        return configuration

    def create_engine(self) -> AnalyzerEngine:
        """
        Load Presidio Analyzer from yaml configuration file.

        :return: analyzer engine initialized with yaml configuration
        """

        nlp_engine = self._load_nlp_engine()
        supported_languages = self.configuration.get("supported_languages", ["en"])
        default_score_threshold = self.configuration.get("default_score_threshold", 0)

        registry = self._load_recognizer_registry(
            supported_languages=supported_languages, nlp_engine=nlp_engine
        )

        analyzer = AnalyzerEngine(
            nlp_engine=nlp_engine,
            registry=registry,
            supported_languages=supported_languages,
            default_score_threshold=default_score_threshold,
        )

        return analyzer

    def _load_recognizer_registry(
        self,
        supported_languages: List[str],
        nlp_engine: NlpEngine,
    ) -> RecognizerRegistry:
        """Load recognizer registry.

        Inline ``recognizer_registry`` section in the analyzer conf takes
        priority over a separately provided per-section file so that a unified
        ANALYZER_CONF_FILE is self-contained and is not silently overridden by
        a per-section file that was baked into the image as a Dockerfile default.
        A per-section file is only used when no inline section is present.
        """
        if "recognizer_registry" in self.configuration:
            registry_configuration = self.configuration["recognizer_registry"]
            provider = RecognizerRegistryProvider(
                registry_configuration={
                    **registry_configuration,
                    "supported_languages": supported_languages,
                },
                nlp_engine=nlp_engine,
            )
        elif self.recognizer_registry_conf_file:
            logger.info(
                f"Reading recognizer registry "
                f"configuration from {self.recognizer_registry_conf_file}"
            )
            provider = RecognizerRegistryProvider(
                conf_file=self.recognizer_registry_conf_file, nlp_engine=nlp_engine
            )
        else:
            logger.warning(
                "configuration file is missing for 'recognizer_registry'. "
                "Using default configuration for recognizer registry"
            )
            registry_configuration = self.configuration.get("recognizer_registry", {})
            provider = RecognizerRegistryProvider(
                registry_configuration={
                    **registry_configuration,
                    "supported_languages": supported_languages,
                },
                nlp_engine=nlp_engine,
            )
        registry = provider.create_recognizer_registry()

        return registry

    def _load_nlp_engine(self) -> NlpEngine:
        """Load NLP engine.

        Inline ``nlp_configuration`` section in the analyzer conf takes
        priority over a separately provided per-section file so that a unified
        ANALYZER_CONF_FILE is self-contained and is not silently overridden by
        a per-section file that was baked into the image as a Dockerfile default.
        A per-section file is only used when no inline section is present.
        """
        if "nlp_configuration" in self.configuration:
            nlp_configuration = self.configuration["nlp_configuration"]
            provider = NlpEngineProvider(nlp_configuration=nlp_configuration)
        elif self.nlp_engine_conf_file:
            logger.info(f"Reading nlp configuration from {self.nlp_engine_conf_file}")
            provider = NlpEngineProvider(conf_file=self.nlp_engine_conf_file)
        else:
            logger.warning(
                "configuration file is missing for 'nlp_configuration'."
                "Using default configuration for nlp engine"
            )
            provider = NlpEngineProvider()

        return provider.create_engine()

    @staticmethod
    def _get_full_conf_path(
        default_conf_file: Union[Path, str] = "default_analyzer.yaml",
    ) -> Path:
        """Return a Path to the default conf file."""
        return Path(Path(__file__).parent, "conf", default_conf_file)

get_configuration

get_configuration(
    conf_file: Optional[Union[Path, str]],
) -> Union[Dict[str, Any]]

Retrieve analyzer engine configuration from the provided file.

Source code in presidio_analyzer/analyzer_engine_provider.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def get_configuration(
    self, conf_file: Optional[Union[Path, str]]
) -> Union[Dict[str, Any]]:
    """Retrieve analyzer engine configuration from the provided file."""

    if not conf_file:
        default_conf_file = self._get_full_conf_path()
        with open(default_conf_file) as file:
            configuration = yaml.safe_load(file)
        logger.info(
            f"Analyzer Engine configuration file "
            f"not provided. Using {default_conf_file}."
        )
    else:
        try:
            logger.info(f"Reading analyzer configuration from {conf_file}")
            with open(conf_file) as file:
                configuration = yaml.safe_load(file)
        except OSError:
            logger.warning(
                f"configuration file {conf_file} not found.  "
                f"Using default config."
            )
            with open(self._get_full_conf_path()) as file:
                configuration = yaml.safe_load(file)
        except Exception:
            logger.warning(
                f"Failed to parse file {conf_file}, resorting to default"
            )
            with open(self._get_full_conf_path()) as file:
                configuration = yaml.safe_load(file)

    ConfigurationValidator.validate_analyzer_configuration(configuration)
    logger.debug("Analyzer configuration validation passed")

    return configuration

create_engine

create_engine() -> AnalyzerEngine

Load Presidio Analyzer from yaml configuration file.

RETURNS DESCRIPTION
AnalyzerEngine

analyzer engine initialized with yaml configuration

Source code in presidio_analyzer/analyzer_engine_provider.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def create_engine(self) -> AnalyzerEngine:
    """
    Load Presidio Analyzer from yaml configuration file.

    :return: analyzer engine initialized with yaml configuration
    """

    nlp_engine = self._load_nlp_engine()
    supported_languages = self.configuration.get("supported_languages", ["en"])
    default_score_threshold = self.configuration.get("default_score_threshold", 0)

    registry = self._load_recognizer_registry(
        supported_languages=supported_languages, nlp_engine=nlp_engine
    )

    analyzer = AnalyzerEngine(
        nlp_engine=nlp_engine,
        registry=registry,
        supported_languages=supported_languages,
        default_score_threshold=default_score_threshold,
    )

    return analyzer

presidio_analyzer.analysis_explanation.AnalysisExplanation

Hold tracing information to explain why PII entities were identified as such.

PARAMETER DESCRIPTION
recognizer

name of recognizer that made the decision

TYPE: str

original_score

recognizer's confidence in result

TYPE: float

pattern_name

name of pattern (if decision was made by a PatternRecognizer)

TYPE: str DEFAULT: None

pattern

regex pattern that was applied (if PatternRecognizer)

TYPE: str DEFAULT: None

validation_result

result of a validation (e.g. checksum)

TYPE: bool DEFAULT: None

textual_explanation

Free text for describing a decision of a logic or model

TYPE: str DEFAULT: None

METHOD DESCRIPTION
set_improved_score

Update the score and calculate the difference from the original score.

set_supportive_context_word

Set the context word which helped increase the score.

append_textual_explanation_line

Append a new line to textual_explanation field.

to_dict

Serialize self to dictionary.

Source code in presidio_analyzer/analysis_explanation.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class AnalysisExplanation:
    """
    Hold tracing information to explain why PII entities were identified as such.

    :param recognizer: name of recognizer that made the decision
    :param original_score: recognizer's confidence in result
    :param pattern_name: name of pattern
            (if decision was made by a PatternRecognizer)
    :param pattern: regex pattern that was applied (if PatternRecognizer)
    :param validation_result: result of a validation (e.g. checksum)
    :param textual_explanation: Free text for describing
            a decision of a logic or model
    """

    def __init__(
        self,
        recognizer: str,
        original_score: float,
        pattern_name: str = None,
        pattern: str = None,
        validation_result: bool = None,
        textual_explanation: str = None,
        regex_flags: int = None,
    ):
        self.recognizer = recognizer
        self.pattern_name = pattern_name
        self.pattern = pattern
        self.original_score = original_score
        self.score = original_score
        self.textual_explanation = textual_explanation
        self.score_context_improvement = 0
        self.supportive_context_word = ""
        self.validation_result = validation_result
        self.regex_flags = regex_flags

    def __repr__(self):
        """Create string representation of the object."""
        return str(self.__dict__)

    def set_improved_score(self, score: float) -> None:
        """Update the score and calculate the difference from the original score."""
        self.score = score
        self.score_context_improvement = self.score - self.original_score

    def set_supportive_context_word(self, word: str) -> None:
        """Set the context word which helped increase the score."""
        self.supportive_context_word = word

    def append_textual_explanation_line(self, text: str) -> None:
        """Append a new line to textual_explanation field."""
        if self.textual_explanation is None:
            self.textual_explanation = text
        else:
            self.textual_explanation = f"{self.textual_explanation}\n{text}"

    def to_dict(self) -> Dict:
        """
        Serialize self to dictionary.

        :return: a dictionary
        """
        return self.__dict__

set_improved_score

set_improved_score(score: float) -> None

Update the score and calculate the difference from the original score.

Source code in presidio_analyzer/analysis_explanation.py
43
44
45
46
def set_improved_score(self, score: float) -> None:
    """Update the score and calculate the difference from the original score."""
    self.score = score
    self.score_context_improvement = self.score - self.original_score

set_supportive_context_word

set_supportive_context_word(word: str) -> None

Set the context word which helped increase the score.

Source code in presidio_analyzer/analysis_explanation.py
48
49
50
def set_supportive_context_word(self, word: str) -> None:
    """Set the context word which helped increase the score."""
    self.supportive_context_word = word

append_textual_explanation_line

append_textual_explanation_line(text: str) -> None

Append a new line to textual_explanation field.

Source code in presidio_analyzer/analysis_explanation.py
52
53
54
55
56
57
def append_textual_explanation_line(self, text: str) -> None:
    """Append a new line to textual_explanation field."""
    if self.textual_explanation is None:
        self.textual_explanation = text
    else:
        self.textual_explanation = f"{self.textual_explanation}\n{text}"

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/analysis_explanation.py
59
60
61
62
63
64
65
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return self.__dict__

presidio_analyzer.recognizer_result.RecognizerResult

Recognizer Result represents the findings of the detected entity.

Result of a recognizer analyzing the text.

PARAMETER DESCRIPTION
entity_type

the type of the entity

TYPE: str

start

the start location of the detected entity

TYPE: int

end

the end location of the detected entity

TYPE: int

score

the score of the detection

TYPE: float

analysis_explanation

contains the explanation of why this entity was identified

TYPE: AnalysisExplanation DEFAULT: None

recognition_metadata

a dictionary of metadata to be used in recognizer specific cases, for example specific recognized context words and recognizer name

TYPE: Dict DEFAULT: None

METHOD DESCRIPTION
append_analysis_explanation_text

Add text to the analysis explanation.

to_dict

Serialize self to dictionary.

from_json

Create RecognizerResult from json.

intersects

Check if self intersects with a different RecognizerResult.

contained_in

Check if self is contained in a different RecognizerResult.

contains

Check if one result is contained or equal to another result.

equal_indices

Check if the indices are equal between two results.

has_conflict

Check if two recognizer results are conflicted or not.

Source code in presidio_analyzer/recognizer_result.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
class RecognizerResult:
    """
    Recognizer Result represents the findings of the detected entity.

    Result of a recognizer analyzing the text.

    :param entity_type: the type of the entity
    :param start: the start location of the detected entity
    :param end: the end location of the detected entity
    :param score: the score of the detection
    :param analysis_explanation: contains the explanation of why this
                                 entity was identified
    :param recognition_metadata: a dictionary of metadata to be used in
    recognizer specific cases, for example specific recognized context words
    and recognizer name
    """

    # Keys for recognizer metadata
    RECOGNIZER_NAME_KEY = "recognizer_name"
    RECOGNIZER_IDENTIFIER_KEY = "recognizer_identifier"

    # Key of a flag inside recognition_metadata dictionary
    # which is set to true if the result enhanced by context
    IS_SCORE_ENHANCED_BY_CONTEXT_KEY = "is_score_enhanced_by_context"

    logger = logging.getLogger("presidio-analyzer")

    def __init__(
        self,
        entity_type: str,
        start: int,
        end: int,
        score: float,
        analysis_explanation: AnalysisExplanation = None,
        recognition_metadata: Dict = None,
    ):
        self.entity_type = entity_type
        self.start = start
        self.end = end
        self.score = score
        self.analysis_explanation = analysis_explanation

        if not recognition_metadata:
            self.logger.debug(
                "recognition_metadata should be passed, "
                "containing a recognizer_name value"
            )

        self.recognition_metadata = recognition_metadata

    def append_analysis_explanation_text(self, text: str) -> None:
        """Add text to the analysis explanation."""
        if self.analysis_explanation:
            self.analysis_explanation.append_textual_explanation_line(text)

    def to_dict(self) -> Dict:
        """
        Serialize self to dictionary.

        :return: a dictionary
        """
        return self.__dict__

    @classmethod
    def from_json(cls, data: Dict) -> "RecognizerResult":
        """
        Create RecognizerResult from json.

        :param data: e.g. {
            "start": 24,
            "end": 32,
            "score": 0.8,
            "entity_type": "NAME"
        }
        :return: RecognizerResult
        """
        score = data.get("score")
        entity_type = data.get("entity_type")
        start = data.get("start")
        end = data.get("end")
        return cls(entity_type, start, end, score)

    def __repr__(self) -> str:
        """Return a string representation of the instance."""
        return self.__str__()

    def intersects(self, other: "RecognizerResult") -> int:
        """
        Check if self intersects with a different RecognizerResult.

        :return: If intersecting, returns the number of
        intersecting characters.
        If not, returns 0
        """
        # if they do not overlap the intersection is 0
        if self.end < other.start or other.end < self.start:
            return 0

        # otherwise the intersection is min(end) - max(start)
        return min(self.end, other.end) - max(self.start, other.start)

    def contained_in(self, other: "RecognizerResult") -> bool:
        """
        Check if self is contained in a different RecognizerResult.

        :return: true if contained
        """
        return self.start >= other.start and self.end <= other.end

    def contains(self, other: "RecognizerResult") -> bool:
        """
        Check if one result is contained or equal to another result.

        :param other: another RecognizerResult
        :return: bool
        """
        return self.start <= other.start and self.end >= other.end

    def equal_indices(self, other: "RecognizerResult") -> bool:
        """
        Check if the indices are equal between two results.

        :param other: another RecognizerResult
        :return:
        """
        return self.start == other.start and self.end == other.end

    def __gt__(self, other: "RecognizerResult") -> bool:
        """
        Check if one result is greater by using the results indices in the text.

        :param other: another RecognizerResult
        :return: bool
        """
        if self.start == other.start:
            return self.end > other.end
        return self.start > other.start

    def __eq__(self, other: "RecognizerResult") -> bool:
        """
        Check two results are equal by using all class fields.

        :param other: another RecognizerResult
        :return: bool
        """
        equal_type = self.entity_type == other.entity_type
        equal_score = self.score == other.score
        return self.equal_indices(other) and equal_type and equal_score

    def __hash__(self):
        """
        Hash the result data by using all class fields.

        :return: int
        """
        return hash(
            f"{str(self.start)} {str(self.end)} {str(self.score)} {self.entity_type}"
        )

    def __str__(self) -> str:
        """Return a string representation of the instance."""
        return (
            f"type: {self.entity_type}, "
            f"start: {self.start}, "
            f"end: {self.end}, "
            f"score: {self.score}"
        )

    def has_conflict(self, other: "RecognizerResult") -> bool:
        """
        Check if two recognizer results are conflicted or not.

        I have a conflict if:
        1. My indices are the same as the other and my score is lower.
        2. If my indices are contained in another.

        :param other: RecognizerResult
        :return:
        """
        if self.equal_indices(other):
            return self.score <= other.score
        return other.contains(self)

append_analysis_explanation_text

append_analysis_explanation_text(text: str) -> None

Add text to the analysis explanation.

Source code in presidio_analyzer/recognizer_result.py
57
58
59
60
def append_analysis_explanation_text(self, text: str) -> None:
    """Add text to the analysis explanation."""
    if self.analysis_explanation:
        self.analysis_explanation.append_textual_explanation_line(text)

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/recognizer_result.py
62
63
64
65
66
67
68
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return self.__dict__

from_json classmethod

from_json(data: Dict) -> RecognizerResult

Create RecognizerResult from json.

PARAMETER DESCRIPTION
data

e.g. { "start": 24, "end": 32, "score": 0.8, "entity_type": "NAME" }

TYPE: Dict

RETURNS DESCRIPTION
RecognizerResult

RecognizerResult

Source code in presidio_analyzer/recognizer_result.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@classmethod
def from_json(cls, data: Dict) -> "RecognizerResult":
    """
    Create RecognizerResult from json.

    :param data: e.g. {
        "start": 24,
        "end": 32,
        "score": 0.8,
        "entity_type": "NAME"
    }
    :return: RecognizerResult
    """
    score = data.get("score")
    entity_type = data.get("entity_type")
    start = data.get("start")
    end = data.get("end")
    return cls(entity_type, start, end, score)

intersects

intersects(other: RecognizerResult) -> int

Check if self intersects with a different RecognizerResult.

RETURNS DESCRIPTION
int

If intersecting, returns the number of intersecting characters. If not, returns 0

Source code in presidio_analyzer/recognizer_result.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def intersects(self, other: "RecognizerResult") -> int:
    """
    Check if self intersects with a different RecognizerResult.

    :return: If intersecting, returns the number of
    intersecting characters.
    If not, returns 0
    """
    # if they do not overlap the intersection is 0
    if self.end < other.start or other.end < self.start:
        return 0

    # otherwise the intersection is min(end) - max(start)
    return min(self.end, other.end) - max(self.start, other.start)

contained_in

contained_in(other: RecognizerResult) -> bool

Check if self is contained in a different RecognizerResult.

RETURNS DESCRIPTION
bool

true if contained

Source code in presidio_analyzer/recognizer_result.py
108
109
110
111
112
113
114
def contained_in(self, other: "RecognizerResult") -> bool:
    """
    Check if self is contained in a different RecognizerResult.

    :return: true if contained
    """
    return self.start >= other.start and self.end <= other.end

contains

contains(other: RecognizerResult) -> bool

Check if one result is contained or equal to another result.

PARAMETER DESCRIPTION
other

another RecognizerResult

TYPE: RecognizerResult

RETURNS DESCRIPTION
bool

bool

Source code in presidio_analyzer/recognizer_result.py
116
117
118
119
120
121
122
123
def contains(self, other: "RecognizerResult") -> bool:
    """
    Check if one result is contained or equal to another result.

    :param other: another RecognizerResult
    :return: bool
    """
    return self.start <= other.start and self.end >= other.end

equal_indices

equal_indices(other: RecognizerResult) -> bool

Check if the indices are equal between two results.

PARAMETER DESCRIPTION
other

another RecognizerResult

TYPE: RecognizerResult

RETURNS DESCRIPTION
bool
Source code in presidio_analyzer/recognizer_result.py
125
126
127
128
129
130
131
132
def equal_indices(self, other: "RecognizerResult") -> bool:
    """
    Check if the indices are equal between two results.

    :param other: another RecognizerResult
    :return:
    """
    return self.start == other.start and self.end == other.end

has_conflict

has_conflict(other: RecognizerResult) -> bool

Check if two recognizer results are conflicted or not.

I have a conflict if: 1. My indices are the same as the other and my score is lower. 2. If my indices are contained in another.

PARAMETER DESCRIPTION
other

RecognizerResult

TYPE: RecognizerResult

RETURNS DESCRIPTION
bool
Source code in presidio_analyzer/recognizer_result.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def has_conflict(self, other: "RecognizerResult") -> bool:
    """
    Check if two recognizer results are conflicted or not.

    I have a conflict if:
    1. My indices are the same as the other and my score is lower.
    2. If my indices are contained in another.

    :param other: RecognizerResult
    :return:
    """
    if self.equal_indices(other):
        return self.score <= other.score
    return other.contains(self)

Batch modules

presidio_analyzer.batch_analyzer_engine.BatchAnalyzerEngine

Batch analysis of documents (tables, lists, dicts).

Wrapper class to run Presidio Analyzer Engine on multiple values, either lists/iterators of strings, or dictionaries.

PARAMETER DESCRIPTION
analyzer_engine

AnalyzerEngine instance to use for handling the values in those collections.

TYPE: Optional[AnalyzerEngine] DEFAULT: None

METHOD DESCRIPTION
analyze_iterator

Analyze an iterable of strings.

analyze_dict

Analyze a dictionary of keys (strings) and values/iterable of values.

Source code in presidio_analyzer/batch_analyzer_engine.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
class BatchAnalyzerEngine:
    """
    Batch analysis of documents (tables, lists, dicts).

    Wrapper class to run Presidio Analyzer Engine on multiple values,
    either lists/iterators of strings, or dictionaries.

    :param analyzer_engine: AnalyzerEngine instance to use
    for handling the values in those collections.
    """

    def __init__(self, analyzer_engine: Optional[AnalyzerEngine] = None):
        self.analyzer_engine = analyzer_engine
        if not analyzer_engine:
            self.analyzer_engine = AnalyzerEngine()

    def analyze_iterator(
        self,
        texts: Iterable[Union[str, bool, float, int]],
        language: str,
        batch_size: int = 1,
        n_process: int = 1,
        **kwargs,
    ) -> List[List[RecognizerResult]]:
        """
        Analyze an iterable of strings.

        :param texts: An list containing strings to be analyzed.
        :param language: Input language
        :param batch_size: Batch size to process in a single iteration
        :param n_process: Number of processors to use. Defaults to `1`
        :param kwargs: Additional parameters for the `AnalyzerEngine.analyze` method.
        (default value depends on the nlp engine implementation)
        """

        # validate types
        texts = self._validate_types(texts)

        # Process the texts as batch for improved performance
        nlp_artifacts_batch: Iterator[Tuple[str, NlpArtifacts]] = (
            self.analyzer_engine.nlp_engine.process_batch(
                texts=texts,
                language=language,
                batch_size=batch_size,
                n_process=n_process,
            )
        )

        list_results = []
        for text, nlp_artifacts in nlp_artifacts_batch:
            results = self.analyzer_engine.analyze(
                text=str(text), nlp_artifacts=nlp_artifacts, language=language, **kwargs
            )

            list_results.append(results)

        return list_results

    def analyze_dict(
        self,
        input_dict: Dict[str, Union[Any, Iterable[Any]]],
        language: str,
        keys_to_skip: Optional[List[str]] = None,
        batch_size: int = 1,
        n_process: int = 1,
        **kwargs,
    ) -> Iterator[DictAnalyzerResult]:
        """
        Analyze a dictionary of keys (strings) and values/iterable of values.

        Non-string values are returned as is.

        :param input_dict: The input dictionary for analysis
        :param language: Input language
        :param keys_to_skip: Keys to ignore during analysis
        :param batch_size: Batch size to process in a single iteration
        :param n_process: Number of processors to use. Defaults to `1`

        :param kwargs: Additional keyword arguments
        for the `AnalyzerEngine.analyze` method.
        Use this to pass arguments to the analyze method,
        such as `ad_hoc_recognizers`, `context`, `return_decision_process`.
        See `AnalyzerEngine.analyze` for the full list.
        """

        context = []
        if "context" in kwargs:
            context = kwargs["context"]
            del kwargs["context"]

        if not keys_to_skip:
            keys_to_skip = []

        for key, value in input_dict.items():
            if not value or key in keys_to_skip:
                yield DictAnalyzerResult(key=key, value=value, recognizer_results=[])
                continue  # skip this key as requested

            # Add the key as an additional context
            specific_context = context[:]
            specific_context.append(key)

            if type(value) in (str, int, bool, float):
                results: List[RecognizerResult] = self.analyzer_engine.analyze(
                    text=str(value), language=language, context=[key], **kwargs
                )
            elif isinstance(value, dict):
                new_keys_to_skip = self._get_nested_keys_to_skip(key, keys_to_skip)
                results = self.analyze_dict(
                    input_dict=value,
                    language=language,
                    context=specific_context,
                    keys_to_skip=new_keys_to_skip,
                    **kwargs,
                )
            elif isinstance(value, Iterable):
                # Recursively iterate nested dicts

                results: List[List[RecognizerResult]] = self.analyze_iterator(
                    texts=value,
                    language=language,
                    context=specific_context,
                    n_process=n_process,
                    batch_size=batch_size,
                    **kwargs,
                )
            else:
                raise ValueError(f"type {type(value)} is unsupported.")

            yield DictAnalyzerResult(key=key, value=value, recognizer_results=results)

    @staticmethod
    def _validate_types(value_iterator: Iterable[Any]) -> Iterator[Any]:
        for val in value_iterator:
            if val and type(val) not in (int, float, bool, str):
                err_msg = (
                    "Analyzer.analyze_iterator only works "
                    "on primitive types (int, float, bool, str). "
                    "Lists of objects are not yet supported."
                )
                logger.error(err_msg)
                raise ValueError(err_msg)
            yield val

    @staticmethod
    def _get_nested_keys_to_skip(key, keys_to_skip):
        new_keys_to_skip = [
            k.replace(f"{key}.", "") for k in keys_to_skip if k.startswith(key)
        ]
        return new_keys_to_skip

analyze_iterator

analyze_iterator(
    texts: Iterable[Union[str, bool, float, int]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    **kwargs
) -> List[List[RecognizerResult]]

Analyze an iterable of strings.

PARAMETER DESCRIPTION
texts

An list containing strings to be analyzed.

TYPE: Iterable[Union[str, bool, float, int]]

language

Input language

TYPE: str

batch_size

Batch size to process in a single iteration

TYPE: int DEFAULT: 1

n_process

Number of processors to use. Defaults to 1

TYPE: int DEFAULT: 1

kwargs

Additional parameters for the AnalyzerEngine.analyze method. (default value depends on the nlp engine implementation)

DEFAULT: {}

Source code in presidio_analyzer/batch_analyzer_engine.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def analyze_iterator(
    self,
    texts: Iterable[Union[str, bool, float, int]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    **kwargs,
) -> List[List[RecognizerResult]]:
    """
    Analyze an iterable of strings.

    :param texts: An list containing strings to be analyzed.
    :param language: Input language
    :param batch_size: Batch size to process in a single iteration
    :param n_process: Number of processors to use. Defaults to `1`
    :param kwargs: Additional parameters for the `AnalyzerEngine.analyze` method.
    (default value depends on the nlp engine implementation)
    """

    # validate types
    texts = self._validate_types(texts)

    # Process the texts as batch for improved performance
    nlp_artifacts_batch: Iterator[Tuple[str, NlpArtifacts]] = (
        self.analyzer_engine.nlp_engine.process_batch(
            texts=texts,
            language=language,
            batch_size=batch_size,
            n_process=n_process,
        )
    )

    list_results = []
    for text, nlp_artifacts in nlp_artifacts_batch:
        results = self.analyzer_engine.analyze(
            text=str(text), nlp_artifacts=nlp_artifacts, language=language, **kwargs
        )

        list_results.append(results)

    return list_results

analyze_dict

analyze_dict(
    input_dict: Dict[str, Union[Any, Iterable[Any]]],
    language: str,
    keys_to_skip: Optional[List[str]] = None,
    batch_size: int = 1,
    n_process: int = 1,
    **kwargs
) -> Iterator[DictAnalyzerResult]

Analyze a dictionary of keys (strings) and values/iterable of values.

Non-string values are returned as is.

PARAMETER DESCRIPTION
input_dict

The input dictionary for analysis

TYPE: Dict[str, Union[Any, Iterable[Any]]]

language

Input language

TYPE: str

keys_to_skip

Keys to ignore during analysis

TYPE: Optional[List[str]] DEFAULT: None

batch_size

Batch size to process in a single iteration

TYPE: int DEFAULT: 1

n_process

Number of processors to use. Defaults to 1

TYPE: int DEFAULT: 1

kwargs

Additional keyword arguments for the AnalyzerEngine.analyze method. Use this to pass arguments to the analyze method, such as ad_hoc_recognizers, context, return_decision_process. See AnalyzerEngine.analyze for the full list.

DEFAULT: {}

Source code in presidio_analyzer/batch_analyzer_engine.py
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def analyze_dict(
    self,
    input_dict: Dict[str, Union[Any, Iterable[Any]]],
    language: str,
    keys_to_skip: Optional[List[str]] = None,
    batch_size: int = 1,
    n_process: int = 1,
    **kwargs,
) -> Iterator[DictAnalyzerResult]:
    """
    Analyze a dictionary of keys (strings) and values/iterable of values.

    Non-string values are returned as is.

    :param input_dict: The input dictionary for analysis
    :param language: Input language
    :param keys_to_skip: Keys to ignore during analysis
    :param batch_size: Batch size to process in a single iteration
    :param n_process: Number of processors to use. Defaults to `1`

    :param kwargs: Additional keyword arguments
    for the `AnalyzerEngine.analyze` method.
    Use this to pass arguments to the analyze method,
    such as `ad_hoc_recognizers`, `context`, `return_decision_process`.
    See `AnalyzerEngine.analyze` for the full list.
    """

    context = []
    if "context" in kwargs:
        context = kwargs["context"]
        del kwargs["context"]

    if not keys_to_skip:
        keys_to_skip = []

    for key, value in input_dict.items():
        if not value or key in keys_to_skip:
            yield DictAnalyzerResult(key=key, value=value, recognizer_results=[])
            continue  # skip this key as requested

        # Add the key as an additional context
        specific_context = context[:]
        specific_context.append(key)

        if type(value) in (str, int, bool, float):
            results: List[RecognizerResult] = self.analyzer_engine.analyze(
                text=str(value), language=language, context=[key], **kwargs
            )
        elif isinstance(value, dict):
            new_keys_to_skip = self._get_nested_keys_to_skip(key, keys_to_skip)
            results = self.analyze_dict(
                input_dict=value,
                language=language,
                context=specific_context,
                keys_to_skip=new_keys_to_skip,
                **kwargs,
            )
        elif isinstance(value, Iterable):
            # Recursively iterate nested dicts

            results: List[List[RecognizerResult]] = self.analyze_iterator(
                texts=value,
                language=language,
                context=specific_context,
                n_process=n_process,
                batch_size=batch_size,
                **kwargs,
            )
        else:
            raise ValueError(f"type {type(value)} is unsupported.")

        yield DictAnalyzerResult(key=key, value=value, recognizer_results=results)

presidio_analyzer.dict_analyzer_result.DictAnalyzerResult dataclass

Data class for holding the output of the Presidio Analyzer on dictionaries.

PARAMETER DESCRIPTION
key

key in dictionary

TYPE: str

value

value to run analysis on (either string or list of strings)

TYPE: Union[str, List[str], dict]

recognizer_results

Analyzer output for one value. Could be either: - A list of recognizer results if the input is one string - A list of lists of recognizer results, if the input is a list of strings. - An iterator of a DictAnalyzerResult, if the input is a dictionary. In this case the recognizer_results would be the iterator of the DictAnalyzerResults next level in the dictionary.

TYPE: Union[List[RecognizerResult], List[List[RecognizerResult]], Iterator[DictAnalyzerResult]]

Source code in presidio_analyzer/dict_analyzer_result.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@dataclass
class DictAnalyzerResult:
    """
    Data class for holding the output of the Presidio Analyzer on dictionaries.

    :param key: key in dictionary
    :param value: value to run analysis on (either string or list of strings)
    :param recognizer_results: Analyzer output for one value.
    Could be either:
     - A list of recognizer results if the input is one string
     - A list of lists of recognizer results, if the input is a list of strings.
     - An iterator of a DictAnalyzerResult, if the input is a dictionary.
     In this case the recognizer_results would be the iterator
     of the DictAnalyzerResults next level in the dictionary.
    """

    key: str
    value: Union[str, List[str], dict]
    recognizer_results: Union[
        List[RecognizerResult],
        List[List[RecognizerResult]],
        Iterator["DictAnalyzerResult"],
    ]

Recognizers and patterns

presidio_analyzer.entity_recognizer.EntityRecognizer

A class representing an abstract PII entity recognizer.

EntityRecognizer is an abstract class to be inherited by Recognizers which hold the logic for recognizing specific PII entities.

EntityRecognizer exposes a method called enhance_using_context which can be overridden in case a custom context aware enhancement is needed in derived class of a recognizer.

PARAMETER DESCRIPTION
supported_entities

the entities supported by this recognizer (for example, phone number, address, etc.)

TYPE: List[str]

supported_language

the language supported by this recognizer. The supported language code is iso6391Name

TYPE: str DEFAULT: 'en'

name

the name of this recognizer (optional)

TYPE: str DEFAULT: None

version

the recognizer current version

TYPE: str DEFAULT: '0.0.1'

context

a list of words which can help boost confidence score when they appear in context of the matched entity

TYPE: Optional[List[str]] DEFAULT: None

country_code

Optional ISO 3166-1 alpha-2 country tag. Custom recognizers may set it per instance; predefined recognizers should prefer the class-level :attr:COUNTRY_CODE. Values are stripped, lower-cased, and must match :attr:COUNTRY_CODE when both are set.

TYPE: Optional[str] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

load

Initialize the recognizer assets if needed.

analyze

Analyze text to identify entities.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

Source code in presidio_analyzer/entity_recognizer.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
class EntityRecognizer:
    """
    A class representing an abstract PII entity recognizer.

    EntityRecognizer is an abstract class to be inherited by
    Recognizers which hold the logic for recognizing specific PII entities.

    EntityRecognizer exposes a method called enhance_using_context which
    can be overridden in case a custom context aware enhancement is needed
    in derived class of a recognizer.

    :param supported_entities: the entities supported by this recognizer
    (for example, phone number, address, etc.)
    :param supported_language: the language supported by this recognizer.
    The supported language code is iso6391Name
    :param name: the name of this recognizer (optional)
    :param version: the recognizer current version
    :param context: a list of words which can help boost confidence score
    when they appear in context of the matched entity
    :param country_code: Optional ISO 3166-1 alpha-2 country tag. Custom
        recognizers may set it per instance; predefined recognizers should
        prefer the class-level :attr:`COUNTRY_CODE`. Values are stripped,
        lower-cased, and must match :attr:`COUNTRY_CODE` when both are set.
    """

    MIN_SCORE = 0
    MAX_SCORE = 1.0
    #: Canonical class-level country tag. Subclasses override on the class
    #: itself (e.g. ``COUNTRY_CODE = "us"``) and predefined country
    #: recognizers always declare it this way. Custom recognizers without
    #: a subclass can pass ``country_code=`` to the constructor instead;
    #: read both via the :meth:`country_code` / :meth:`is_country_specific`
    #: instance methods.
    COUNTRY_CODE: ClassVar[Optional[str]] = None

    def __init__(
        self,
        supported_entities: List[str],
        name: str = None,
        supported_language: str = "en",
        version: str = "0.0.1",
        context: Optional[List[str]] = None,
        country_code: Optional[str] = None,
    ):
        self.supported_entities = supported_entities

        if name is None:
            self.name = self.__class__.__name__  # assign class name as name
        else:
            self.name = name

        self._id = f"{self.name}_{id(self)}"

        self.supported_language = supported_language
        self.version = version
        self.is_loaded = False
        self.context = context if context else []

        self._country_code = self._resolve_country_code(country_code)

        self.load()
        logger.info("Loaded recognizer: %s", self.name)
        self.is_loaded = True

    @classmethod
    def _resolve_country_code(cls, passed: Optional[str]) -> Optional[str]:
        """Reconcile a constructor-passed country code with the class attribute.

        Implements the two-path tagging matrix: the class-level
        :attr:`COUNTRY_CODE` is the canonical declaration for predefined
        recognizers, and the constructor kwarg is the path for custom
        recognizers (typically routed through ``from_dict`` from YAML).
        Both can be set as long as they agree; conflicting values raise
        ``ValueError`` so a Polish tax-ID recognizer can't be silently
        re-tagged as British, regardless of which path the misconfiguration
        comes from.

        :param passed: The value supplied to ``__init__`` (already typed
            as ``Optional[str]``).
        :return: The lower-cased, stripped country code stored on the
            instance, or ``None`` for locale-agnostic recognizers.
        :raises TypeError: If ``passed`` is set but is not a string.
        :raises ValueError: If ``passed`` is blank, or if it disagrees
            with a class-level :attr:`COUNTRY_CODE`.
        """
        class_code = cls.COUNTRY_CODE
        normalized_class = (
            class_code.lower() if isinstance(class_code, str) else class_code
        )

        if passed is None:
            return normalized_class

        if not isinstance(passed, str):
            raise TypeError(
                f"country_code must be a string or None, got "
                f"{type(passed).__name__}: {passed!r}."
            )
        trimmed = passed.strip()
        if not trimmed:
            raise ValueError(
                f"country_code must be a non-empty string; got {passed!r}."
            )
        normalized_passed = trimmed.lower()

        if normalized_class is not None and normalized_passed != normalized_class:
            raise ValueError(
                f"country_code={passed!r} conflicts with class-level "
                f"{cls.__name__}.COUNTRY_CODE={class_code!r}. The class "
                f"attribute is the canonical declaration; pass the matching "
                f"value or omit ``country_code=`` entirely."
            )

        return normalized_passed

    def country_code(self) -> Optional[str]:
        """Return the country tag for this recognizer, or ``None`` if generic.

        Resolved at construction time from the class-level
        :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
        constructor kwarg; the two are reconciled by
        :meth:`_resolve_country_code` so this method always returns a
        single, lower-cased value (or ``None``). Note this is an instance
        method — to introspect a class without instantiating, read
        ``cls.COUNTRY_CODE`` directly.
        """
        return self._country_code

    def is_country_specific(self) -> bool:
        """Return ``True`` iff this recognizer is tagged with a country.

        Equivalent to ``self.country_code() is not None``. Provided as a
        named predicate because filter / registry / discoverability code
        reads more naturally with an explicit "is this country-specific?"
        question than with a None-check.
        """
        return self.country_code() is not None

    @property
    def id(self):
        """Return a unique identifier of this recognizer."""

        return self._id

    @abstractmethod
    def load(self) -> None:
        """
        Initialize the recognizer assets if needed.

        (e.g. machine learning models)
        """

    @abstractmethod
    def analyze(
        self, text: str, entities: List[str], nlp_artifacts: "NlpArtifacts"
    ) -> List[RecognizerResult]:
        """
        Analyze text to identify entities.

        :param text: The text to be analyzed
        :param entities: The list of entities this recognizer is able to detect
        :param nlp_artifacts: A group of attributes which are the result of
        an NLP process over the input text.
        :return: List of results detected by this recognizer.
        """
        return None

    def enhance_using_context(
        self,
        text: str,
        raw_recognizer_results: List[RecognizerResult],
        other_raw_recognizer_results: List[RecognizerResult],
        nlp_artifacts: "NlpArtifacts",
        context: Optional[List[str]] = None,
    ) -> List[RecognizerResult]:
        """Enhance confidence score using context of the entity.

        Override this method in derived class in case a custom logic
        is needed, otherwise return value will be equal to
        raw_results.

        in case a result score is boosted, derived class need to update
        result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

        :param text: The actual text that was analyzed
        :param raw_recognizer_results: This recognizer's results, to be updated
        based on recognizer specific context.
        :param other_raw_recognizer_results: Other recognizer results matched in
        the given text to allow related entity context enhancement
        :param nlp_artifacts: The nlp artifacts contains elements
                              such as lemmatized tokens for better
                              accuracy of the context enhancement process
        :param context: list of context words
        """
        return raw_recognizer_results

    def get_supported_entities(self) -> List[str]:
        """
        Return the list of entities this recognizer can identify.

        :return: A list of the supported entities by this recognizer
        """
        return self.supported_entities

    def get_supported_language(self) -> str:
        """
        Return the language this recognizer can support.

        :return: A list of the supported language by this recognizer
        """
        return self.supported_language

    def get_version(self) -> str:
        """
        Return the version of this recognizer.

        :return: The current version of this recognizer
        """
        return self.version

    def to_dict(self) -> Dict:
        """
        Serialize self to dictionary.

        :return: a dictionary
        """
        return_dict = {
            "supported_entities": self.supported_entities,
            "supported_language": self.supported_language,
            "name": self.name,
            "version": self.version,
        }
        if self._country_code is not None:
            return_dict["country_code"] = self._country_code
        return return_dict

    @classmethod
    def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
        """
        Create EntityRecognizer from a dict input.

        :param entity_recognizer_dict: Dict containing keys and values for instantiation
        """
        return cls(**entity_recognizer_dict)

    @staticmethod
    def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
        """
        Remove duplicate results.

        Remove duplicates in case the two results
        have identical start and ends and types.
        :param results: List[RecognizerResult]
        :return: List[RecognizerResult]
        """
        results = list(set(results))
        results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
        filtered_results = []

        for result in results:
            if result.score == 0:
                continue

            to_keep = result not in filtered_results  # equals based comparison
            if to_keep:
                for filtered in filtered_results:
                    # If result is contained in one of the other results
                    if (
                        result.contained_in(filtered)
                        and result.entity_type == filtered.entity_type
                    ):
                        to_keep = False
                        break

            if to_keep:
                filtered_results.append(result)

        return filtered_results

    @staticmethod
    def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
        """
        Cleanse the input string of the replacement pairs specified as argument.

        :param text: input string
        :param replacement_pairs: pairs of what has to be replaced with which value
        :return: cleansed string
        """
        for search_string, replacement_string in replacement_pairs:
            text = text.replace(search_string, replacement_string)
        return text

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

load abstractmethod

load() -> None

Initialize the recognizer assets if needed.

(e.g. machine learning models)

Source code in presidio_analyzer/entity_recognizer.py
157
158
159
160
161
162
163
@abstractmethod
def load(self) -> None:
    """
    Initialize the recognizer assets if needed.

    (e.g. machine learning models)
    """

analyze abstractmethod

analyze(
    text: str, entities: List[str], nlp_artifacts: NlpArtifacts
) -> List[RecognizerResult]

Analyze text to identify entities.

PARAMETER DESCRIPTION
text

The text to be analyzed

TYPE: str

entities

The list of entities this recognizer is able to detect

TYPE: List[str]

nlp_artifacts

A group of attributes which are the result of an NLP process over the input text.

TYPE: NlpArtifacts

RETURNS DESCRIPTION
List[RecognizerResult]

List of results detected by this recognizer.

Source code in presidio_analyzer/entity_recognizer.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@abstractmethod
def analyze(
    self, text: str, entities: List[str], nlp_artifacts: "NlpArtifacts"
) -> List[RecognizerResult]:
    """
    Analyze text to identify entities.

    :param text: The text to be analyzed
    :param entities: The list of entities this recognizer is able to detect
    :param nlp_artifacts: A group of attributes which are the result of
    an NLP process over the input text.
    :return: List of results detected by this recognizer.
    """
    return None

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

presidio_analyzer.local_recognizer.LocalRecognizer

Bases: ABC, EntityRecognizer

PII entity recognizer which runs on the same process as the AnalyzerEngine.

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

load

Initialize the recognizer assets if needed.

analyze

Analyze text to identify entities.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

Source code in presidio_analyzer/local_recognizer.py
6
7
class LocalRecognizer(ABC, EntityRecognizer):
    """PII entity recognizer which runs on the same process as the AnalyzerEngine."""

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

load abstractmethod

load() -> None

Initialize the recognizer assets if needed.

(e.g. machine learning models)

Source code in presidio_analyzer/entity_recognizer.py
157
158
159
160
161
162
163
@abstractmethod
def load(self) -> None:
    """
    Initialize the recognizer assets if needed.

    (e.g. machine learning models)
    """

analyze abstractmethod

analyze(
    text: str, entities: List[str], nlp_artifacts: NlpArtifacts
) -> List[RecognizerResult]

Analyze text to identify entities.

PARAMETER DESCRIPTION
text

The text to be analyzed

TYPE: str

entities

The list of entities this recognizer is able to detect

TYPE: List[str]

nlp_artifacts

A group of attributes which are the result of an NLP process over the input text.

TYPE: NlpArtifacts

RETURNS DESCRIPTION
List[RecognizerResult]

List of results detected by this recognizer.

Source code in presidio_analyzer/entity_recognizer.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@abstractmethod
def analyze(
    self, text: str, entities: List[str], nlp_artifacts: "NlpArtifacts"
) -> List[RecognizerResult]:
    """
    Analyze text to identify entities.

    :param text: The text to be analyzed
    :param entities: The list of entities this recognizer is able to detect
    :param nlp_artifacts: A group of attributes which are the result of
    an NLP process over the input text.
    :return: List of results detected by this recognizer.
    """
    return None

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

presidio_analyzer.pattern.Pattern

A class that represents a regex pattern.

PARAMETER DESCRIPTION
name

the name of the pattern

TYPE: str

regex

the regex pattern to detect

TYPE: str

score

the pattern's strength (values varies 0-1)

TYPE: float

METHOD DESCRIPTION
to_dict

Turn this instance into a dictionary.

from_dict

Load an instance from a dictionary.

Source code in presidio_analyzer/pattern.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Pattern:
    """
    A class that represents a regex pattern.

    :param name: the name of the pattern
    :param regex: the regex pattern to detect
    :param score: the pattern's strength (values varies 0-1)
    """

    def __init__(self, name: str, regex: str, score: float):
        self.name = name
        self.regex = regex
        self.score = score
        self.compiled_regex = None
        self.compiled_with_flags = None

        self.__validate_regex(self.regex)
        self.__validate_score(self.score)

    @staticmethod
    def __validate_regex(pattern: str) -> None:
        """Validate that the regex pattern is valid."""
        try:
            re.compile(pattern)
        except re.error as e:
            raise ValueError(f"Invalid regex pattern: {e}")

    @staticmethod
    def __validate_score(score: float) -> None:
        if score < 0 or score > 1:
            raise ValueError(
                f"Invalid score: {score}. " "Score should be between 0 and 1"
            )

    def to_dict(self) -> Dict:
        """
        Turn this instance into a dictionary.

        :return: a dictionary
        """
        return_dict = {"name": self.name, "score": self.score, "regex": self.regex}
        return return_dict

    @classmethod
    def from_dict(cls, pattern_dict: Dict) -> "Pattern":
        """
        Load an instance from a dictionary.

        :param pattern_dict: a dictionary holding the pattern's parameters
        :return: a Pattern instance
        """
        return cls(**pattern_dict)

    def __repr__(self):
        """Return string representation of instance."""
        return json.dumps(self.to_dict())

    def __str__(self):
        """Return string representation of instance."""
        return json.dumps(self.to_dict())

to_dict

to_dict() -> Dict

Turn this instance into a dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/pattern.py
41
42
43
44
45
46
47
48
def to_dict(self) -> Dict:
    """
    Turn this instance into a dictionary.

    :return: a dictionary
    """
    return_dict = {"name": self.name, "score": self.score, "regex": self.regex}
    return return_dict

from_dict classmethod

from_dict(pattern_dict: Dict) -> Pattern

Load an instance from a dictionary.

PARAMETER DESCRIPTION
pattern_dict

a dictionary holding the pattern's parameters

TYPE: Dict

RETURNS DESCRIPTION
Pattern

a Pattern instance

Source code in presidio_analyzer/pattern.py
50
51
52
53
54
55
56
57
58
@classmethod
def from_dict(cls, pattern_dict: Dict) -> "Pattern":
    """
    Load an instance from a dictionary.

    :param pattern_dict: a dictionary holding the pattern's parameters
    :return: a Pattern instance
    """
    return cls(**pattern_dict)

presidio_analyzer.pattern_recognizer.PatternRecognizer

Bases: LocalRecognizer

PII entity recognizer using regular expressions or deny-lists.

:attr:EntityRecognizer.COUNTRY_CODE (the canonical path for predefined recognizers) or per-instance via the country_code constructor kwarg; see :class:EntityRecognizer for the full reconciliation rules.

PARAMETER DESCRIPTION
patterns

A list of patterns to detect

TYPE: List[Pattern] DEFAULT: None

deny_list

A list of words to detect, in case our recognizer uses a predefined list of words (deny list)

TYPE: List[str] DEFAULT: None

context

list of context words

TYPE: List[str] DEFAULT: None

deny_list_score

confidence score for a term identified using a deny-list

TYPE: float DEFAULT: 1.0

global_regex_flags

regex flags to be used in regex matching, including deny-lists.

TYPE: Optional[int] DEFAULT: DOTALL | MULTILINE | IGNORECASE

country_code

Optional ISO 3166-1 alpha-2 country tag, forwarded to :class:EntityRecognizer. Lets custom recognizers declare a country without subclassing — typically used by from_dict and the corresponding YAML type: custom entries. Subclasses with a class- level :attr:EntityRecognizer.COUNTRY_CODE should leave this unset or pass the matching value.

Country tagging may be declared at the class level via

TYPE: Optional[str] DEFAULT: None

METHOD DESCRIPTION
analyze

Analyzes text to detect PII using regular expressions or deny-lists.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

Source code in presidio_analyzer/pattern_recognizer.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
class PatternRecognizer(LocalRecognizer):
    """
    PII entity recognizer using regular expressions or deny-lists.

    :param patterns: A list of patterns to detect
    :param deny_list: A list of words to detect,
    in case our recognizer uses a predefined list of words (deny list)
    :param context: list of context words
    :param deny_list_score: confidence score for a term
    identified using a deny-list
    :param global_regex_flags: regex flags to be used in regex matching,
    including deny-lists.
    :param country_code: Optional ISO 3166-1 alpha-2 country tag, forwarded
    to :class:`EntityRecognizer`. Lets custom recognizers declare a country
    without subclassing — typically used by ``from_dict`` and the
    corresponding YAML ``type: custom`` entries. Subclasses with a class-
    level :attr:`EntityRecognizer.COUNTRY_CODE` should leave this unset
    or pass the matching value.

    Country tagging may be declared at the class level via
    :attr:`EntityRecognizer.COUNTRY_CODE` (the canonical path for
    predefined recognizers) or per-instance via the ``country_code``
    constructor kwarg; see :class:`EntityRecognizer` for the full
    reconciliation rules.
    """

    def __init__(
        self,
        supported_entity: str,
        name: str = None,
        supported_language: str = "en",
        patterns: List[Pattern] = None,
        deny_list: List[str] = None,
        context: List[str] = None,
        deny_list_score: float = 1.0,
        global_regex_flags: Optional[int] = re.DOTALL | re.MULTILINE | re.IGNORECASE,
        version: str = "0.0.1",
        country_code: Optional[str] = None,
    ):
        if not supported_entity:
            raise ValueError("Pattern recognizer should be initialized with entity")

        if not patterns and not deny_list:
            raise ValueError(
                "Pattern recognizer should be initialized with patterns"
                " or with deny list"
            )

        super().__init__(
            supported_entities=[supported_entity],
            supported_language=supported_language,
            name=name,
            version=version,
            country_code=country_code,
        )
        if patterns is None:
            self.patterns = []
        else:
            self.patterns = patterns
        self.context = context
        self.deny_list_score = deny_list_score
        self.global_regex_flags = global_regex_flags

        if deny_list:
            deny_list_pattern = self._deny_list_to_regex(deny_list)
            self.patterns.append(deny_list_pattern)
            self.deny_list = deny_list
        else:
            self.deny_list = []

    def load(self):  # noqa: D102
        pass

    def analyze(
        self,
        text: str,
        entities: List[str],
        nlp_artifacts: Optional["NlpArtifacts"] = None,
        regex_flags: Optional[int] = None,
    ) -> List[RecognizerResult]:
        """
        Analyzes text to detect PII using regular expressions or deny-lists.

        :param text: Text to be analyzed
        :param entities: Entities this recognizer can detect
        :param nlp_artifacts: Output values from the NLP engine
        :param regex_flags: regex flags to be used in regex matching
        :return:
        """
        results = []

        if self.patterns:
            pattern_result = self.__analyze_patterns(text, regex_flags)
            results.extend(pattern_result)

        return results

    def _deny_list_to_regex(self, deny_list: List[str]) -> Pattern:
        """
        Convert a list of words to a matching regex.

        To be analyzed by the analyze method as any other regex patterns.

        :param deny_list: the list of words to detect
        :return:the regex of the words for detection
        """

        # Escape deny list elements as preparation for regex
        escaped_deny_list = [re.escape(element) for element in deny_list]
        regex = r"(?:^|(?<=\W))(" + "|".join(escaped_deny_list) + r")(?:(?=\W)|$)"
        return Pattern(name="deny_list", regex=regex, score=self.deny_list_score)

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """
        return None

    def invalidate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Logic to check for result invalidation by running pruning logic.

        For example, each SSN number group should not consist of all the same digits.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the result is invalidated
        """
        return None

    @staticmethod
    def build_regex_explanation(
        recognizer_name: str,
        pattern_name: str,
        pattern: str,
        original_score: float,
        validation_result: bool,
        regex_flags: int,
    ) -> AnalysisExplanation:
        """
        Construct an explanation for why this entity was detected.

        :param recognizer_name: Name of recognizer detecting the entity
        :param pattern_name: Regex pattern name which detected the entity
        :param pattern: Regex pattern logic
        :param original_score: Score given by the recognizer
        :param validation_result: Whether validation was used and its result
        :param regex_flags: Regex flags used in the regex matching
        :return: Analysis explanation
        """
        textual_explanation = (
            f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
        )

        explanation = AnalysisExplanation(
            recognizer=recognizer_name,
            original_score=original_score,
            pattern_name=pattern_name,
            pattern=pattern,
            validation_result=validation_result,
            regex_flags=regex_flags,
            textual_explanation=textual_explanation,
        )
        return explanation

    def __analyze_patterns(
        self, text: str, flags: int = None
    ) -> List[RecognizerResult]:
        """
        Evaluate all patterns in the provided text.

        Including words in the provided deny-list

        :param text: text to analyze
        :param flags: regex flags
        :return: A list of RecognizerResult
        """
        flags = flags if flags else self.global_regex_flags
        results = []
        for pattern in self.patterns:
            match_start_time = datetime.datetime.now()

            # Compile regex if flags differ from flags the regex was compiled with
            if not pattern.compiled_regex or pattern.compiled_with_flags != flags:
                pattern.compiled_with_flags = flags
                pattern.compiled_regex = re.compile(pattern.regex, flags=flags)

            try:
                matches = pattern.compiled_regex.finditer(
                    text, timeout=REGEX_TIMEOUT_SECONDS
                )
                match_time = datetime.datetime.now() - match_start_time
                logger.debug(
                    "--- match_time[%s]: %.6f seconds",
                    pattern.name,
                    match_time.total_seconds(),
                )

                for match in matches:
                    start, end = match.span()
                    current_match = text[start:end]

                    # Skip empty results
                    if current_match == "":
                        continue

                    score = pattern.score

                    validation_result = self.validate_result(current_match)
                    description = self.build_regex_explanation(
                        self.name,
                        pattern.name,
                        pattern.regex,
                        score,
                        validation_result,
                        flags,
                    )
                    pattern_result = RecognizerResult(
                        entity_type=self.supported_entities[0],
                        start=start,
                        end=end,
                        score=score,
                        analysis_explanation=description,
                        recognition_metadata={
                            RecognizerResult.RECOGNIZER_NAME_KEY: self.name,
                            RecognizerResult.RECOGNIZER_IDENTIFIER_KEY: self.id,
                        },
                    )

                    if validation_result is not None:
                        if validation_result:
                            pattern_result.score = EntityRecognizer.MAX_SCORE
                        else:
                            pattern_result.score = EntityRecognizer.MIN_SCORE

                    invalidation_result = self.invalidate_result(current_match)
                    if invalidation_result is not None and invalidation_result:
                        pattern_result.score = EntityRecognizer.MIN_SCORE

                    if pattern_result.score > EntityRecognizer.MIN_SCORE:
                        results.append(pattern_result)

                    # Update analysis explanation score after validation or invalidation
                    description.score = pattern_result.score
            except TimeoutError:
                logger.warning(
                    "Regex pattern '%s' timed out after %s seconds, skipping.",
                    pattern.name,
                    REGEX_TIMEOUT_SECONDS,
                    exc_info=True,
                )

        results = EntityRecognizer.remove_duplicates(results)
        return results

    def to_dict(self) -> Dict:
        """Serialize instance into a dictionary."""
        return_dict = super().to_dict()

        return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
        return_dict["deny_list"] = self.deny_list
        return_dict["context"] = self.context
        return_dict["supported_entity"] = return_dict["supported_entities"][0]
        del return_dict["supported_entities"]

        return return_dict

    @classmethod
    def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
        """Create instance from a serialized dict."""
        # Make a copy to avoid mutating the input
        entity_recognizer_dict = entity_recognizer_dict.copy()

        patterns = entity_recognizer_dict.get("patterns")
        if patterns:
            patterns_list = [Pattern.from_dict(pat) for pat in patterns]
            entity_recognizer_dict["patterns"] = patterns_list

        # Transform supported_entities (plural) to supported_entity (singular)
        # PatternRecognizer only accepts supported_entity (singular)
        if (
            "supported_entity" in entity_recognizer_dict
            and "supported_entities" in entity_recognizer_dict
        ):
            raise ValueError(
                "Both 'supported_entity' and 'supported_entities' "
                "are present in the input dictionary. "
                "Only one should be provided."
            )
        if "supported_entities" in entity_recognizer_dict:
            supported_entities = entity_recognizer_dict.pop("supported_entities")
            if supported_entities and len(supported_entities) > 0:
                # Only set if not already present
                if "supported_entity" not in entity_recognizer_dict:
                    entity_recognizer_dict["supported_entity"] = supported_entities[0]

        return cls(**entity_recognizer_dict)

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

presidio_analyzer.remote_recognizer.RemoteRecognizer

Bases: ABC, EntityRecognizer

A configuration for a recognizer that runs on a different process / remote machine.

PARAMETER DESCRIPTION
supported_entities

A list of entities this recognizer can identify

TYPE: List[str]

name

name of recognizer

TYPE: Optional[str]

supported_language

The language this recognizer can detect entities in

TYPE: str

version

Version of this recognizer

TYPE: str

METHOD DESCRIPTION
analyze

Call an external service for PII detection.

country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

Source code in presidio_analyzer/remote_recognizer.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class RemoteRecognizer(ABC, EntityRecognizer):
    """
    A configuration for a recognizer that runs on a different process / remote machine.

    :param supported_entities: A list of entities this recognizer can identify
    :param name: name of recognizer
    :param supported_language: The language this recognizer can detect entities in
    :param version: Version of this recognizer
    """

    def __init__(
        self,
        supported_entities: List[str],
        name: Optional[str],
        supported_language: str,
        version: str,
        context: Optional[List[str]] = None,
    ):
        super().__init__(
            supported_entities=supported_entities,
            name=name,
            supported_language=supported_language,
            version=version,
            context=context,
        )

    def load(self):  # noqa: D102
        pass

    @abstractmethod
    def analyze(self, text: str, entities: List[str], nlp_artifacts: "NlpArtifacts"):
        """
        Call an external service for PII detection.

        :param text: text to be analyzed
        :param entities: Entities that should be looked for
        :param nlp_artifacts: Additional metadata from the NLP engine
        :return: List of identified PII entities
        """

        # 1. Call the external service.
        # 2. Translate results into List[RecognizerResult]
        pass

    @abstractmethod
    def get_supported_entities(self) -> List[str]:  # noqa: D102
        pass

analyze abstractmethod

analyze(text: str, entities: List[str], nlp_artifacts: NlpArtifacts)

Call an external service for PII detection.

PARAMETER DESCRIPTION
text

text to be analyzed

TYPE: str

entities

Entities that should be looked for

TYPE: List[str]

nlp_artifacts

Additional metadata from the NLP engine

TYPE: NlpArtifacts

RETURNS DESCRIPTION

List of identified PII entities

Source code in presidio_analyzer/remote_recognizer.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@abstractmethod
def analyze(self, text: str, entities: List[str], nlp_artifacts: "NlpArtifacts"):
    """
    Call an external service for PII detection.

    :param text: text to be analyzed
    :param entities: Entities that should be looked for
    :param nlp_artifacts: Additional metadata from the NLP engine
    :return: List of identified PII entities
    """

    # 1. Call the external service.
    # 2. Translate results into List[RecognizerResult]
    pass

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

Recognizer registry modules

presidio_analyzer.recognizer_registry.RecognizerRegistry

Detect, register and hold all recognizers to be used by the analyzer.

PARAMETER DESCRIPTION
recognizers

An optional list of recognizers, that will be available instead of the predefined recognizers

TYPE: Optional[Iterable[EntityRecognizer]] DEFAULT: None

global_regex_flags

regex flags to be used in regex matching, including deny-lists

TYPE: Optional[int] DEFAULT: DOTALL | MULTILINE | IGNORECASE

supported_languages

List of languages supported by this registry.

TYPE: Optional[List[str]] DEFAULT: None

METHOD DESCRIPTION
add_nlp_recognizer

Adding NLP recognizer in accordance with the nlp engine.

load_predefined_recognizers

Load the existing recognizers into memory.

get_nlp_recognizer

Return the recognizer leveraging the selected NLP Engine.

get_recognizers

Return a list of recognizers which supports the specified name and language.

get_country_codes

Return the set of country codes currently represented in the registry.

add_recognizer

Add a new recognizer to the list of recognizers.

remove_recognizer

Remove a recognizer based on its name.

add_pattern_recognizer_from_dict

Load a pattern recognizer from a Dict into the recognizer registry.

add_recognizers_from_yaml

Read YAML file and load recognizers into the recognizer registry.

get_supported_entities

Return the supported entities by the set of recognizers loaded.

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
class RecognizerRegistry:
    """
    Detect, register and hold all recognizers to be used by the analyzer.

    :param recognizers: An optional list of recognizers,
    that will be available instead of the predefined recognizers
    :param global_regex_flags: regex flags to be used in regex matching,
    including deny-lists
    :param supported_languages: List of languages supported by this registry.

    """

    def __init__(
        self,
        recognizers: Optional[Iterable[EntityRecognizer]] = None,
        global_regex_flags: Optional[int] = re.DOTALL | re.MULTILINE | re.IGNORECASE,
        supported_languages: Optional[List[str]] = None,
    ):
        if recognizers:
            self.recognizers = recognizers
        else:
            self.recognizers = []
        self.global_regex_flags = global_regex_flags
        self.supported_languages = (
            supported_languages if supported_languages else ["en"]
        )

    def _create_nlp_recognizer(
        self,
        nlp_engine: Optional[NlpEngine] = None,
        supported_language: Optional[str] = None,
    ) -> SpacyRecognizer:
        nlp_recognizer = self.get_nlp_recognizer(nlp_engine)

        if nlp_engine:
            return nlp_recognizer(
                supported_language=supported_language,
                supported_entities=nlp_engine.get_supported_entities(),
            )

        return nlp_recognizer(supported_language=supported_language)

    def add_nlp_recognizer(self, nlp_engine: NlpEngine) -> None:
        """
        Adding NLP recognizer in accordance with the nlp engine.

        :param nlp_engine: The NLP engine.
        :return: None
        """

        if not nlp_engine:
            supported_languages = self.supported_languages
        else:
            supported_languages = nlp_engine.get_supported_languages()

        self.recognizers.extend(
            [
                self._create_nlp_recognizer(
                    nlp_engine=nlp_engine, supported_language=supported_language
                )
                for supported_language in supported_languages
            ]
        )

    def load_predefined_recognizers(
        self,
        languages: Optional[List[str]] = None,
        nlp_engine: NlpEngine = None,
        countries: Optional[List[str]] = None,
    ) -> None:
        """
        Load the existing recognizers into memory.

        :param languages: List of languages for which to load recognizers
        :param nlp_engine: The NLP engine to use.
        :param countries: Optional list of country codes (case-insensitive,
            ISO 3166-1 alpha-2 — e.g. ``["us", "uk"]``).
            When provided, the loaded recognizers are limited per the
            ``country_code`` attribute on each recognizer:

            - recognizers with ``country_code is None`` (locale-agnostic
              built-ins, NER, NLP engine, third-party recognizers, and any
              custom recognizer that hasn't opted into the country tag) are
              **always loaded**;
            - recognizers with ``country_code`` set are loaded only when
              their code is in ``countries``.

            Passing an empty list (``countries=[]``) keeps only
            locale-agnostic recognizers. Passing ``None`` (the default)
            preserves the previous behavior of loading every predefined
            recognizer.
        :return: None
        """

        registry_configuration = {"global_regex_flags": self.global_regex_flags}
        if languages is not None:
            registry_configuration["supported_languages"] = languages
        if countries is not None:
            # Threaded through the configuration the same way as
            # ``supported_languages`` so the filter is applied inside
            # ``RecognizerListLoader.get(...)`` and behaves uniformly
            # whether driven from Python or from a YAML config file.
            registry_configuration["supported_countries"] = countries

        configuration = RecognizerConfigurationLoader.get(
            registry_configuration=registry_configuration
        )
        recognizers = RecognizerListLoader.get(**configuration)

        self.recognizers.extend(recognizers)
        self.add_nlp_recognizer(nlp_engine=nlp_engine)

    @staticmethod
    def get_nlp_recognizer(
        nlp_engine: NlpEngine,
    ) -> Type[SpacyRecognizer]:
        """Return the recognizer leveraging the selected NLP Engine."""

        if isinstance(nlp_engine, StanzaNlpEngine):
            return StanzaRecognizer
        if isinstance(nlp_engine, TransformersNlpEngine):
            return TransformersRecognizer
        if not nlp_engine or isinstance(nlp_engine, SpacyNlpEngine):
            return SpacyRecognizer
        else:
            logger.warning(
                "nlp engine should be either SpacyNlpEngine,"
                "StanzaNlpEngine or TransformersNlpEngine"
            )
            # Returning default
            return SpacyRecognizer

    def get_recognizers(
        self,
        language: str,
        entities: Optional[List[str]] = None,
        all_fields: bool = False,
        ad_hoc_recognizers: Optional[List[EntityRecognizer]] = None,
    ) -> List[EntityRecognizer]:
        """
        Return a list of recognizers which supports the specified name and language.

        :param entities: the requested entities
        :param language: the requested language
        :param all_fields: a flag to return all fields of a requested language.
        :param ad_hoc_recognizers: Additional recognizers provided by the user
        as part of the request
        :return: A list of the recognizers which supports the supplied entities
        and language
        """
        if language is None:
            raise ValueError("No language provided")

        if entities is None and all_fields is False:
            raise ValueError("No entities provided")

        all_possible_recognizers = copy.copy(self.recognizers)
        if ad_hoc_recognizers:
            all_possible_recognizers.extend(ad_hoc_recognizers)

        # filter out unwanted recognizers
        to_return = set()
        if all_fields:
            to_return = [
                rec
                for rec in all_possible_recognizers
                if language == rec.supported_language
            ]
        else:
            for entity in entities:
                subset = [
                    rec
                    for rec in all_possible_recognizers
                    if entity in rec.supported_entities
                    and language == rec.supported_language
                ]

                if not subset:
                    logger.warning(
                        "Entity %s doesn't have the corresponding"
                        " recognizer in language : %s",
                        entity,
                        language,
                    )
                else:
                    to_return.update(set(subset))

        logger.debug(
            "Returning a total of %s recognizers",
            str(len(to_return)),
        )

        if not to_return:
            raise ValueError("No matching recognizers were found to serve the request.")

        return list(to_return)

    def get_country_codes(self) -> List[str]:
        """Return the set of country codes currently represented in the registry.

        Aggregates the resolved country tag (via
        :meth:`EntityRecognizer.country_code`) across all loaded
        recognizers — including both class-level ``COUNTRY_CODE`` and
        per-instance ``country_code=`` constructor kwargs — and excludes
        generic / locale-agnostic ones. Useful for debugging country-
        filter behavior:

        >>> registry = RecognizerRegistry()
        >>> registry.load_predefined_recognizers()
        >>> sorted(registry.get_country_codes())  # doctest: +SKIP
        ['au', 'ca', 'de', 'es', 'fi', 'in', 'it', 'kr', 'ng', 'pl', 'se',
         'sg', 'th', 'tr', 'uk', 'us']

        :return: A sorted list of unique country codes (lowercased) seen on
            the loaded recognizers.
        """
        codes = set()
        for rec in self.recognizers:
            try:
                code = rec.country_code()
            except Exception:  # pragma: no cover — defensive
                code = None
            if isinstance(code, str) and code:
                codes.add(code.lower())
        return sorted(codes)

    def add_recognizer(self, recognizer: EntityRecognizer) -> None:
        """
        Add a new recognizer to the list of recognizers.

        :param recognizer: Recognizer to add
        """
        if not isinstance(recognizer, EntityRecognizer):
            raise ValueError("Input is not of type EntityRecognizer")

        self.recognizers.append(recognizer)

    def remove_recognizer(
        self, recognizer_name: str, language: Optional[str] = None
    ) -> None:
        """
        Remove a recognizer based on its name.

        :param recognizer_name: Name of recognizer to remove
        :param language: The supported language of the recognizer to be removed,
        in case multiple recognizers with the same name are present,
        and only one should be removed.
        """

        if not language:
            new_recognizers = [
                rec for rec in self.recognizers if rec.name != recognizer_name
            ]

            logger.info(
                "Removed %s recognizers which had the name %s",
                str(len(self.recognizers) - len(new_recognizers)),
                recognizer_name,
            )

        else:
            new_recognizers = [
                rec
                for rec in self.recognizers
                if rec.name != recognizer_name or rec.supported_language != language
            ]

            logger.info(
                "Removed %s recognizers which had the name %s and language %s",
                str(len(self.recognizers) - len(new_recognizers)),
                recognizer_name,
                language,
            )

        self.recognizers = new_recognizers

    def add_pattern_recognizer_from_dict(self, recognizer_dict: Dict) -> None:
        """
        Load a pattern recognizer from a Dict into the recognizer registry.

        :param recognizer_dict: Dict holding a serialization of an PatternRecognizer

        :example:
        >>> registry = RecognizerRegistry()
        >>> recognizer = { "name": "Titles Recognizer", "supported_language": "en","supported_entity": "TITLE", "deny_list": ["Mr.","Mrs."]}
        >>> registry.add_pattern_recognizer_from_dict(recognizer)
        """  # noqa: E501

        recognizer = PatternRecognizer.from_dict(recognizer_dict)
        self.add_recognizer(recognizer)

    def add_recognizers_from_yaml(self, yml_path: Union[str, Path]) -> None:
        r"""
        Read YAML file and load recognizers into the recognizer registry.

        See example yaml file here:
        https://github.com/data-privacy-stack/presidio/blob/main/presidio-analyzer/presidio_analyzer/conf/example_recognizers.yaml

        :example:
        >>> yaml_file = "recognizers.yaml"
        >>> registry = RecognizerRegistry()
        >>> registry.add_recognizers_from_yaml(yaml_file)

        """

        try:
            with open(yml_path) as stream:
                yaml_recognizers = yaml.safe_load(stream)

            for yaml_recognizer in yaml_recognizers["recognizers"]:
                self.add_pattern_recognizer_from_dict(yaml_recognizer)
        except OSError as io_error:
            print(f"Error reading file {yml_path}")
            raise io_error
        except yaml.YAMLError as yaml_error:
            print(f"Failed to parse file {yml_path}")
            raise yaml_error
        except TypeError as yaml_error:
            print(f"Failed to parse file {yml_path}")
            raise yaml_error

    def __instantiate_recognizer(
        self, recognizer_class: Type[EntityRecognizer], supported_language: str
    ):
        """
        Instantiate a recognizer class given type and input.

        :param recognizer_class: Class object of the recognizer
        :param supported_language: Language this recognizer should support
        """

        inst = recognizer_class(supported_language=supported_language)
        if isinstance(inst, PatternRecognizer):
            inst.global_regex_flags = self.global_regex_flags
        return inst

    def _get_supported_languages(self) -> List[str]:
        languages = []
        for rec in self.recognizers:
            languages.append(rec.supported_language)

        return list(set(languages))

    def get_supported_entities(
        self, languages: Optional[List[str]] = None
    ) -> List[str]:
        """
        Return the supported entities by the set of recognizers loaded.

        :param languages: The languages to get the supported entities for.
        If languages=None, returns all entities for all languages.
        """
        if not languages:
            languages = self._get_supported_languages()

        supported_entities = []
        for language in languages:
            recognizers = self.get_recognizers(language=language, all_fields=True)

            for recognizer in recognizers:
                supported_entities.extend(recognizer.get_supported_entities())

        return list(set(supported_entities))

add_nlp_recognizer

add_nlp_recognizer(nlp_engine: NlpEngine) -> None

Adding NLP recognizer in accordance with the nlp engine.

PARAMETER DESCRIPTION
nlp_engine

The NLP engine.

TYPE: NlpEngine

RETURNS DESCRIPTION
None

None

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def add_nlp_recognizer(self, nlp_engine: NlpEngine) -> None:
    """
    Adding NLP recognizer in accordance with the nlp engine.

    :param nlp_engine: The NLP engine.
    :return: None
    """

    if not nlp_engine:
        supported_languages = self.supported_languages
    else:
        supported_languages = nlp_engine.get_supported_languages()

    self.recognizers.extend(
        [
            self._create_nlp_recognizer(
                nlp_engine=nlp_engine, supported_language=supported_language
            )
            for supported_language in supported_languages
        ]
    )

load_predefined_recognizers

load_predefined_recognizers(
    languages: Optional[List[str]] = None,
    nlp_engine: NlpEngine = None,
    countries: Optional[List[str]] = None,
) -> None

Load the existing recognizers into memory.

PARAMETER DESCRIPTION
languages

List of languages for which to load recognizers

TYPE: Optional[List[str]] DEFAULT: None

nlp_engine

The NLP engine to use.

TYPE: NlpEngine DEFAULT: None

countries

Optional list of country codes (case-insensitive, ISO 3166-1 alpha-2 — e.g. ["us", "uk"]). When provided, the loaded recognizers are limited per the country_code attribute on each recognizer:

  • recognizers with country_code is None (locale-agnostic built-ins, NER, NLP engine, third-party recognizers, and any custom recognizer that hasn't opted into the country tag) are always loaded;
  • recognizers with country_code set are loaded only when their code is in countries.

Passing an empty list (countries=[]) keeps only locale-agnostic recognizers. Passing None (the default) preserves the previous behavior of loading every predefined recognizer.

TYPE: Optional[List[str]] DEFAULT: None

RETURNS DESCRIPTION
None

None

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def load_predefined_recognizers(
    self,
    languages: Optional[List[str]] = None,
    nlp_engine: NlpEngine = None,
    countries: Optional[List[str]] = None,
) -> None:
    """
    Load the existing recognizers into memory.

    :param languages: List of languages for which to load recognizers
    :param nlp_engine: The NLP engine to use.
    :param countries: Optional list of country codes (case-insensitive,
        ISO 3166-1 alpha-2 — e.g. ``["us", "uk"]``).
        When provided, the loaded recognizers are limited per the
        ``country_code`` attribute on each recognizer:

        - recognizers with ``country_code is None`` (locale-agnostic
          built-ins, NER, NLP engine, third-party recognizers, and any
          custom recognizer that hasn't opted into the country tag) are
          **always loaded**;
        - recognizers with ``country_code`` set are loaded only when
          their code is in ``countries``.

        Passing an empty list (``countries=[]``) keeps only
        locale-agnostic recognizers. Passing ``None`` (the default)
        preserves the previous behavior of loading every predefined
        recognizer.
    :return: None
    """

    registry_configuration = {"global_regex_flags": self.global_regex_flags}
    if languages is not None:
        registry_configuration["supported_languages"] = languages
    if countries is not None:
        # Threaded through the configuration the same way as
        # ``supported_languages`` so the filter is applied inside
        # ``RecognizerListLoader.get(...)`` and behaves uniformly
        # whether driven from Python or from a YAML config file.
        registry_configuration["supported_countries"] = countries

    configuration = RecognizerConfigurationLoader.get(
        registry_configuration=registry_configuration
    )
    recognizers = RecognizerListLoader.get(**configuration)

    self.recognizers.extend(recognizers)
    self.add_nlp_recognizer(nlp_engine=nlp_engine)

get_nlp_recognizer staticmethod

get_nlp_recognizer(nlp_engine: NlpEngine) -> Type[SpacyRecognizer]

Return the recognizer leveraging the selected NLP Engine.

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@staticmethod
def get_nlp_recognizer(
    nlp_engine: NlpEngine,
) -> Type[SpacyRecognizer]:
    """Return the recognizer leveraging the selected NLP Engine."""

    if isinstance(nlp_engine, StanzaNlpEngine):
        return StanzaRecognizer
    if isinstance(nlp_engine, TransformersNlpEngine):
        return TransformersRecognizer
    if not nlp_engine or isinstance(nlp_engine, SpacyNlpEngine):
        return SpacyRecognizer
    else:
        logger.warning(
            "nlp engine should be either SpacyNlpEngine,"
            "StanzaNlpEngine or TransformersNlpEngine"
        )
        # Returning default
        return SpacyRecognizer

get_recognizers

get_recognizers(
    language: str,
    entities: Optional[List[str]] = None,
    all_fields: bool = False,
    ad_hoc_recognizers: Optional[List[EntityRecognizer]] = None,
) -> List[EntityRecognizer]

Return a list of recognizers which supports the specified name and language.

PARAMETER DESCRIPTION
entities

the requested entities

TYPE: Optional[List[str]] DEFAULT: None

language

the requested language

TYPE: str

all_fields

a flag to return all fields of a requested language.

TYPE: bool DEFAULT: False

ad_hoc_recognizers

Additional recognizers provided by the user as part of the request

TYPE: Optional[List[EntityRecognizer]] DEFAULT: None

RETURNS DESCRIPTION
List[EntityRecognizer]

A list of the recognizers which supports the supplied entities and language

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def get_recognizers(
    self,
    language: str,
    entities: Optional[List[str]] = None,
    all_fields: bool = False,
    ad_hoc_recognizers: Optional[List[EntityRecognizer]] = None,
) -> List[EntityRecognizer]:
    """
    Return a list of recognizers which supports the specified name and language.

    :param entities: the requested entities
    :param language: the requested language
    :param all_fields: a flag to return all fields of a requested language.
    :param ad_hoc_recognizers: Additional recognizers provided by the user
    as part of the request
    :return: A list of the recognizers which supports the supplied entities
    and language
    """
    if language is None:
        raise ValueError("No language provided")

    if entities is None and all_fields is False:
        raise ValueError("No entities provided")

    all_possible_recognizers = copy.copy(self.recognizers)
    if ad_hoc_recognizers:
        all_possible_recognizers.extend(ad_hoc_recognizers)

    # filter out unwanted recognizers
    to_return = set()
    if all_fields:
        to_return = [
            rec
            for rec in all_possible_recognizers
            if language == rec.supported_language
        ]
    else:
        for entity in entities:
            subset = [
                rec
                for rec in all_possible_recognizers
                if entity in rec.supported_entities
                and language == rec.supported_language
            ]

            if not subset:
                logger.warning(
                    "Entity %s doesn't have the corresponding"
                    " recognizer in language : %s",
                    entity,
                    language,
                )
            else:
                to_return.update(set(subset))

    logger.debug(
        "Returning a total of %s recognizers",
        str(len(to_return)),
    )

    if not to_return:
        raise ValueError("No matching recognizers were found to serve the request.")

    return list(to_return)

get_country_codes

get_country_codes() -> List[str]

Return the set of country codes currently represented in the registry.

Aggregates the resolved country tag (via :meth:EntityRecognizer.country_code) across all loaded recognizers — including both class-level COUNTRY_CODE and per-instance country_code= constructor kwargs — and excludes generic / locale-agnostic ones. Useful for debugging country- filter behavior:

registry = RecognizerRegistry() registry.load_predefined_recognizers() sorted(registry.get_country_codes()) # doctest: +SKIP ['au', 'ca', 'de', 'es', 'fi', 'in', 'it', 'kr', 'ng', 'pl', 'se', 'sg', 'th', 'tr', 'uk', 'us']

RETURNS DESCRIPTION
List[str]

A sorted list of unique country codes (lowercased) seen on the loaded recognizers.

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
def get_country_codes(self) -> List[str]:
    """Return the set of country codes currently represented in the registry.

    Aggregates the resolved country tag (via
    :meth:`EntityRecognizer.country_code`) across all loaded
    recognizers — including both class-level ``COUNTRY_CODE`` and
    per-instance ``country_code=`` constructor kwargs — and excludes
    generic / locale-agnostic ones. Useful for debugging country-
    filter behavior:

    >>> registry = RecognizerRegistry()
    >>> registry.load_predefined_recognizers()
    >>> sorted(registry.get_country_codes())  # doctest: +SKIP
    ['au', 'ca', 'de', 'es', 'fi', 'in', 'it', 'kr', 'ng', 'pl', 'se',
     'sg', 'th', 'tr', 'uk', 'us']

    :return: A sorted list of unique country codes (lowercased) seen on
        the loaded recognizers.
    """
    codes = set()
    for rec in self.recognizers:
        try:
            code = rec.country_code()
        except Exception:  # pragma: no cover — defensive
            code = None
        if isinstance(code, str) and code:
            codes.add(code.lower())
    return sorted(codes)

add_recognizer

add_recognizer(recognizer: EntityRecognizer) -> None

Add a new recognizer to the list of recognizers.

PARAMETER DESCRIPTION
recognizer

Recognizer to add

TYPE: EntityRecognizer

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
255
256
257
258
259
260
261
262
263
264
def add_recognizer(self, recognizer: EntityRecognizer) -> None:
    """
    Add a new recognizer to the list of recognizers.

    :param recognizer: Recognizer to add
    """
    if not isinstance(recognizer, EntityRecognizer):
        raise ValueError("Input is not of type EntityRecognizer")

    self.recognizers.append(recognizer)

remove_recognizer

remove_recognizer(recognizer_name: str, language: Optional[str] = None) -> None

Remove a recognizer based on its name.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer to remove

TYPE: str

language

The supported language of the recognizer to be removed, in case multiple recognizers with the same name are present, and only one should be removed.

TYPE: Optional[str] DEFAULT: None

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
def remove_recognizer(
    self, recognizer_name: str, language: Optional[str] = None
) -> None:
    """
    Remove a recognizer based on its name.

    :param recognizer_name: Name of recognizer to remove
    :param language: The supported language of the recognizer to be removed,
    in case multiple recognizers with the same name are present,
    and only one should be removed.
    """

    if not language:
        new_recognizers = [
            rec for rec in self.recognizers if rec.name != recognizer_name
        ]

        logger.info(
            "Removed %s recognizers which had the name %s",
            str(len(self.recognizers) - len(new_recognizers)),
            recognizer_name,
        )

    else:
        new_recognizers = [
            rec
            for rec in self.recognizers
            if rec.name != recognizer_name or rec.supported_language != language
        ]

        logger.info(
            "Removed %s recognizers which had the name %s and language %s",
            str(len(self.recognizers) - len(new_recognizers)),
            recognizer_name,
            language,
        )

    self.recognizers = new_recognizers

add_pattern_recognizer_from_dict

add_pattern_recognizer_from_dict(recognizer_dict: Dict) -> None

Load a pattern recognizer from a Dict into the recognizer registry.

:example:

registry = RecognizerRegistry() recognizer = { "name": "Titles Recognizer", "supported_language": "en","supported_entity": "TITLE", "deny_list": ["Mr.","Mrs."]} registry.add_pattern_recognizer_from_dict(recognizer)

PARAMETER DESCRIPTION
recognizer_dict

Dict holding a serialization of an PatternRecognizer

TYPE: Dict

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
305
306
307
308
309
310
311
312
313
314
315
316
317
318
def add_pattern_recognizer_from_dict(self, recognizer_dict: Dict) -> None:
    """
    Load a pattern recognizer from a Dict into the recognizer registry.

    :param recognizer_dict: Dict holding a serialization of an PatternRecognizer

    :example:
    >>> registry = RecognizerRegistry()
    >>> recognizer = { "name": "Titles Recognizer", "supported_language": "en","supported_entity": "TITLE", "deny_list": ["Mr.","Mrs."]}
    >>> registry.add_pattern_recognizer_from_dict(recognizer)
    """  # noqa: E501

    recognizer = PatternRecognizer.from_dict(recognizer_dict)
    self.add_recognizer(recognizer)

add_recognizers_from_yaml

add_recognizers_from_yaml(yml_path: Union[str, Path]) -> None

Read YAML file and load recognizers into the recognizer registry.

See example yaml file here: https://github.com/data-privacy-stack/presidio/blob/main/presidio-analyzer/presidio_analyzer/conf/example_recognizers.yaml

:example:

yaml_file = "recognizers.yaml" registry = RecognizerRegistry() registry.add_recognizers_from_yaml(yaml_file)

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
def add_recognizers_from_yaml(self, yml_path: Union[str, Path]) -> None:
    r"""
    Read YAML file and load recognizers into the recognizer registry.

    See example yaml file here:
    https://github.com/data-privacy-stack/presidio/blob/main/presidio-analyzer/presidio_analyzer/conf/example_recognizers.yaml

    :example:
    >>> yaml_file = "recognizers.yaml"
    >>> registry = RecognizerRegistry()
    >>> registry.add_recognizers_from_yaml(yaml_file)

    """

    try:
        with open(yml_path) as stream:
            yaml_recognizers = yaml.safe_load(stream)

        for yaml_recognizer in yaml_recognizers["recognizers"]:
            self.add_pattern_recognizer_from_dict(yaml_recognizer)
    except OSError as io_error:
        print(f"Error reading file {yml_path}")
        raise io_error
    except yaml.YAMLError as yaml_error:
        print(f"Failed to parse file {yml_path}")
        raise yaml_error
    except TypeError as yaml_error:
        print(f"Failed to parse file {yml_path}")
        raise yaml_error

get_supported_entities

get_supported_entities(languages: Optional[List[str]] = None) -> List[str]

Return the supported entities by the set of recognizers loaded.

PARAMETER DESCRIPTION
languages

The languages to get the supported entities for. If languages=None, returns all entities for all languages.

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/recognizer_registry/recognizer_registry.py
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
def get_supported_entities(
    self, languages: Optional[List[str]] = None
) -> List[str]:
    """
    Return the supported entities by the set of recognizers loaded.

    :param languages: The languages to get the supported entities for.
    If languages=None, returns all entities for all languages.
    """
    if not languages:
        languages = self._get_supported_languages()

    supported_entities = []
    for language in languages:
        recognizers = self.get_recognizers(language=language, all_fields=True)

        for recognizer in recognizers:
            supported_entities.extend(recognizer.get_supported_entities())

    return list(set(supported_entities))

presidio_analyzer.recognizer_registry.RecognizerRegistryProvider

Utility class for loading Recognizer Registry.

Use this class to load recognizer registry from a yaml file

:example: { "supported_languages": ["de", "es"], "recognizers": [ { "name": "Zip code Recognizer", "supported_language": "en", "patterns": [ { "name": "zip code (weak)", "regex": "(\b\d{5}(?:\-\d{4})?\b)", "score": 0.01, } ], "context": ["zip", "code"], "supported_entity": "ZIP", } ] }

PARAMETER DESCRIPTION
conf_file

Path to yaml file containing registry configuration

TYPE: Optional[Union[Path, str]] DEFAULT: None

registry_configuration

Dict containing registry configuration

TYPE: Optional[Dict] DEFAULT: None

METHOD DESCRIPTION
create_recognizer_registry

Create a recognizer registry according to configuration loaded previously.

Source code in presidio_analyzer/recognizer_registry/recognizer_registry_provider.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
class RecognizerRegistryProvider:
    r"""
    Utility class for loading Recognizer Registry.

    Use this class to load recognizer registry from a yaml file

    :param conf_file: Path to yaml file containing registry configuration
    :param registry_configuration: Dict containing registry configuration
    :example:
        {
            "supported_languages": ["de", "es"],
            "recognizers": [
                {
                    "name": "Zip code Recognizer",
                    "supported_language": "en",
                    "patterns": [
                        {
                            "name": "zip code (weak)",
                            "regex": "(\\b\\d{5}(?:\\-\\d{4})?\\b)",
                            "score": 0.01,
                        }
                    ],
                    "context": ["zip", "code"],
                    "supported_entity": "ZIP",
                }
            ]
        }
    """

    def __init__(
        self,
        conf_file: Optional[Union[Path, str]] = None,
        registry_configuration: Optional[Dict] = None,
        nlp_engine: Optional[NlpEngine] = None,
    ):
        self.configuration = RecognizerConfigurationLoader.get(
            conf_file=conf_file, registry_configuration=registry_configuration
        )

        self.configuration = (
            ConfigurationValidator.validate_recognizer_registry_configuration(
                self.configuration
            )
        )
        self.nlp_engine = nlp_engine

    def create_recognizer_registry(self) -> RecognizerRegistry:
        """Create a recognizer registry according to configuration loaded previously."""
        supported_languages = self.configuration.get("supported_languages")
        global_regex_flags = self.configuration.get("global_regex_flags")
        recognizers_conf = self.configuration.get("recognizers")
        recognizers = RecognizerListLoader.get(
            recognizers_conf,
            supported_languages,
            global_regex_flags,
        )

        recognizers = list(recognizers)

        self.__update_based_on_nlp_recognizer_conf(
            recognizers, recognizers_conf, supported_languages
        )

        registry = RecognizerRegistry(
            recognizers=recognizers,
            supported_languages=supported_languages,
            global_regex_flags=global_regex_flags,
        )

        return registry

    def __update_based_on_nlp_recognizer_conf(
        self,
        recognizers: List[EntityRecognizer],
        recognizers_conf: Optional[Dict],
        supported_languages: List[str],
    ) -> None:
        """Update the list of recognizers based on the NLP recognizer configuration.

        The method adds the NLP recognizer to the list of recognizers
        if it is not already present,
        or removes it if it is not enabled in the configuration.
        Furthermore, it checks if there are
        any inconsistencies in configuration. For example:
        - Multiple enabled NLP recognizers in the configuration for one language.
        - The NLP recognizer in the configuration does not match the Nlp Engine.

        :param recognizers: List of recognizers to update.
        :param recognizers_conf: Configuration of the recognizers from the YAML file
        :param supported_languages: List of supported languages.

        :raises ValueError: If there are multiple enabled NLP recognizers
        in the configuration.
        :raises ValueError: If the NLP recognizer
        in the configuration does not match the Nlp Engine.
        """
        nlp_engine = self.nlp_engine

        if not nlp_engine:
            return

        for language in nlp_engine.get_supported_languages():
            self.__update_based_on_nlp_recognizer_conf_and_lang(
                recognizers=recognizers, nlp_engine=nlp_engine, language=language
            )
            self.__remove_disabled_nlp_recognizers(
                recognizers=recognizers,
                recognizers_conf=recognizers_conf,
                language=language,
            )

    @staticmethod
    def __update_based_on_nlp_recognizer_conf_and_lang(
        recognizers: List[EntityRecognizer],
        nlp_engine: NlpEngine,
        language: str,
    ):
        """
        Update the list of recognizers with nlp recognizers.

        Update based on the NLP recognizer configuration for a specific language.
        """

        nlp_recognizers = [
            rec
            for rec in recognizers
            if isinstance(rec, SpacyRecognizer) and rec.supported_language == language
        ]

        # Case 1: NLP recognizer is not in the list of recognizers
        if not nlp_recognizers:
            warning_text = (
                f"NLP recognizer (e.g. SpacyRecognizer, StanzaRecognizer) "
                f"is not in the list of recognizers "
                f"for language {language}. "
                f"Adding the default recognizer to the list."
                f"If you wish to remove the NLP recognizer, "
                f"define it as `enabled=false`."
            )
            logger.warning(warning_text)
            warnings.warn(warning_text)
            if nlp_engine:
                nlp_recognizer_cls = RecognizerRegistry.get_nlp_recognizer(
                    nlp_engine=nlp_engine
                )
                recognizers.append(
                    nlp_recognizer_cls(
                        supported_language=language,
                        supported_entities=nlp_engine.get_supported_entities(),
                    )
                )
            else:
                recognizers.append(SpacyRecognizer(supported_language=language))
            return

        # Case 2: There are multiple NLP recognizers for this language, throw error
        if len(nlp_recognizers) > 1:
            raise ValueError(
                f"Multiple NLP recognizers for language {language} "
                f"found in the configuration. "
                f"Please remove the duplicates."
            )

        # Case 3: There is a mismatch between the NLP Engine and the NLP Recognizer
        nlp_recognizer = nlp_recognizers[0]
        expected_nlp_recognizer_cls = RecognizerRegistry.get_nlp_recognizer(nlp_engine)
        if nlp_recognizer.__class__ != expected_nlp_recognizer_cls:
            raise ValueError(
                f"There is a mismatch between the NLP Engine defined "
                f"({nlp_engine.__class__.__name__}),"
                f"and the configured NLP recognizer "
                f"({nlp_recognizer.__class__.__name__})."
                f"Make sure the NLP recognizer is aligned with the "
                f"NLP engine and that all others are removed/disabled."
            )

    @staticmethod
    def __remove_disabled_nlp_recognizers(
        recognizers: List[EntityRecognizer],
        recognizers_conf: Dict[str, Any],
        language: str,
    ):
        """
        Remove recognizers that are disabled in the configuration.

        Goes through the recognizer conf provided by the user,
        and checks if a recognizer for a given language is disabled.
        If yes, it removes it from the recognizers list
        (as some are not removed in the previous step).
        """

        disabled = [
            rec_conf
            for rec_conf in recognizers_conf
            if not RecognizerListLoader.is_recognizer_enabled(rec_conf)
        ]

        disabled_rec_names = [
            RecognizerListLoader.get_recognizer_name(rec) for rec in disabled
        ]

        if not disabled:
            return

        disabled_rec_classes = [
            cls
            for cls in RecognizerListLoader.get_all_existing_recognizers()
            if cls.__name__ in disabled_rec_names
        ]

        lang_recognizers = [
            rec for rec in recognizers if rec.supported_language == language
        ]

        for recognizer in lang_recognizers:
            if type(recognizer) in disabled_rec_classes:
                recognizers.remove(recognizer)
                logger.info(
                    f"Disabled {recognizer.__class__.__name__} "
                    f"recognizer for language {language}."
                )

create_recognizer_registry

create_recognizer_registry() -> RecognizerRegistry

Create a recognizer registry according to configuration loaded previously.

Source code in presidio_analyzer/recognizer_registry/recognizer_registry_provider.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def create_recognizer_registry(self) -> RecognizerRegistry:
    """Create a recognizer registry according to configuration loaded previously."""
    supported_languages = self.configuration.get("supported_languages")
    global_regex_flags = self.configuration.get("global_regex_flags")
    recognizers_conf = self.configuration.get("recognizers")
    recognizers = RecognizerListLoader.get(
        recognizers_conf,
        supported_languages,
        global_regex_flags,
    )

    recognizers = list(recognizers)

    self.__update_based_on_nlp_recognizer_conf(
        recognizers, recognizers_conf, supported_languages
    )

    registry = RecognizerRegistry(
        recognizers=recognizers,
        supported_languages=supported_languages,
        global_regex_flags=global_regex_flags,
    )

    return registry

Context awareness modules

presidio_analyzer.context_aware_enhancers

Context awareness modules.

ContextAwareEnhancer

A class representing an abstract context aware enhancer.

Context words might enhance confidence score of a recognized entity, ContextAwareEnhancer is an abstract class to be inherited by a context aware enhancer logic.

PARAMETER DESCRIPTION
context_similarity_factor

How much to enhance confidence of match entity

TYPE: float

min_score_with_context_similarity

Minimum confidence score

TYPE: float

context_prefix_count

how many words before the entity to match context

TYPE: int

context_suffix_count

how many words after the entity to match context

TYPE: int

METHOD DESCRIPTION
enhance_using_context

Update results in case surrounding words are relevant to the context words.

Source code in presidio_analyzer/context_aware_enhancers/context_aware_enhancer.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class ContextAwareEnhancer:
    """
    A class representing an abstract context aware enhancer.

    Context words might enhance confidence score of a recognized entity,
    ContextAwareEnhancer is an abstract class to be inherited by a context aware
    enhancer logic.

    :param context_similarity_factor: How much to enhance confidence of match entity
    :param min_score_with_context_similarity: Minimum confidence score
    :param context_prefix_count: how many words before the entity to match context
    :param context_suffix_count: how many words after the entity to match context
    """

    MIN_SCORE = 0
    MAX_SCORE = 1.0

    def __init__(
        self,
        context_similarity_factor: float,
        min_score_with_context_similarity: float,
        context_prefix_count: int,
        context_suffix_count: int,
    ):
        self.context_similarity_factor = context_similarity_factor
        self.min_score_with_context_similarity = min_score_with_context_similarity
        self.context_prefix_count = context_prefix_count
        self.context_suffix_count = context_suffix_count

    @abstractmethod
    def enhance_using_context(
        self,
        text: str,
        raw_results: List[RecognizerResult],
        nlp_artifacts: NlpArtifacts,
        recognizers: List[EntityRecognizer],
        context: Optional[List[str]] = None,
    ) -> List[RecognizerResult]:
        """
        Update results in case surrounding words are relevant to the context words.

        Using the surrounding words of the actual word matches, look
        for specific strings that if found contribute to the score
        of the result, improving the confidence that the match is
        indeed of that PII entity type

        :param text: The actual text that was analyzed
        :param raw_results: Recognizer results which didn't take
                            context into consideration
        :param nlp_artifacts: The nlp artifacts contains elements
                              such as lemmatized tokens for better
                              accuracy of the context enhancement process
        :param recognizers: the list of recognizers
        :param context: list of context words
        """
        return raw_results

enhance_using_context abstractmethod

enhance_using_context(
    text: str,
    raw_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    recognizers: List[EntityRecognizer],
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Update results in case surrounding words are relevant to the context words.

Using the surrounding words of the actual word matches, look for specific strings that if found contribute to the score of the result, improving the confidence that the match is indeed of that PII entity type

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_results

Recognizer results which didn't take context into consideration

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

recognizers

the list of recognizers

TYPE: List[EntityRecognizer]

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/context_aware_enhancers/context_aware_enhancer.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@abstractmethod
def enhance_using_context(
    self,
    text: str,
    raw_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    recognizers: List[EntityRecognizer],
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """
    Update results in case surrounding words are relevant to the context words.

    Using the surrounding words of the actual word matches, look
    for specific strings that if found contribute to the score
    of the result, improving the confidence that the match is
    indeed of that PII entity type

    :param text: The actual text that was analyzed
    :param raw_results: Recognizer results which didn't take
                        context into consideration
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param recognizers: the list of recognizers
    :param context: list of context words
    """
    return raw_results

LemmaContextAwareEnhancer

Bases: ContextAwareEnhancer

A class representing a lemma based context aware enhancer logic.

Context words might enhance confidence score of a recognized entity, LemmaContextAwareEnhancer is an implementation of Lemma based context aware logic, it compares spacy lemmas of each word in context of the matched entity to given context and the recognizer context words, if matched it enhance the recognized entity confidence score by a given factor.

PARAMETER DESCRIPTION
context_similarity_factor

How much to enhance confidence of match entity

TYPE: float DEFAULT: 0.35

min_score_with_context_similarity

Minimum confidence score

TYPE: float DEFAULT: 0.4

context_prefix_count

how many words before the entity to match context

TYPE: int DEFAULT: 5

context_suffix_count

how many words after the entity to match context

TYPE: int DEFAULT: 0

context_matching_mode

Matching mode for context words. Options: - "substring" (default): Match context words as substrings (e.g., 'card' matches 'creditcard', 'lic' matches 'duplicate'). Maintains backward compatibility. - "whole_word": Match context words only as whole words (e.g., 'lic' matches 'lic' but not 'duplicate'). Prevents false positives.

TYPE: str DEFAULT: 'substring'

METHOD DESCRIPTION
enhance_using_context

Update results in case the lemmas of surrounding words or input context

Source code in presidio_analyzer/context_aware_enhancers/lemma_context_aware_enhancer.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
class LemmaContextAwareEnhancer(ContextAwareEnhancer):
    """
    A class representing a lemma based context aware enhancer logic.

    Context words might enhance confidence score of a recognized entity,
    LemmaContextAwareEnhancer is an implementation of Lemma based context aware logic,
    it compares spacy lemmas of each word in context of the matched entity to given
    context and the recognizer context words,
    if matched it enhance the recognized entity confidence score by a given factor.

    :param context_similarity_factor: How much to enhance confidence of match entity
    :param min_score_with_context_similarity: Minimum confidence score
    :param context_prefix_count: how many words before the entity to match context
    :param context_suffix_count: how many words after the entity to match context
    :param context_matching_mode: Matching mode for context words. Options:
        - "substring" (default): Match context words as substrings
          (e.g., 'card' matches 'creditcard', 'lic' matches 'duplicate').
          Maintains backward compatibility.
        - "whole_word": Match context words only as whole words
          (e.g., 'lic' matches 'lic' but not 'duplicate').
          Prevents false positives.
    """

    def __init__(
        self,
        context_similarity_factor: float = 0.35,
        min_score_with_context_similarity: float = 0.4,
        context_prefix_count: int = 5,
        context_suffix_count: int = 0,
        context_matching_mode: str = "substring",
    ):
        super().__init__(
            context_similarity_factor=context_similarity_factor,
            min_score_with_context_similarity=min_score_with_context_similarity,
            context_prefix_count=context_prefix_count,
            context_suffix_count=context_suffix_count,
        )
        if context_matching_mode not in ["whole_word", "substring"]:
            raise ValueError(
                f"context_matching_mode must be one of: 'whole_word', 'substring'. "
                f"Got: {context_matching_mode}"
            )
        self.context_matching_mode = context_matching_mode

    def enhance_using_context(
        self,
        text: str,
        raw_results: List[RecognizerResult],
        nlp_artifacts: NlpArtifacts,
        recognizers: List[EntityRecognizer],
        context: Optional[List[str]] = None,
    ) -> List[RecognizerResult]:
        """
        Update results in case the lemmas of surrounding words or input context
        words are identical to the context words.

        Using the surrounding words of the actual word matches, look
        for specific strings that if found contribute to the score
        of the result, improving the confidence that the match is
        indeed of that PII entity type

        :param text: The actual text that was analyzed
        :param raw_results: Recognizer results which didn't take
                            context into consideration
        :param nlp_artifacts: The nlp artifacts contains elements
                              such as lemmatized tokens for better
                              accuracy of the context enhancement process
        :param recognizers: the list of recognizers
        :param context: list of context words
        """  # noqa: D205,D400

        # create a deep copy of the results object, so we can manipulate it
        results = copy.deepcopy(raw_results)

        # create recognizer context dictionary
        recognizers_dict = {recognizer.id: recognizer for recognizer in recognizers}

        # Create empty list in None or lowercase all context words in the list
        if not context:
            context = []
        else:
            context = [word.lower() for word in context]

        # Sanity
        if nlp_artifacts is None:
            logger.warning("NLP artifacts were not provided")
            return results

        for result in results:
            recognizer = None
            # get recognizer matching the result, if found.
            if (
                result.recognition_metadata
                and RecognizerResult.RECOGNIZER_IDENTIFIER_KEY
                in result.recognition_metadata.keys()
            ):
                recognizer = recognizers_dict.get(
                    result.recognition_metadata[
                        RecognizerResult.RECOGNIZER_IDENTIFIER_KEY
                    ]
                )

            if not recognizer:
                logger.debug(
                    "Recognizer name not found as part of the "
                    "recognition_metadata dict in the RecognizerResult. "
                )
                continue

            # skip recognizer result if the recognizer doesn't support
            # context enhancement
            if not recognizer.context:
                logger.debug(
                    "recognizer '%s' does not support context enhancement",
                    recognizer.name,
                )
                continue

            # skip context enhancement if already boosted by recognizer level
            if result.recognition_metadata.get(
                RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY
            ):
                logger.debug("result score already boosted, skipping")
                continue

            # extract lemmatized context from the surrounding of the match
            word = text[result.start : result.end]

            surrounding_words = self._extract_surrounding_words(
                nlp_artifacts=nlp_artifacts, word=word, start=result.start
            )

            # combine other sources of context with surrounding words
            surrounding_words.extend(context)

            supportive_context_word = self._find_supportive_word_in_context(
                surrounding_words, recognizer.context, self.context_matching_mode
            )
            if supportive_context_word != "":
                result.score += self.context_similarity_factor
                result.score = max(result.score, self.min_score_with_context_similarity)
                result.score = min(result.score, ContextAwareEnhancer.MAX_SCORE)

                # Update the explainability object with context information
                # helped to improve the score
                result.analysis_explanation.set_supportive_context_word(
                    supportive_context_word
                )
                result.analysis_explanation.set_improved_score(result.score)
        return results

    @staticmethod
    def _find_supportive_word_in_context(
        context_list: List[str],
        recognizer_context_list: List[str],
        matching_mode: str = "substring",
    ) -> str:
        """
        Find words in the text which are relevant for context evaluation.

        A word is considered a supportive context word based on the matching mode:
        - "substring" (default): Substring match
          (e.g., 'card' matches 'creditcard', 'lic' matches 'duplicate')
        - "whole_word": Exact whole-word match (case-insensitive)
          (e.g., 'lic' matches 'lic' but not 'duplicate')

        :param context_list words before and after the matched entity within
               a specified window size
        :param recognizer_context_list a list of words considered as
                context keywords manually specified by the recognizer's author
        :param matching_mode: Matching mode ('whole_word' or 'substring').
               Defaults to 'substring'.
        """
        word = ""
        # If the context list is empty, no need to continue
        if context_list is None or recognizer_context_list is None:
            return word

        for predefined_context_word in recognizer_context_list:
            result = False

            if matching_mode == "substring":
                # Substring match (case-insensitive) - default behavior
                # for backward compatibility
                result = next(
                    (
                        True
                        for keyword in context_list
                        if predefined_context_word.lower() in keyword.lower()
                    ),
                    False,
                )
            elif matching_mode == "whole_word":
                # Exact whole-word match (case-insensitive)
                result = next(
                    (
                        True
                        for keyword in context_list
                        if predefined_context_word.lower() == keyword.lower()
                    ),
                    False,
                )

            if result:
                logger.debug("Found context keyword '%s'", predefined_context_word)
                word = predefined_context_word
                break

        return word

    def _extract_surrounding_words(
        self, nlp_artifacts: NlpArtifacts, word: str, start: int
    ) -> List[str]:
        """Extract words surrounding another given word.

        The text from which the context is extracted is given in the nlp
        doc.

        :param nlp_artifacts: An abstraction layer which holds different
                              items which are the result of a NLP pipeline
                              execution on a given text
        :param word: The word to look for context around
        :param start: The start index of the word in the original text
        """
        if not nlp_artifacts.tokens:
            logger.info("Skipping context extraction due to lack of NLP artifacts")
            # if there are no nlp artifacts, this is ok, we can
            # extract context and we return a valid, yet empty
            # context
            return [""]

        # Get the already prepared words in the given text, in their
        # LEMMATIZED version
        lemmatized_keywords = nlp_artifacts.keywords

        # since the list of tokens is not necessarily aligned
        # with the actual index of the match, we look for the
        # token index which corresponds to the match
        token_index = self._find_index_of_match_token(
            word, start, nlp_artifacts.tokens, nlp_artifacts.tokens_indices
        )

        # index i belongs to the PII entity, take the preceding n words
        # and the successing m words into a context list

        backward_context = self._add_n_words_backward(
            token_index,
            self.context_prefix_count,
            nlp_artifacts.lemmas,
            lemmatized_keywords,
        )
        forward_context = self._add_n_words_forward(
            token_index,
            self.context_suffix_count,
            nlp_artifacts.lemmas,
            lemmatized_keywords,
        )

        context_list = []
        context_list.extend(backward_context)
        context_list.extend(forward_context)
        context_list = list(set(context_list))
        logger.debug("Context list is: %s", " ".join(context_list))
        return context_list

    @staticmethod
    def _find_index_of_match_token(
        word: str,
        start: int,
        tokens,
        tokens_indices: List[int],
    ) -> int:
        found = False
        # we use the known start index of the original word to find the actual
        # token at that index, we are not checking for equivalence since the
        # token might be just a substring of that word (e.g. for phone number
        # 555-124564 the first token might be just '555' or for a match like '
        # rocket' the actual token will just be 'rocket' hence the misalignment
        # of indices)
        # Note: we are iterating over the original tokens (not the lemmatized)
        i = -1
        for i, token in enumerate(tokens, 0):
            # Either we found a token with the exact location, or
            # we take a token which its characters indices covers
            # the index we are looking for.
            if (tokens_indices[i] == start) or (start < tokens_indices[i] + len(token)):
                # found the interesting token, the one that around it
                # we take n words, we save the matching lemma
                found = True
                break

        if not found:
            raise ValueError(
                "Did not find word '" + word + "' "
                "in the list of tokens although it "
                "is expected to be found"
            )
        return i

    @staticmethod
    def _add_n_words(
        index: int,
        n_words: int,
        lemmas: List[str],
        lemmatized_filtered_keywords: List[str],
        is_backward: bool,
    ) -> List[str]:
        """
        Prepare a string of context words.

        Return a list of words which surrounds a lemma at a given index.
        The words will be collected only if exist in the filtered array

        :param index: index of the lemma that its surrounding words we want
        :param n_words: number of words to take
        :param lemmas: array of lemmas
        :param lemmatized_filtered_keywords: the array of filtered
               lemmas from the original sentence,
        :param is_backward: if true take the preceeding words, if false,
                            take the successing words
        """
        i = index
        context_words = []
        # The entity itself is no interest to us...however we want to
        # consider it anyway for cases were it is attached with no spaces
        # to an interesting context word, so we allow it and add 1 to
        # the number of collected words

        # collect at most n words (in lower case)
        remaining = n_words + 1
        while 0 <= i < len(lemmas) and remaining > 0:
            lower_lemma = lemmas[i].lower()
            if lower_lemma in lemmatized_filtered_keywords:
                context_words.append(lower_lemma)
                remaining -= 1
            i = i - 1 if is_backward else i + 1
        return context_words

    def _add_n_words_forward(
        self,
        index: int,
        n_words: int,
        lemmas: List[str],
        lemmatized_filtered_keywords: List[str],
    ) -> List[str]:
        return self._add_n_words(
            index, n_words, lemmas, lemmatized_filtered_keywords, False
        )

    def _add_n_words_backward(
        self,
        index: int,
        n_words: int,
        lemmas: List[str],
        lemmatized_filtered_keywords: List[str],
    ) -> List[str]:
        return self._add_n_words(
            index, n_words, lemmas, lemmatized_filtered_keywords, True
        )

enhance_using_context

enhance_using_context(
    text: str,
    raw_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    recognizers: List[EntityRecognizer],
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Update results in case the lemmas of surrounding words or input context words are identical to the context words.

Using the surrounding words of the actual word matches, look for specific strings that if found contribute to the score of the result, improving the confidence that the match is indeed of that PII entity type

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_results

Recognizer results which didn't take context into consideration

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

recognizers

the list of recognizers

TYPE: List[EntityRecognizer]

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/context_aware_enhancers/lemma_context_aware_enhancer.py
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def enhance_using_context(
    self,
    text: str,
    raw_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    recognizers: List[EntityRecognizer],
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """
    Update results in case the lemmas of surrounding words or input context
    words are identical to the context words.

    Using the surrounding words of the actual word matches, look
    for specific strings that if found contribute to the score
    of the result, improving the confidence that the match is
    indeed of that PII entity type

    :param text: The actual text that was analyzed
    :param raw_results: Recognizer results which didn't take
                        context into consideration
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param recognizers: the list of recognizers
    :param context: list of context words
    """  # noqa: D205,D400

    # create a deep copy of the results object, so we can manipulate it
    results = copy.deepcopy(raw_results)

    # create recognizer context dictionary
    recognizers_dict = {recognizer.id: recognizer for recognizer in recognizers}

    # Create empty list in None or lowercase all context words in the list
    if not context:
        context = []
    else:
        context = [word.lower() for word in context]

    # Sanity
    if nlp_artifacts is None:
        logger.warning("NLP artifacts were not provided")
        return results

    for result in results:
        recognizer = None
        # get recognizer matching the result, if found.
        if (
            result.recognition_metadata
            and RecognizerResult.RECOGNIZER_IDENTIFIER_KEY
            in result.recognition_metadata.keys()
        ):
            recognizer = recognizers_dict.get(
                result.recognition_metadata[
                    RecognizerResult.RECOGNIZER_IDENTIFIER_KEY
                ]
            )

        if not recognizer:
            logger.debug(
                "Recognizer name not found as part of the "
                "recognition_metadata dict in the RecognizerResult. "
            )
            continue

        # skip recognizer result if the recognizer doesn't support
        # context enhancement
        if not recognizer.context:
            logger.debug(
                "recognizer '%s' does not support context enhancement",
                recognizer.name,
            )
            continue

        # skip context enhancement if already boosted by recognizer level
        if result.recognition_metadata.get(
            RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY
        ):
            logger.debug("result score already boosted, skipping")
            continue

        # extract lemmatized context from the surrounding of the match
        word = text[result.start : result.end]

        surrounding_words = self._extract_surrounding_words(
            nlp_artifacts=nlp_artifacts, word=word, start=result.start
        )

        # combine other sources of context with surrounding words
        surrounding_words.extend(context)

        supportive_context_word = self._find_supportive_word_in_context(
            surrounding_words, recognizer.context, self.context_matching_mode
        )
        if supportive_context_word != "":
            result.score += self.context_similarity_factor
            result.score = max(result.score, self.min_score_with_context_similarity)
            result.score = min(result.score, ContextAwareEnhancer.MAX_SCORE)

            # Update the explainability object with context information
            # helped to improve the score
            result.analysis_explanation.set_supportive_context_word(
                supportive_context_word
            )
            result.analysis_explanation.set_improved_score(result.score)
    return results

NLP Engine modules

presidio_analyzer.nlp_engine

NLP engine package. Performs text pre-processing.

NerModelConfiguration

Bases: BaseModel

NER model configuration using Pydantic validation.

PARAMETER DESCRIPTION
labels_to_ignore

List of labels to not return predictions for.

aggregation_strategy

See https://huggingface.co/docs/transformers/main_classes/pipelines#transformers.TokenClassificationPipeline.aggregation_strategy

stride

See https://huggingface.co/docs/transformers/main_classes/pipelines#transformers.TokenClassificationPipeline.stride

alignment_mode

See https://spacy.io/api/doc#char_span

default_score

Default confidence score if the model does not provide one.

model_to_presidio_entity_mapping

Mapping between the NER model entities and Presidio entities.

low_score_entity_names

Set of entity names that are likely to have low detection accuracy that should be adjusted.

low_confidence_score_multiplier

A multiplier for the score given for low_score_entity_names. Multiplier to the score given for low_score_entity_names.

METHOD DESCRIPTION
validate_aggregation_strategy

Validate aggregation strategy.

validate_stride

Validate stride and handle None values.

validate_alignment_mode

Validate alignment mode and handle None values.

from_dict

Create NerModelConfiguration from a dictionary with Pydantic validation.

to_dict

Convert to dictionary representation.

Source code in presidio_analyzer/nlp_engine/ner_model_configuration.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class NerModelConfiguration(BaseModel):
    """NER model configuration using Pydantic validation.

    :param labels_to_ignore: List of labels to not return predictions for.
    :param aggregation_strategy:
    See https://huggingface.co/docs/transformers/main_classes/pipelines#transformers.TokenClassificationPipeline.aggregation_strategy
    :param stride:
    See https://huggingface.co/docs/transformers/main_classes/pipelines#transformers.TokenClassificationPipeline.stride
    :param alignment_mode: See https://spacy.io/api/doc#char_span
    :param default_score: Default confidence score if the model does not provide one.
    :param model_to_presidio_entity_mapping:
    Mapping between the NER model entities and Presidio entities.
    :param low_score_entity_names:
    Set of entity names that are likely to have low detection accuracy that should be adjusted.
    :param low_confidence_score_multiplier: A multiplier for the score given for low_score_entity_names.
    Multiplier to the score given for low_score_entity_names.
    """  # noqa: E501

    labels_to_ignore: Optional[Collection[str]] = Field(
        default_factory=list, description="List of labels to ignore"
    )
    aggregation_strategy: Optional[str] = Field(
        default="max", description="Token classification aggregation strategy"
    )
    stride: Optional[int] = Field(
        default=14, description="Stride for token classification"
    )
    alignment_mode: Optional[str] = Field(
        default="expand", description="Alignment mode for spaCy char spans"
    )
    default_score: Optional[float] = Field(
        default=0.85, ge=0.0, le=1.0, description="Default confidence score"
    )
    model_to_presidio_entity_mapping: Optional[Dict[str, str]] = Field(
        default_factory=lambda: MODEL_TO_PRESIDIO_ENTITY_MAPPING.copy(),
        description="Mapping between model entities and Presidio entities",
    )
    low_score_entity_names: Optional[Collection[str]] = Field(
        default_factory=lambda: LOW_SCORE_ENTITY_NAMES.copy(),
        description="Entity names with likely low detection accuracy",
    )
    low_confidence_score_multiplier: Optional[float] = Field(
        default=0.4, ge=0.0, description="Score multiplier for low confidence entities"
    )

    model_config = ConfigDict(arbitrary_types_allowed=True)

    @field_validator("aggregation_strategy")
    @classmethod
    def validate_aggregation_strategy(cls, agg_strategy: str) -> str:
        """Validate aggregation strategy."""
        valid_strategies = ["simple", "first", "average", "max"]
        if agg_strategy not in valid_strategies:
            logger.warning(
                f"Aggregation strategy '{agg_strategy}' might not be supported. "
                f"Valid options: {valid_strategies}"
            )
        return agg_strategy

    @field_validator("stride")
    @classmethod
    def validate_stride(cls, stride: Optional[int]) -> int:
        """Validate stride and handle None values."""
        if stride is None:
            # Get the default value from the field definition
            return cls.model_fields["stride"].default
        return stride

    @field_validator("alignment_mode")
    @classmethod
    def validate_alignment_mode(cls, alignment: Optional[str]) -> str:
        """Validate alignment mode and handle None values."""
        if alignment is None:
            # Get the default value from the field definition
            return cls.model_fields["alignment_mode"].default
        valid_modes = ["strict", "contract", "expand"]
        if alignment not in valid_modes:
            logger.warning(
                f"Alignment mode '{alignment}' might not be supported. "
                f"Valid options: {valid_modes}"
            )
        return alignment

    @classmethod
    def from_dict(cls, ner_model_configuration_dict: Dict) -> "NerModelConfiguration":
        """
        Create NerModelConfiguration from a dictionary with Pydantic validation.

        :param ner_model_configuration_dict: Dictionary containing configuration
        :return: NerModelConfiguration instance
        """
        return cls(**ner_model_configuration_dict)

    def to_dict(self) -> Dict:
        """Convert to dictionary representation."""
        return self.model_dump(exclude_none=True)

validate_aggregation_strategy classmethod

validate_aggregation_strategy(agg_strategy: str) -> str

Validate aggregation strategy.

Source code in presidio_analyzer/nlp_engine/ner_model_configuration.py
80
81
82
83
84
85
86
87
88
89
90
@field_validator("aggregation_strategy")
@classmethod
def validate_aggregation_strategy(cls, agg_strategy: str) -> str:
    """Validate aggregation strategy."""
    valid_strategies = ["simple", "first", "average", "max"]
    if agg_strategy not in valid_strategies:
        logger.warning(
            f"Aggregation strategy '{agg_strategy}' might not be supported. "
            f"Valid options: {valid_strategies}"
        )
    return agg_strategy

validate_stride classmethod

validate_stride(stride: Optional[int]) -> int

Validate stride and handle None values.

Source code in presidio_analyzer/nlp_engine/ner_model_configuration.py
92
93
94
95
96
97
98
99
@field_validator("stride")
@classmethod
def validate_stride(cls, stride: Optional[int]) -> int:
    """Validate stride and handle None values."""
    if stride is None:
        # Get the default value from the field definition
        return cls.model_fields["stride"].default
    return stride

validate_alignment_mode classmethod

validate_alignment_mode(alignment: Optional[str]) -> str

Validate alignment mode and handle None values.

Source code in presidio_analyzer/nlp_engine/ner_model_configuration.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
@field_validator("alignment_mode")
@classmethod
def validate_alignment_mode(cls, alignment: Optional[str]) -> str:
    """Validate alignment mode and handle None values."""
    if alignment is None:
        # Get the default value from the field definition
        return cls.model_fields["alignment_mode"].default
    valid_modes = ["strict", "contract", "expand"]
    if alignment not in valid_modes:
        logger.warning(
            f"Alignment mode '{alignment}' might not be supported. "
            f"Valid options: {valid_modes}"
        )
    return alignment

from_dict classmethod

from_dict(ner_model_configuration_dict: Dict) -> NerModelConfiguration

Create NerModelConfiguration from a dictionary with Pydantic validation.

PARAMETER DESCRIPTION
ner_model_configuration_dict

Dictionary containing configuration

TYPE: Dict

RETURNS DESCRIPTION
NerModelConfiguration

NerModelConfiguration instance

Source code in presidio_analyzer/nlp_engine/ner_model_configuration.py
116
117
118
119
120
121
122
123
124
@classmethod
def from_dict(cls, ner_model_configuration_dict: Dict) -> "NerModelConfiguration":
    """
    Create NerModelConfiguration from a dictionary with Pydantic validation.

    :param ner_model_configuration_dict: Dictionary containing configuration
    :return: NerModelConfiguration instance
    """
    return cls(**ner_model_configuration_dict)

to_dict

to_dict() -> Dict

Convert to dictionary representation.

Source code in presidio_analyzer/nlp_engine/ner_model_configuration.py
126
127
128
def to_dict(self) -> Dict:
    """Convert to dictionary representation."""
    return self.model_dump(exclude_none=True)

NlpArtifacts

NlpArtifacts is an abstraction layer over the results of an NLP pipeline.

processing over a given text, it holds attributes such as entities, tokens and lemmas which can be used by any recognizer

PARAMETER DESCRIPTION
entities

Identified entities

TYPE: List[Span]

tokens

Tokenized text

TYPE: Doc

tokens_indices

Indices of tokens

TYPE: List[int]

lemmas

List of lemmas in text

TYPE: List[str]

nlp_engine

NlpEngine object

TYPE: NlpEngine

language

Text language

TYPE: str

scores

Entity confidence scores

TYPE: Optional[List[float]] DEFAULT: None

METHOD DESCRIPTION
set_keywords

Return keywords fpr text.

to_json

Convert nlp artifacts to json.

Source code in presidio_analyzer/nlp_engine/nlp_artifacts.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class NlpArtifacts:
    """
    NlpArtifacts is an abstraction layer over the results of an NLP pipeline.

    processing over a given text, it holds attributes such as entities,
    tokens and lemmas which can be used by any recognizer

    :param entities: Identified entities
    :param tokens: Tokenized text
    :param tokens_indices: Indices of tokens
    :param lemmas: List of lemmas in text
    :param nlp_engine: NlpEngine object
    :param language: Text language
    :param scores: Entity confidence scores
    """

    def __init__(
        self,
        entities: List[Span],
        tokens: Doc,
        tokens_indices: List[int],
        lemmas: List[str],
        nlp_engine: "NlpEngine",  # noqa: F821
        language: str,
        scores: Optional[List[float]] = None,
    ):
        self.entities = entities
        self.tokens = tokens
        self.lemmas = lemmas
        self.tokens_indices = tokens_indices
        self.keywords = self.set_keywords(nlp_engine, lemmas, language)
        self.nlp_engine = nlp_engine
        self.scores = scores if scores else [0.85] * len(entities)

    @staticmethod
    def set_keywords(
        nlp_engine,
        lemmas: List[str],
        language: str,
    ) -> List[str]:
        """
        Return keywords fpr text.

        Extracts lemmas with certain conditions as keywords.
        """
        if not nlp_engine:
            return []
        keywords = [
            k.lower()
            for k in lemmas
            if not nlp_engine.is_stopword(k, language)
            and not nlp_engine.is_punct(k, language)
            and k != "-PRON-"
            and k != "be"
        ]

        # best effort, try even further to break tokens into sub tokens,
        # this can result in reducing false negatives
        keywords = [i.split(":") for i in keywords]

        # splitting the list can, if happened, will result in list of lists,
        # we flatten the list
        keywords = [item for sublist in keywords for item in sublist]
        return keywords

    def to_json(self) -> str:
        """Convert nlp artifacts to json."""

        return_dict = self.__dict__.copy()

        # Ignore NLP engine as it's not serializable currently
        del return_dict["nlp_engine"]

        # Converting spaCy tokens and spans to string as they are not serializable
        if "tokens" in return_dict:
            return_dict["tokens"] = [token.text for token in self.tokens]
        if "entities" in return_dict:
            return_dict["entities"] = [entity.text for entity in self.entities]
        if "scores" in return_dict:
            return_dict["scores"] = [float(score) for score in self.scores]

        return json.dumps(return_dict)

set_keywords staticmethod

set_keywords(nlp_engine, lemmas: List[str], language: str) -> List[str]

Return keywords fpr text.

Extracts lemmas with certain conditions as keywords.

Source code in presidio_analyzer/nlp_engine/nlp_artifacts.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@staticmethod
def set_keywords(
    nlp_engine,
    lemmas: List[str],
    language: str,
) -> List[str]:
    """
    Return keywords fpr text.

    Extracts lemmas with certain conditions as keywords.
    """
    if not nlp_engine:
        return []
    keywords = [
        k.lower()
        for k in lemmas
        if not nlp_engine.is_stopword(k, language)
        and not nlp_engine.is_punct(k, language)
        and k != "-PRON-"
        and k != "be"
    ]

    # best effort, try even further to break tokens into sub tokens,
    # this can result in reducing false negatives
    keywords = [i.split(":") for i in keywords]

    # splitting the list can, if happened, will result in list of lists,
    # we flatten the list
    keywords = [item for sublist in keywords for item in sublist]
    return keywords

to_json

to_json() -> str

Convert nlp artifacts to json.

Source code in presidio_analyzer/nlp_engine/nlp_artifacts.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def to_json(self) -> str:
    """Convert nlp artifacts to json."""

    return_dict = self.__dict__.copy()

    # Ignore NLP engine as it's not serializable currently
    del return_dict["nlp_engine"]

    # Converting spaCy tokens and spans to string as they are not serializable
    if "tokens" in return_dict:
        return_dict["tokens"] = [token.text for token in self.tokens]
    if "entities" in return_dict:
        return_dict["entities"] = [entity.text for entity in self.entities]
    if "scores" in return_dict:
        return_dict["scores"] = [float(score) for score in self.scores]

    return json.dumps(return_dict)

NlpEngine

Bases: ABC

NlpEngine is an abstraction layer over the nlp module.

It provides NLP preprocessing functionality as well as other queries on tokens.

METHOD DESCRIPTION
load

Load the NLP model.

is_loaded

Return True if the model is already loaded.

process_text

Execute the NLP pipeline on the given text and language.

process_batch

Execute the NLP pipeline on a batch of texts.

is_stopword

Return true if the given word is a stop word.

is_punct

Return true if the given word is a punctuation word.

get_supported_entities

Return the supported entities for this NLP engine.

get_supported_languages

Return the supported languages for this NLP engine.

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class NlpEngine(ABC):
    """
    NlpEngine is an abstraction layer over the nlp module.

    It provides NLP preprocessing functionality as well as other queries
    on tokens.
    """

    @abstractmethod
    def load(self) -> None:
        """Load the NLP model."""

    @abstractmethod
    def is_loaded(self) -> bool:
        """Return True if the model is already loaded."""

    @abstractmethod
    def process_text(self, text: str, language: str) -> NlpArtifacts:
        """Execute the NLP pipeline on the given text and language."""

    @abstractmethod
    def process_batch(
        self,
        texts: Iterable[str],
        language: str,
        batch_size: int = 1,
        n_process: int = 1,
        **kwargs,
    ) -> Iterator[Tuple[str, NlpArtifacts]]:
        """Execute the NLP pipeline on a batch of texts.

        Returns a tuple of (text, NlpArtifacts)
        """

    @abstractmethod
    def is_stopword(self, word: str, language: str) -> bool:
        """
        Return true if the given word is a stop word.

        (within the given language)
        """

    @abstractmethod
    def is_punct(self, word: str, language: str) -> bool:
        """
        Return true if the given word is a punctuation word.

        (within the given language)
        """

    @abstractmethod
    def get_supported_entities(self) -> List[str]:
        """Return the supported entities for this NLP engine."""
        pass

    @abstractmethod
    def get_supported_languages(self) -> List[str]:
        """Return the supported languages for this NLP engine."""
        pass

load abstractmethod

load() -> None

Load the NLP model.

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
15
16
17
@abstractmethod
def load(self) -> None:
    """Load the NLP model."""

is_loaded abstractmethod

is_loaded() -> bool

Return True if the model is already loaded.

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
19
20
21
@abstractmethod
def is_loaded(self) -> bool:
    """Return True if the model is already loaded."""

process_text abstractmethod

process_text(text: str, language: str) -> NlpArtifacts

Execute the NLP pipeline on the given text and language.

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
23
24
25
@abstractmethod
def process_text(self, text: str, language: str) -> NlpArtifacts:
    """Execute the NLP pipeline on the given text and language."""

process_batch abstractmethod

process_batch(
    texts: Iterable[str],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    **kwargs
) -> Iterator[Tuple[str, NlpArtifacts]]

Execute the NLP pipeline on a batch of texts.

Returns a tuple of (text, NlpArtifacts)

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
27
28
29
30
31
32
33
34
35
36
37
38
39
@abstractmethod
def process_batch(
    self,
    texts: Iterable[str],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    **kwargs,
) -> Iterator[Tuple[str, NlpArtifacts]]:
    """Execute the NLP pipeline on a batch of texts.

    Returns a tuple of (text, NlpArtifacts)
    """

is_stopword abstractmethod

is_stopword(word: str, language: str) -> bool

Return true if the given word is a stop word.

(within the given language)

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
41
42
43
44
45
46
47
@abstractmethod
def is_stopword(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a stop word.

    (within the given language)
    """

is_punct abstractmethod

is_punct(word: str, language: str) -> bool

Return true if the given word is a punctuation word.

(within the given language)

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
49
50
51
52
53
54
55
@abstractmethod
def is_punct(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a punctuation word.

    (within the given language)
    """

get_supported_entities abstractmethod

get_supported_entities() -> List[str]

Return the supported entities for this NLP engine.

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
57
58
59
60
@abstractmethod
def get_supported_entities(self) -> List[str]:
    """Return the supported entities for this NLP engine."""
    pass

get_supported_languages abstractmethod

get_supported_languages() -> List[str]

Return the supported languages for this NLP engine.

Source code in presidio_analyzer/nlp_engine/nlp_engine.py
62
63
64
65
@abstractmethod
def get_supported_languages(self) -> List[str]:
    """Return the supported languages for this NLP engine."""
    pass

SlimSpacyNlpEngine

Bases: NlpEngine

A slim NLP engine that provides only tokenization and lemmatization.

This engine loads spaCy models with the NER pipeline component disabled, reducing memory usage and load time. It is intended for use in Presidio v3 where entity extraction is handled by self-contained recognizers rather than the shared NLP engine.

The slim engine: - Provides tokenization, lemmatization, stopword and punctuation checks. - Does NOT extract named entities (returns empty entity lists). - Uses small spaCy models by default for fast loading and low memory. - Supports auto-downloading spaCy models when they are missing. - Falls back to a generic tokenizer for unsupported languages.

PARAMETER DESCRIPTION
models

List of model configurations per language. Example: [{"lang_code": "en", "model_name": "en_core_web_sm"}] If not provided, uses default small models for the given languages.

TYPE: Optional[List[Dict[str, str]]] DEFAULT: None

supported_languages

List of language codes to support. Used only when models is not provided, to select default models.

TYPE: Optional[List[str]] DEFAULT: None

auto_download

Whether to automatically download missing spaCy models.

TYPE: bool DEFAULT: True

generic_tokenizer

Model name to use as a fallback for languages without a default model. Set to "blank" to use spacy.blank(lang) (basic tokenization, no lemmatization). Set to a spaCy model name (e.g. "xx_ent_wiki_sm") to use that model for all fallback languages. If None, a ValueError is raised for unsupported languages.

TYPE: Optional[str] DEFAULT: None

METHOD DESCRIPTION
load

Load spaCy models with NER disabled.

is_loaded

Return True if the model is already loaded.

process_text

Execute the slim NLP pipeline on the given text.

process_batch

Execute the slim NLP pipeline on a batch of texts.

is_stopword

Return true if the given word is a stop word.

is_punct

Return true if the given word is a punctuation word.

get_supported_entities

Return an empty list — the slim engine does not extract entities.

get_supported_languages

Return the supported languages for this NLP engine.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
class SlimSpacyNlpEngine(NlpEngine):
    """A slim NLP engine that provides only tokenization and lemmatization.

    This engine loads spaCy models with the NER pipeline component disabled,
    reducing memory usage and load time. It is intended for use in Presidio v3
    where entity extraction is handled by self-contained recognizers rather
    than the shared NLP engine.

    The slim engine:
    - Provides tokenization, lemmatization, stopword and punctuation checks.
    - Does NOT extract named entities (returns empty entity lists).
    - Uses small spaCy models by default for fast loading and low memory.
    - Supports auto-downloading spaCy models when they are missing.
    - Falls back to a generic tokenizer for unsupported languages.

    :param models: List of model configurations per language.
        Example: [{"lang_code": "en", "model_name": "en_core_web_sm"}]
        If not provided, uses default small models for the given languages.
    :param supported_languages: List of language codes to support.
        Used only when models is not provided, to select default models.
    :param auto_download: Whether to automatically download missing spaCy models.
    :param generic_tokenizer: Model name to use as a fallback for languages
        without a default model. Set to "blank" to use spacy.blank(lang)
        (basic tokenization, no lemmatization). Set to a spaCy model name
        (e.g. "xx_ent_wiki_sm") to use that model for all fallback languages.
        If None, a ValueError is raised for unsupported languages.
    """

    engine_name = "slim"
    is_available = bool(spacy)

    def __init__(
        self,
        models: Optional[List[Dict[str, str]]] = None,
        supported_languages: Optional[List[str]] = None,
        auto_download: bool = True,
        generic_tokenizer: Optional[str] = None,
    ):
        self.generic_tokenizer = generic_tokenizer
        self._blank_languages: Set[str] = set()

        if models:
            self.models = models
        elif supported_languages:
            self.models = self._models_from_languages(
                supported_languages, generic_tokenizer
            )
        else:
            self.models = [{"lang_code": "en", "model_name": "en_core_web_sm"}]

        self.auto_download = auto_download
        self.nlp = None

    def _models_from_languages(
        self,
        languages: List[str],
        generic_tokenizer: Optional[str] = None,
    ) -> List[Dict[str, str]]:
        """Build model configs from language codes using default small models.

        If a language has no default model and generic_tokenizer is set,
        uses the generic tokenizer as a fallback. If generic_tokenizer is
        "blank", creates a bare spacy.blank() pipeline for that language.
        """
        models = []
        for lang in languages:
            model_name = DEFAULT_SLIM_MODELS.get(lang)
            if not model_name:
                if generic_tokenizer:
                    if generic_tokenizer == "blank":
                        self._blank_languages.add(lang)
                        continue
                    else:
                        model_name = generic_tokenizer
                    logger.info(
                        f"No default slim model for language '{lang}', "
                        f"using generic tokenizer: {model_name}"
                    )
                else:
                    raise ValueError(
                        f"No default slim model for language '{lang}'. "
                        f"Provide an explicit model via the 'models' parameter, "
                        f"or set 'generic_tokenizer' to 'blank' or a spaCy model "
                        f"name for a fallback. "
                        f"Supported defaults: {sorted(DEFAULT_SLIM_MODELS.keys())}"
                    )
            models.append({"lang_code": lang, "model_name": model_name})
        return models

    def load(self) -> None:
        """Load spaCy models with NER disabled."""
        logger.debug(f"Loading slim SpaCy models: {self.models}")

        self.nlp = {}

        # Load blank pipelines for languages that have no trained model
        for lang in self._blank_languages:
            try:
                self.nlp[lang] = spacy.blank(lang)
            except ImportError:
                logger.warning(
                    f"spaCy has no language module for '{lang}', "
                    f"falling back to multilingual tokenizer (xx)"
                )
                self.nlp[lang] = spacy.blank("xx")
            logger.info(f"Created blank spaCy pipeline for '{lang}'")

        # Load trained models with NER/parser disabled
        for model in self.models:
            self._validate_model_params(model)
            model_name = model["model_name"]

            if self.auto_download:
                self._download_spacy_model_if_needed(model_name)

            self.nlp[model["lang_code"]] = spacy.load(
                model_name, disable=["ner", "parser"]
            )

        logger.info(f"Loaded slim NLP engine with languages: {list(self.nlp.keys())}")

    @staticmethod
    def _download_spacy_model_if_needed(model_name: str) -> None:
        """Download a spaCy model if it is not already installed."""
        if not (spacy.util.is_package(model_name) or Path(model_name).exists()):
            logger.warning(f"Model {model_name} is not installed. Downloading...")
            spacy.cli.download(model_name)
            logger.info(f"Finished downloading model {model_name}")

    @staticmethod
    def _validate_model_params(model: Dict) -> None:
        """Validate that required model parameters are present."""
        if "lang_code" not in model:
            raise ValueError("lang_code is missing from model configuration")
        if "model_name" not in model:
            raise ValueError("model_name is missing from model configuration")
        if not isinstance(model["model_name"], str):
            raise ValueError("model_name must be a string")

    def is_loaded(self) -> bool:
        """Return True if the model is already loaded."""
        return self.nlp is not None

    def process_text(self, text: str, language: str) -> NlpArtifacts:
        """Execute the slim NLP pipeline on the given text.

        Performs tokenization and lemmatization only. No named entities
        are extracted.
        """
        if not self.nlp:
            raise ValueError("NLP engine is not loaded. Consider calling .load()")

        if language not in self.nlp:
            raise ValueError(
                f"Language '{language}' is not supported by this NLP engine. "
                f"Supported languages: {list(self.nlp.keys())}"
            )

        doc = self.nlp[language](text)
        return self._doc_to_nlp_artifact(doc, language)

    def process_batch(
        self,
        texts: Union[List[str], List[Tuple[str, object]]],
        language: str,
        batch_size: int = 1,
        n_process: int = 1,
        as_tuples: bool = False,
    ) -> Generator[
        Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
    ]:
        """Execute the slim NLP pipeline on a batch of texts."""
        if not self.nlp:
            raise ValueError("NLP engine is not loaded. Consider calling .load()")

        if language not in self.nlp:
            raise ValueError(
                f"Language '{language}' is not supported by this NLP engine. "
                f"Supported languages: {list(self.nlp.keys())}"
            )

        if as_tuples:
            if not all(isinstance(item, tuple) and len(item) == 2 for item in texts):
                raise ValueError(
                    "When 'as_tuples' is True, "
                    "'texts' must be a list of tuples (text, context)."
                )
            texts = ((str(text), context) for text, context in texts)
        else:
            texts = (str(text) for text in texts)

        batch_output = self.nlp[language].pipe(
            texts, as_tuples=as_tuples, batch_size=batch_size, n_process=n_process
        )
        for output in batch_output:
            if as_tuples:
                doc, context = output
                yield doc.text, self._doc_to_nlp_artifact(doc, language), context
            else:
                doc = output
                yield doc.text, self._doc_to_nlp_artifact(doc, language)

    def is_stopword(self, word: str, language: str) -> bool:
        """Return true if the given word is a stop word."""
        return self.nlp[language].vocab[word].is_stop

    def is_punct(self, word: str, language: str) -> bool:
        """Return true if the given word is a punctuation word."""
        return self.nlp[language].vocab[word].is_punct

    def get_supported_entities(self) -> List[str]:
        """Return an empty list — the slim engine does not extract entities."""
        return []

    def get_supported_languages(self) -> List[str]:
        """Return the supported languages for this NLP engine."""
        if not self.nlp:
            raise ValueError("NLP engine is not loaded. Consider calling .load()")
        return list(self.nlp.keys())

    def _doc_to_nlp_artifact(self, doc: Doc, language: str) -> NlpArtifacts:
        """Convert a spaCy Doc to NlpArtifacts with no entities."""
        lemmas = [token.lemma_ for token in doc]
        tokens_indices = [token.idx for token in doc]

        return NlpArtifacts(
            entities=[],
            tokens=doc,
            tokens_indices=tokens_indices,
            lemmas=lemmas,
            nlp_engine=self,
            language=language,
            scores=[],
        )

load

load() -> None

Load spaCy models with NER disabled.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def load(self) -> None:
    """Load spaCy models with NER disabled."""
    logger.debug(f"Loading slim SpaCy models: {self.models}")

    self.nlp = {}

    # Load blank pipelines for languages that have no trained model
    for lang in self._blank_languages:
        try:
            self.nlp[lang] = spacy.blank(lang)
        except ImportError:
            logger.warning(
                f"spaCy has no language module for '{lang}', "
                f"falling back to multilingual tokenizer (xx)"
            )
            self.nlp[lang] = spacy.blank("xx")
        logger.info(f"Created blank spaCy pipeline for '{lang}'")

    # Load trained models with NER/parser disabled
    for model in self.models:
        self._validate_model_params(model)
        model_name = model["model_name"]

        if self.auto_download:
            self._download_spacy_model_if_needed(model_name)

        self.nlp[model["lang_code"]] = spacy.load(
            model_name, disable=["ner", "parser"]
        )

    logger.info(f"Loaded slim NLP engine with languages: {list(self.nlp.keys())}")

is_loaded

is_loaded() -> bool

Return True if the model is already loaded.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
175
176
177
def is_loaded(self) -> bool:
    """Return True if the model is already loaded."""
    return self.nlp is not None

process_text

process_text(text: str, language: str) -> NlpArtifacts

Execute the slim NLP pipeline on the given text.

Performs tokenization and lemmatization only. No named entities are extracted.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def process_text(self, text: str, language: str) -> NlpArtifacts:
    """Execute the slim NLP pipeline on the given text.

    Performs tokenization and lemmatization only. No named entities
    are extracted.
    """
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    if language not in self.nlp:
        raise ValueError(
            f"Language '{language}' is not supported by this NLP engine. "
            f"Supported languages: {list(self.nlp.keys())}"
        )

    doc = self.nlp[language](text)
    return self._doc_to_nlp_artifact(doc, language)

process_batch

process_batch(
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]

Execute the slim NLP pipeline on a batch of texts.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
def process_batch(
    self,
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]:
    """Execute the slim NLP pipeline on a batch of texts."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    if language not in self.nlp:
        raise ValueError(
            f"Language '{language}' is not supported by this NLP engine. "
            f"Supported languages: {list(self.nlp.keys())}"
        )

    if as_tuples:
        if not all(isinstance(item, tuple) and len(item) == 2 for item in texts):
            raise ValueError(
                "When 'as_tuples' is True, "
                "'texts' must be a list of tuples (text, context)."
            )
        texts = ((str(text), context) for text, context in texts)
    else:
        texts = (str(text) for text in texts)

    batch_output = self.nlp[language].pipe(
        texts, as_tuples=as_tuples, batch_size=batch_size, n_process=n_process
    )
    for output in batch_output:
        if as_tuples:
            doc, context = output
            yield doc.text, self._doc_to_nlp_artifact(doc, language), context
        else:
            doc = output
            yield doc.text, self._doc_to_nlp_artifact(doc, language)

is_stopword

is_stopword(word: str, language: str) -> bool

Return true if the given word is a stop word.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
238
239
240
def is_stopword(self, word: str, language: str) -> bool:
    """Return true if the given word is a stop word."""
    return self.nlp[language].vocab[word].is_stop

is_punct

is_punct(word: str, language: str) -> bool

Return true if the given word is a punctuation word.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
242
243
244
def is_punct(self, word: str, language: str) -> bool:
    """Return true if the given word is a punctuation word."""
    return self.nlp[language].vocab[word].is_punct

get_supported_entities

get_supported_entities() -> List[str]

Return an empty list — the slim engine does not extract entities.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
246
247
248
def get_supported_entities(self) -> List[str]:
    """Return an empty list — the slim engine does not extract entities."""
    return []

get_supported_languages

get_supported_languages() -> List[str]

Return the supported languages for this NLP engine.

Source code in presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py
250
251
252
253
254
def get_supported_languages(self) -> List[str]:
    """Return the supported languages for this NLP engine."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")
    return list(self.nlp.keys())

SpacyNlpEngine

Bases: NlpEngine

SpacyNlpEngine is an abstraction layer over the nlp module.

It provides processing functionality as well as other queries on tokens. The SpacyNlpEngine uses SpaCy as its NLP module

METHOD DESCRIPTION
load

Load the spaCy NLP model.

get_supported_entities

Return the supported entities for this NLP engine.

get_supported_languages

Return the supported languages for this NLP engine.

is_loaded

Return True if the model is already loaded.

process_text

Execute the SpaCy NLP pipeline on the given text and language.

process_batch

Execute the NLP pipeline on a batch of texts using spacy pipe.

is_stopword

Return true if the given word is a stop word.

is_punct

Return true if the given word is a punctuation word.

get_nlp

Return the language model loaded for a language.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
class SpacyNlpEngine(NlpEngine):
    """
    SpacyNlpEngine is an abstraction layer over the nlp module.

    It provides processing functionality as well as other queries
    on tokens.
    The SpacyNlpEngine uses SpaCy as its NLP module
    """

    engine_name = "spacy"
    is_available = bool(spacy)

    def __init__(
        self,
        models: Optional[List[Dict[str, str]]] = None,
        ner_model_configuration: Optional[NerModelConfiguration] = None,
    ):
        """
        Initialize a wrapper on spaCy functionality.

        :param models: Dictionary with the name of the spaCy model per language.
        For example: models = [{"lang_code": "en", "model_name": "en_core_web_lg"}]
        :param ner_model_configuration: Parameters for the NER model.
        See conf/spacy.yaml for an example
        """
        if not models:
            models = [{"lang_code": "en", "model_name": "en_core_web_lg"}]
        self.models = models

        if not ner_model_configuration:
            ner_model_configuration = NerModelConfiguration()
        self.ner_model_configuration = ner_model_configuration

        self.nlp = None

    def _enable_gpu(self) -> None:
        """Enable GPU support for spaCy/transformers if available."""
        device = device_detector.get_device()
        if device != "cpu":
            try:
                spacy.require_gpu()
            except Exception as e:
                logger.warning(
                    f"Failed to enable GPU ({device}), falling back to CPU: {e}"
                )

    def load(self) -> None:
        """Load the spaCy NLP model."""
        logger.debug(f"Loading SpaCy models: {self.models}")

        self._enable_gpu()

        self.nlp = {}
        for model in self.models:
            self._validate_model_params(model)
            self._download_spacy_model_if_needed(model["model_name"])
            self.nlp[model["lang_code"]] = spacy.load(model["model_name"])

    @staticmethod
    def _download_spacy_model_if_needed(model_name: str) -> None:
        if not (spacy.util.is_package(model_name) or Path(model_name).exists()):
            logger.warning(f"Model {model_name} is not installed. Downloading...")
            spacy.cli.download(model_name)
            logger.info(f"Finished downloading model {model_name}")

    @staticmethod
    def _validate_model_params(model: Dict) -> None:
        if "lang_code" not in model:
            raise ValueError("lang_code is missing from model configuration")
        if "model_name" not in model:
            raise ValueError("model_name is missing from model configuration")
        if not isinstance(model["model_name"], str):
            raise ValueError("model_name must be a string")

    def get_supported_entities(self) -> List[str]:
        """Return the supported entities for this NLP engine."""
        if not self.ner_model_configuration.model_to_presidio_entity_mapping:
            raise ValueError(
                "model_to_presidio_entity_mapping is missing from model configuration"
            )
        entities_from_mapping = list(
            set(self.ner_model_configuration.model_to_presidio_entity_mapping.values())
        )
        entities = [
            ent
            for ent in entities_from_mapping
            if ent not in self.ner_model_configuration.labels_to_ignore
        ]
        return entities

    def get_supported_languages(self) -> List[str]:
        """Return the supported languages for this NLP engine."""
        if not self.nlp:
            raise ValueError("NLP engine is not loaded. Consider calling .load()")
        return list(self.nlp.keys())

    def is_loaded(self) -> bool:
        """Return True if the model is already loaded."""
        return self.nlp is not None

    def process_text(self, text: str, language: str) -> NlpArtifacts:
        """Execute the SpaCy NLP pipeline on the given text and language."""
        if not self.nlp:
            raise ValueError("NLP engine is not loaded. Consider calling .load()")

        doc = self.nlp[language](text)
        return self._doc_to_nlp_artifact(doc, language)

    def process_batch(
        self,
        texts: Union[List[str], List[Tuple[str, object]]],
        language: str,
        batch_size: int = 1,
        n_process: int = 1,
        as_tuples: bool = False,
    ) -> Generator[
        Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
    ]:
        """Execute the NLP pipeline on a batch of texts using spacy pipe.

        :param texts: A list of texts to process. if as_tuples is set to True,
            texts should be a list of tuples (text, context).
        :param language: The language of the texts.
        :param batch_size: Default batch size for pipe and evaluate.
        :param n_process: Number of processors to process texts.
        :param as_tuples: If set to True, inputs should be a sequence of
            (text, context) tuples. Output will then be a sequence of
            (doc, context) tuples. Defaults to False.

        :return: A generator of tuples (text, NlpArtifacts, context) or
            (text, NlpArtifacts) depending on the value of as_tuples.
        """

        if not self.nlp:
            raise ValueError("NLP engine is not loaded. Consider calling .load()")

        if as_tuples:
            if not all(isinstance(item, tuple) and len(item) == 2 for item in texts):
                raise ValueError(
                    "When 'as_tuples' is True, "
                    "'texts' must be a list of tuples (text, context)."
                )
            texts = ((str(text), context) for text, context in texts)
        else:
            texts = (str(text) for text in texts)
        batch_output = self.nlp[language].pipe(
            texts, as_tuples=as_tuples, batch_size=batch_size, n_process=n_process
        )
        for output in batch_output:
            if as_tuples:
                doc, context = output
                yield doc.text, self._doc_to_nlp_artifact(doc, language), context
            else:
                doc = output
                yield doc.text, self._doc_to_nlp_artifact(doc, language)

    def is_stopword(self, word: str, language: str) -> bool:
        """
        Return true if the given word is a stop word.

        (within the given language)
        """
        return self.nlp[language].vocab[word].is_stop

    def is_punct(self, word: str, language: str) -> bool:
        """
        Return true if the given word is a punctuation word.

        (within the given language).
        """
        return self.nlp[language].vocab[word].is_punct

    def get_nlp(self, language: str) -> Language:
        """
        Return the language model loaded for a language.

        :param language: Language
        :return: Model from spaCy
        """
        return self.nlp[language]

    def _doc_to_nlp_artifact(self, doc: Doc, language: str) -> NlpArtifacts:
        lemmas = [token.lemma_ for token in doc]
        tokens_indices = [token.idx for token in doc]

        entities = self._get_entities(doc)
        scores = self._get_scores_for_entities(doc)

        entities, scores = self._get_updated_entities(entities, scores)

        return NlpArtifacts(
            entities=entities,
            tokens=doc,
            tokens_indices=tokens_indices,
            lemmas=lemmas,
            nlp_engine=self,
            language=language,
            scores=scores,
        )

    def _get_entities(self, doc: Doc) -> List[Span]:
        """
        Extract entities out of a spaCy pipeline, depending on the type of pipeline.

        For normal spaCy, this would be doc.ents
        :param doc: the output spaCy doc.
        :return: List of entities
        """

        return doc.ents

    def _get_scores_for_entities(self, doc: Doc) -> List[float]:
        """Extract scores for entities from the doc.

        Since spaCy does not provide confidence scores for entities by default,
        we use the default score from the ner model configuration.
        :param doc: SpaCy doc
        """

        entities = doc.ents
        scores = [self.ner_model_configuration.default_score] * len(entities)
        return scores

    def _get_updated_entities(
        self, entities: List[Span], scores: List[float]
    ) -> Tuple[List[Span], List[float]]:
        """
        Get an updated list of entities based on the ner model configuration.

        Remove entities that are in labels_to_ignore,
        update entity names based on model_to_presidio_entity_mapping

        :param entities: Entities that were extracted from a spaCy pipeline
        :param scores: Original confidence scores for the entities extracted
        :return: Tuple holding the entities and confidence scores
        """
        if len(entities) != len(scores):
            raise ValueError("Entities and scores must be the same length")

        new_entities = []
        new_scores = []

        mapping = self.ner_model_configuration.model_to_presidio_entity_mapping
        to_ignore = self.ner_model_configuration.labels_to_ignore
        for ent, score in zip(entities, scores):
            # Remove model labels in the ignore list
            if ent.label_ in to_ignore:
                continue

            # Update entity label based on mapping
            if ent.label_ in mapping:
                ent.label_ = mapping[ent.label_]
            else:
                logger.warning(
                    f"Entity {ent.label_} is not mapped to a Presidio entity, "
                    f"but keeping anyway. "
                    f"Add to `NerModelConfiguration.labels_to_ignore` to remove."
                )

            # Remove presidio entities in the ignore list
            if ent.label_ in to_ignore:
                continue

            new_entities.append(ent)

            # Update score if entity is in low score entity names
            if ent.label_ in self.ner_model_configuration.low_score_entity_names:
                score *= self.ner_model_configuration.low_confidence_score_multiplier

            new_scores.append(score)

        return new_entities, new_scores

load

load() -> None

Load the spaCy NLP model.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
65
66
67
68
69
70
71
72
73
74
75
def load(self) -> None:
    """Load the spaCy NLP model."""
    logger.debug(f"Loading SpaCy models: {self.models}")

    self._enable_gpu()

    self.nlp = {}
    for model in self.models:
        self._validate_model_params(model)
        self._download_spacy_model_if_needed(model["model_name"])
        self.nlp[model["lang_code"]] = spacy.load(model["model_name"])

get_supported_entities

get_supported_entities() -> List[str]

Return the supported entities for this NLP engine.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def get_supported_entities(self) -> List[str]:
    """Return the supported entities for this NLP engine."""
    if not self.ner_model_configuration.model_to_presidio_entity_mapping:
        raise ValueError(
            "model_to_presidio_entity_mapping is missing from model configuration"
        )
    entities_from_mapping = list(
        set(self.ner_model_configuration.model_to_presidio_entity_mapping.values())
    )
    entities = [
        ent
        for ent in entities_from_mapping
        if ent not in self.ner_model_configuration.labels_to_ignore
    ]
    return entities

get_supported_languages

get_supported_languages() -> List[str]

Return the supported languages for this NLP engine.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
109
110
111
112
113
def get_supported_languages(self) -> List[str]:
    """Return the supported languages for this NLP engine."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")
    return list(self.nlp.keys())

is_loaded

is_loaded() -> bool

Return True if the model is already loaded.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
115
116
117
def is_loaded(self) -> bool:
    """Return True if the model is already loaded."""
    return self.nlp is not None

process_text

process_text(text: str, language: str) -> NlpArtifacts

Execute the SpaCy NLP pipeline on the given text and language.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
119
120
121
122
123
124
125
def process_text(self, text: str, language: str) -> NlpArtifacts:
    """Execute the SpaCy NLP pipeline on the given text and language."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    doc = self.nlp[language](text)
    return self._doc_to_nlp_artifact(doc, language)

process_batch

process_batch(
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]

Execute the NLP pipeline on a batch of texts using spacy pipe.

PARAMETER DESCRIPTION
texts

A list of texts to process. if as_tuples is set to True, texts should be a list of tuples (text, context).

TYPE: Union[List[str], List[Tuple[str, object]]]

language

The language of the texts.

TYPE: str

batch_size

Default batch size for pipe and evaluate.

TYPE: int DEFAULT: 1

n_process

Number of processors to process texts.

TYPE: int DEFAULT: 1

as_tuples

If set to True, inputs should be a sequence of (text, context) tuples. Output will then be a sequence of (doc, context) tuples. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Generator[Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None]

A generator of tuples (text, NlpArtifacts, context) or (text, NlpArtifacts) depending on the value of as_tuples.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def process_batch(
    self,
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]:
    """Execute the NLP pipeline on a batch of texts using spacy pipe.

    :param texts: A list of texts to process. if as_tuples is set to True,
        texts should be a list of tuples (text, context).
    :param language: The language of the texts.
    :param batch_size: Default batch size for pipe and evaluate.
    :param n_process: Number of processors to process texts.
    :param as_tuples: If set to True, inputs should be a sequence of
        (text, context) tuples. Output will then be a sequence of
        (doc, context) tuples. Defaults to False.

    :return: A generator of tuples (text, NlpArtifacts, context) or
        (text, NlpArtifacts) depending on the value of as_tuples.
    """

    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    if as_tuples:
        if not all(isinstance(item, tuple) and len(item) == 2 for item in texts):
            raise ValueError(
                "When 'as_tuples' is True, "
                "'texts' must be a list of tuples (text, context)."
            )
        texts = ((str(text), context) for text, context in texts)
    else:
        texts = (str(text) for text in texts)
    batch_output = self.nlp[language].pipe(
        texts, as_tuples=as_tuples, batch_size=batch_size, n_process=n_process
    )
    for output in batch_output:
        if as_tuples:
            doc, context = output
            yield doc.text, self._doc_to_nlp_artifact(doc, language), context
        else:
            doc = output
            yield doc.text, self._doc_to_nlp_artifact(doc, language)

is_stopword

is_stopword(word: str, language: str) -> bool

Return true if the given word is a stop word.

(within the given language)

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
175
176
177
178
179
180
181
def is_stopword(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a stop word.

    (within the given language)
    """
    return self.nlp[language].vocab[word].is_stop

is_punct

is_punct(word: str, language: str) -> bool

Return true if the given word is a punctuation word.

(within the given language).

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
183
184
185
186
187
188
189
def is_punct(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a punctuation word.

    (within the given language).
    """
    return self.nlp[language].vocab[word].is_punct

get_nlp

get_nlp(language: str) -> Language

Return the language model loaded for a language.

PARAMETER DESCRIPTION
language

Language

TYPE: str

RETURNS DESCRIPTION
Language

Model from spaCy

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
191
192
193
194
195
196
197
198
def get_nlp(self, language: str) -> Language:
    """
    Return the language model loaded for a language.

    :param language: Language
    :return: Model from spaCy
    """
    return self.nlp[language]

StanzaNlpEngine

Bases: SpacyNlpEngine

StanzaNlpEngine is an abstraction layer over the nlp module.

It provides processing functionality as well as other queries on tokens. The StanzaNlpEngine uses spacy-stanza and stanza as its NLP module

PARAMETER DESCRIPTION
models

Dictionary with the name of the spaCy model per language. For example: models = [{"lang_code": "en", "model_name": "en"}]

TYPE: Optional[List[Dict[str, str]]] DEFAULT: None

ner_model_configuration

Parameters for the NER model. See conf/stanza.yaml for an example

TYPE: Optional[NerModelConfiguration] DEFAULT: None

METHOD DESCRIPTION
is_loaded

Return True if the model is already loaded.

process_text

Execute the SpaCy NLP pipeline on the given text and language.

is_stopword

Return true if the given word is a stop word.

is_punct

Return true if the given word is a punctuation word.

get_supported_entities

Return the supported entities for this NLP engine.

get_supported_languages

Return the supported languages for this NLP engine.

get_nlp

Return the language model loaded for a language.

load

Load the NLP model.

process_batch

Execute the NLP pipeline on a batch of texts using Stanza's bulk processing.

Source code in presidio_analyzer/nlp_engine/stanza_nlp_engine.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
class StanzaNlpEngine(SpacyNlpEngine):
    """
    StanzaNlpEngine is an abstraction layer over the nlp module.

    It provides processing functionality as well as other queries
    on tokens.
    The StanzaNlpEngine uses spacy-stanza and stanza as its NLP module

    :param models: Dictionary with the name of the spaCy model per language.
    For example: models = [{"lang_code": "en", "model_name": "en"}]
    :param ner_model_configuration: Parameters for the NER model.
    See conf/stanza.yaml for an example

    """

    engine_name = "stanza"
    is_available = bool(stanza)

    def __init__(
        self,
        models: Optional[List[Dict[str, str]]] = None,
        ner_model_configuration: Optional[NerModelConfiguration] = None,
        download_if_missing: bool = True,
    ):
        super().__init__(models, ner_model_configuration)
        self.download_if_missing = download_if_missing
        self.device = device_detector.get_device()

    def load(self) -> None:
        """Load the NLP model."""

        logger.debug(f"Loading Stanza models: {self.models}")

        # Enable GPU support using parent class method
        super()._enable_gpu()

        self.nlp = {}
        for model in self.models:
            self._validate_model_params(model)
            self.nlp[model["lang_code"]] = load_pipeline(
                model["model_name"],
                processors="tokenize,pos,lemma,ner",
                download_method="DOWNLOAD_RESOURCES"
                if self.download_if_missing
                else None,
                device=self.device,
            )

    def process_batch(
        self,
        texts: Union[List[str], List[Tuple[str, object]]],
        language: str,
        batch_size: int = 1,
        n_process: int = 1,
        as_tuples: bool = False,
    ) -> Generator[
        Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
    ]:
        """Execute the NLP pipeline on a batch of texts using Stanza's bulk processing.

        This method overrides SpacyNlpEngine.process_batch to leverage Stanza's
        efficient bulk_process method, which processes multiple documents together
        for better GPU utilization.

        Note: Stanza batches internally at the sentence/token level, not docs.
        For optimal GPU performance, use larger batch sizes (e.g., 16-32 docs).
        GPU utilization depends on total sentences/tokens across all docs in batch.

        :param texts: A list of texts to process. if as_tuples is set to True,
            texts should be a list of tuples (text, context).
        :param language: The language of the texts.
        :param batch_size: Number of documents per bulk_process call.
            Recommended: 16-32+ for GPU, lower values acceptable for CPU.
        :param n_process: Not used for Stanza (kept for API compatibility).
        :param as_tuples: If set to True, inputs should be a sequence of
            (text, context) tuples. Output will then be a sequence of
            (text, NlpArtifacts, context) tuples. Defaults to False.

        :return: A generator of tuples (text, NlpArtifacts, context) or
            (text, NlpArtifacts) depending on the value of as_tuples.
        """

        if not self.nlp:
            raise ValueError("NLP engine is not loaded. Consider calling .load()")

        # Get the StanzaTokenizer (which wraps the Stanza pipeline)
        # In spaCy, tokenizers are accessed via .tokenizer, not .get_pipe()
        stanza_tokenizer = self.nlp[language].tokenizer
        stanza_pipeline = stanza_tokenizer.snlp

        text_list = list(texts) if not isinstance(texts, list) else texts

        for batch_start in range(0, len(text_list), batch_size):
            batch_end = min(batch_start + batch_size, len(text_list))
            batch = text_list[batch_start:batch_end]

            if as_tuples:
                batch_texts = [str(text) for text, context in batch]
                contexts = [context for text, context in batch]
            else:
                batch_texts = [str(text) for text in batch]
                contexts = None

            # Create Stanza Document objects and process via bulk_process
            # Stanza handles internal batching at sentence/token level
            stanza_docs = [stanza.Document([], text=text) for text in batch_texts]
            processed_stanza_docs = stanza_pipeline.bulk_process(stanza_docs)

            # Convert processed Stanza docs to spaCy docs using spacy-stanza's logic
            # We call _convert_doc() which reuses StanzaTokenizer's conversion path
            for idx, processed_stanza_doc in enumerate(processed_stanza_docs):
                spacy_doc = stanza_tokenizer._convert_doc(processed_stanza_doc)
                nlp_artifacts = self._doc_to_nlp_artifact(spacy_doc, language)

                if as_tuples:
                    yield batch_texts[idx], nlp_artifacts, contexts[idx]
                else:
                    yield batch_texts[idx], nlp_artifacts

is_loaded

is_loaded() -> bool

Return True if the model is already loaded.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
115
116
117
def is_loaded(self) -> bool:
    """Return True if the model is already loaded."""
    return self.nlp is not None

process_text

process_text(text: str, language: str) -> NlpArtifacts

Execute the SpaCy NLP pipeline on the given text and language.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
119
120
121
122
123
124
125
def process_text(self, text: str, language: str) -> NlpArtifacts:
    """Execute the SpaCy NLP pipeline on the given text and language."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    doc = self.nlp[language](text)
    return self._doc_to_nlp_artifact(doc, language)

is_stopword

is_stopword(word: str, language: str) -> bool

Return true if the given word is a stop word.

(within the given language)

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
175
176
177
178
179
180
181
def is_stopword(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a stop word.

    (within the given language)
    """
    return self.nlp[language].vocab[word].is_stop

is_punct

is_punct(word: str, language: str) -> bool

Return true if the given word is a punctuation word.

(within the given language).

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
183
184
185
186
187
188
189
def is_punct(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a punctuation word.

    (within the given language).
    """
    return self.nlp[language].vocab[word].is_punct

get_supported_entities

get_supported_entities() -> List[str]

Return the supported entities for this NLP engine.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def get_supported_entities(self) -> List[str]:
    """Return the supported entities for this NLP engine."""
    if not self.ner_model_configuration.model_to_presidio_entity_mapping:
        raise ValueError(
            "model_to_presidio_entity_mapping is missing from model configuration"
        )
    entities_from_mapping = list(
        set(self.ner_model_configuration.model_to_presidio_entity_mapping.values())
    )
    entities = [
        ent
        for ent in entities_from_mapping
        if ent not in self.ner_model_configuration.labels_to_ignore
    ]
    return entities

get_supported_languages

get_supported_languages() -> List[str]

Return the supported languages for this NLP engine.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
109
110
111
112
113
def get_supported_languages(self) -> List[str]:
    """Return the supported languages for this NLP engine."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")
    return list(self.nlp.keys())

get_nlp

get_nlp(language: str) -> Language

Return the language model loaded for a language.

PARAMETER DESCRIPTION
language

Language

TYPE: str

RETURNS DESCRIPTION
Language

Model from spaCy

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
191
192
193
194
195
196
197
198
def get_nlp(self, language: str) -> Language:
    """
    Return the language model loaded for a language.

    :param language: Language
    :return: Model from spaCy
    """
    return self.nlp[language]

load

load() -> None

Load the NLP model.

Source code in presidio_analyzer/nlp_engine/stanza_nlp_engine.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def load(self) -> None:
    """Load the NLP model."""

    logger.debug(f"Loading Stanza models: {self.models}")

    # Enable GPU support using parent class method
    super()._enable_gpu()

    self.nlp = {}
    for model in self.models:
        self._validate_model_params(model)
        self.nlp[model["lang_code"]] = load_pipeline(
            model["model_name"],
            processors="tokenize,pos,lemma,ner",
            download_method="DOWNLOAD_RESOURCES"
            if self.download_if_missing
            else None,
            device=self.device,
        )

process_batch

process_batch(
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]

Execute the NLP pipeline on a batch of texts using Stanza's bulk processing.

This method overrides SpacyNlpEngine.process_batch to leverage Stanza's efficient bulk_process method, which processes multiple documents together for better GPU utilization.

Note: Stanza batches internally at the sentence/token level, not docs. For optimal GPU performance, use larger batch sizes (e.g., 16-32 docs). GPU utilization depends on total sentences/tokens across all docs in batch.

PARAMETER DESCRIPTION
texts

A list of texts to process. if as_tuples is set to True, texts should be a list of tuples (text, context).

TYPE: Union[List[str], List[Tuple[str, object]]]

language

The language of the texts.

TYPE: str

batch_size

Number of documents per bulk_process call. Recommended: 16-32+ for GPU, lower values acceptable for CPU.

TYPE: int DEFAULT: 1

n_process

Not used for Stanza (kept for API compatibility).

TYPE: int DEFAULT: 1

as_tuples

If set to True, inputs should be a sequence of (text, context) tuples. Output will then be a sequence of (text, NlpArtifacts, context) tuples. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Generator[Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None]

A generator of tuples (text, NlpArtifacts, context) or (text, NlpArtifacts) depending on the value of as_tuples.

Source code in presidio_analyzer/nlp_engine/stanza_nlp_engine.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def process_batch(
    self,
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]:
    """Execute the NLP pipeline on a batch of texts using Stanza's bulk processing.

    This method overrides SpacyNlpEngine.process_batch to leverage Stanza's
    efficient bulk_process method, which processes multiple documents together
    for better GPU utilization.

    Note: Stanza batches internally at the sentence/token level, not docs.
    For optimal GPU performance, use larger batch sizes (e.g., 16-32 docs).
    GPU utilization depends on total sentences/tokens across all docs in batch.

    :param texts: A list of texts to process. if as_tuples is set to True,
        texts should be a list of tuples (text, context).
    :param language: The language of the texts.
    :param batch_size: Number of documents per bulk_process call.
        Recommended: 16-32+ for GPU, lower values acceptable for CPU.
    :param n_process: Not used for Stanza (kept for API compatibility).
    :param as_tuples: If set to True, inputs should be a sequence of
        (text, context) tuples. Output will then be a sequence of
        (text, NlpArtifacts, context) tuples. Defaults to False.

    :return: A generator of tuples (text, NlpArtifacts, context) or
        (text, NlpArtifacts) depending on the value of as_tuples.
    """

    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    # Get the StanzaTokenizer (which wraps the Stanza pipeline)
    # In spaCy, tokenizers are accessed via .tokenizer, not .get_pipe()
    stanza_tokenizer = self.nlp[language].tokenizer
    stanza_pipeline = stanza_tokenizer.snlp

    text_list = list(texts) if not isinstance(texts, list) else texts

    for batch_start in range(0, len(text_list), batch_size):
        batch_end = min(batch_start + batch_size, len(text_list))
        batch = text_list[batch_start:batch_end]

        if as_tuples:
            batch_texts = [str(text) for text, context in batch]
            contexts = [context for text, context in batch]
        else:
            batch_texts = [str(text) for text in batch]
            contexts = None

        # Create Stanza Document objects and process via bulk_process
        # Stanza handles internal batching at sentence/token level
        stanza_docs = [stanza.Document([], text=text) for text in batch_texts]
        processed_stanza_docs = stanza_pipeline.bulk_process(stanza_docs)

        # Convert processed Stanza docs to spaCy docs using spacy-stanza's logic
        # We call _convert_doc() which reuses StanzaTokenizer's conversion path
        for idx, processed_stanza_doc in enumerate(processed_stanza_docs):
            spacy_doc = stanza_tokenizer._convert_doc(processed_stanza_doc)
            nlp_artifacts = self._doc_to_nlp_artifact(spacy_doc, language)

            if as_tuples:
                yield batch_texts[idx], nlp_artifacts, contexts[idx]
            else:
                yield batch_texts[idx], nlp_artifacts

TransformersNlpEngine

Bases: SpacyNlpEngine

TransformersNlpEngine is a transformers based NlpEngine.

It comprises a spacy pipeline used for tokenization, lemmatization, pos, and a transformers component for NER.

Both the underlying spacy pipeline and the transformers engine could be configured by the user. :example: [{"lang_code": "en", "model_name": { "spacy": "en_core_web_sm", "transformers": "dslim/bert-base-NER" } }]

PARAMETER DESCRIPTION
models

A dict holding the model's configuration.

TYPE: Optional[List[Dict]] DEFAULT: None

ner_model_configuration

Parameters for the NER model. See conf/transformers.yaml for an example

Note that since the spaCy model is not used for NER, we recommend using a simple model, such as en_core_web_sm for English. For potential Transformers models, see a list of models here: https://huggingface.co/models?pipeline_tag=token-classification It is further recommended to fine-tune these models to the specific scenario in hand.

TYPE: Optional[NerModelConfiguration] DEFAULT: None

METHOD DESCRIPTION
is_loaded

Return True if the model is already loaded.

process_text

Execute the SpaCy NLP pipeline on the given text and language.

process_batch

Execute the NLP pipeline on a batch of texts using spacy pipe.

is_stopword

Return true if the given word is a stop word.

is_punct

Return true if the given word is a punctuation word.

get_supported_entities

Return the supported entities for this NLP engine.

get_supported_languages

Return the supported languages for this NLP engine.

get_nlp

Return the language model loaded for a language.

load

Load the spaCy and transformers models.

Source code in presidio_analyzer/nlp_engine/transformers_nlp_engine.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class TransformersNlpEngine(SpacyNlpEngine):
    """

    TransformersNlpEngine is a transformers based NlpEngine.

    It comprises a spacy pipeline used for tokenization,
    lemmatization, pos, and a transformers component for NER.

    Both the underlying spacy pipeline and the transformers engine could be
    configured by the user.
    :param models: A dict holding the model's configuration.
    :example:
    [{"lang_code": "en", "model_name": {
            "spacy": "en_core_web_sm",
            "transformers": "dslim/bert-base-NER"
            }
    }]
    :param ner_model_configuration: Parameters for the NER model.
    See conf/transformers.yaml for an example


    Note that since the spaCy model is not used for NER,
    we recommend using a simple model, such as en_core_web_sm for English.
    For potential Transformers models, see a list of models here:
    https://huggingface.co/models?pipeline_tag=token-classification
    It is further recommended to fine-tune these models
    to the specific scenario in hand.

    """

    engine_name = "transformers"
    is_available = bool(spacy_huggingface_pipelines)

    def __init__(
        self,
        models: Optional[List[Dict]] = None,
        ner_model_configuration: Optional[NerModelConfiguration] = None,
    ):
        if not models:
            models = [
                {
                    "lang_code": "en",
                    "model_name": {
                        "spacy": "en_core_web_sm",
                        "transformers": "obi/deid_roberta_i2b2",
                    },
                }
            ]
        super().__init__(models=models, ner_model_configuration=ner_model_configuration)
        self.entity_key = "bert-base-ner"

    def load(self) -> None:
        """Load the spaCy and transformers models."""

        logger.debug(f"Loading SpaCy and transformers models: {self.models}")

        super()._enable_gpu()

        self.nlp = {}

        for model in self.models:
            self._validate_model_params(model)
            spacy_model = model["model_name"]["spacy"]
            transformers_model = model["model_name"]["transformers"]
            self._download_spacy_model_if_needed(spacy_model)

            nlp = spacy.load(spacy_model, disable=["parser", "ner"])

            pipe_config = {
                "model": transformers_model,
                "annotate": "spans",
                "stride": self.ner_model_configuration.stride,
                "alignment_mode": self.ner_model_configuration.alignment_mode,
                "aggregation_strategy": self.ner_model_configuration.aggregation_strategy,  # noqa: E501
                "annotate_spans_key": self.entity_key,
            }

            nlp.add_pipe("hf_token_pipe", config=pipe_config)
            self.nlp[model["lang_code"]] = nlp

    @staticmethod
    def _validate_model_params(model: Dict) -> None:
        if "lang_code" not in model:
            raise ValueError("lang_code is missing from model configuration")
        if "model_name" not in model:
            raise ValueError("model_name is missing from model configuration")
        if not isinstance(model["model_name"], dict):
            raise ValueError("model_name must be a dictionary")
        if "spacy" not in model["model_name"]:
            raise ValueError("spacy model name is missing from model configuration")
        if "transformers" not in model["model_name"]:
            raise ValueError(
                "transformers model name is missing from model configuration"
            )

    def _get_entities(self, doc: Doc) -> List[Span]:
        """
        Extract entities out of a spaCy pipeline, depending on the type of pipeline.

        For spacy-huggingface-pipeline, this would be doc.spans[key]
        :param doc: the output spaCy doc.
        :return: List of entities
        """

        return doc.spans[self.entity_key]

    def _get_scores_for_entities(self, doc: Doc) -> List[float]:
        """Extract scores for entities from the doc.

        While spaCy does not provide confidence scores,
        the spacy-huggingface-pipeline flow adds confidence scores
        as SpanGroup attributes.
        :param doc: SpaCy doc
        """

        return [float(score) for score in doc.spans[self.entity_key].attrs["scores"]]

is_loaded

is_loaded() -> bool

Return True if the model is already loaded.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
115
116
117
def is_loaded(self) -> bool:
    """Return True if the model is already loaded."""
    return self.nlp is not None

process_text

process_text(text: str, language: str) -> NlpArtifacts

Execute the SpaCy NLP pipeline on the given text and language.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
119
120
121
122
123
124
125
def process_text(self, text: str, language: str) -> NlpArtifacts:
    """Execute the SpaCy NLP pipeline on the given text and language."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    doc = self.nlp[language](text)
    return self._doc_to_nlp_artifact(doc, language)

process_batch

process_batch(
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]

Execute the NLP pipeline on a batch of texts using spacy pipe.

PARAMETER DESCRIPTION
texts

A list of texts to process. if as_tuples is set to True, texts should be a list of tuples (text, context).

TYPE: Union[List[str], List[Tuple[str, object]]]

language

The language of the texts.

TYPE: str

batch_size

Default batch size for pipe and evaluate.

TYPE: int DEFAULT: 1

n_process

Number of processors to process texts.

TYPE: int DEFAULT: 1

as_tuples

If set to True, inputs should be a sequence of (text, context) tuples. Output will then be a sequence of (doc, context) tuples. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Generator[Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None]

A generator of tuples (text, NlpArtifacts, context) or (text, NlpArtifacts) depending on the value of as_tuples.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def process_batch(
    self,
    texts: Union[List[str], List[Tuple[str, object]]],
    language: str,
    batch_size: int = 1,
    n_process: int = 1,
    as_tuples: bool = False,
) -> Generator[
    Union[Tuple[Any, NlpArtifacts, Any], Tuple[Any, NlpArtifacts]], Any, None
]:
    """Execute the NLP pipeline on a batch of texts using spacy pipe.

    :param texts: A list of texts to process. if as_tuples is set to True,
        texts should be a list of tuples (text, context).
    :param language: The language of the texts.
    :param batch_size: Default batch size for pipe and evaluate.
    :param n_process: Number of processors to process texts.
    :param as_tuples: If set to True, inputs should be a sequence of
        (text, context) tuples. Output will then be a sequence of
        (doc, context) tuples. Defaults to False.

    :return: A generator of tuples (text, NlpArtifacts, context) or
        (text, NlpArtifacts) depending on the value of as_tuples.
    """

    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")

    if as_tuples:
        if not all(isinstance(item, tuple) and len(item) == 2 for item in texts):
            raise ValueError(
                "When 'as_tuples' is True, "
                "'texts' must be a list of tuples (text, context)."
            )
        texts = ((str(text), context) for text, context in texts)
    else:
        texts = (str(text) for text in texts)
    batch_output = self.nlp[language].pipe(
        texts, as_tuples=as_tuples, batch_size=batch_size, n_process=n_process
    )
    for output in batch_output:
        if as_tuples:
            doc, context = output
            yield doc.text, self._doc_to_nlp_artifact(doc, language), context
        else:
            doc = output
            yield doc.text, self._doc_to_nlp_artifact(doc, language)

is_stopword

is_stopword(word: str, language: str) -> bool

Return true if the given word is a stop word.

(within the given language)

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
175
176
177
178
179
180
181
def is_stopword(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a stop word.

    (within the given language)
    """
    return self.nlp[language].vocab[word].is_stop

is_punct

is_punct(word: str, language: str) -> bool

Return true if the given word is a punctuation word.

(within the given language).

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
183
184
185
186
187
188
189
def is_punct(self, word: str, language: str) -> bool:
    """
    Return true if the given word is a punctuation word.

    (within the given language).
    """
    return self.nlp[language].vocab[word].is_punct

get_supported_entities

get_supported_entities() -> List[str]

Return the supported entities for this NLP engine.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def get_supported_entities(self) -> List[str]:
    """Return the supported entities for this NLP engine."""
    if not self.ner_model_configuration.model_to_presidio_entity_mapping:
        raise ValueError(
            "model_to_presidio_entity_mapping is missing from model configuration"
        )
    entities_from_mapping = list(
        set(self.ner_model_configuration.model_to_presidio_entity_mapping.values())
    )
    entities = [
        ent
        for ent in entities_from_mapping
        if ent not in self.ner_model_configuration.labels_to_ignore
    ]
    return entities

get_supported_languages

get_supported_languages() -> List[str]

Return the supported languages for this NLP engine.

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
109
110
111
112
113
def get_supported_languages(self) -> List[str]:
    """Return the supported languages for this NLP engine."""
    if not self.nlp:
        raise ValueError("NLP engine is not loaded. Consider calling .load()")
    return list(self.nlp.keys())

get_nlp

get_nlp(language: str) -> Language

Return the language model loaded for a language.

PARAMETER DESCRIPTION
language

Language

TYPE: str

RETURNS DESCRIPTION
Language

Model from spaCy

Source code in presidio_analyzer/nlp_engine/spacy_nlp_engine.py
191
192
193
194
195
196
197
198
def get_nlp(self, language: str) -> Language:
    """
    Return the language model loaded for a language.

    :param language: Language
    :return: Model from spaCy
    """
    return self.nlp[language]

load

load() -> None

Load the spaCy and transformers models.

Source code in presidio_analyzer/nlp_engine/transformers_nlp_engine.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def load(self) -> None:
    """Load the spaCy and transformers models."""

    logger.debug(f"Loading SpaCy and transformers models: {self.models}")

    super()._enable_gpu()

    self.nlp = {}

    for model in self.models:
        self._validate_model_params(model)
        spacy_model = model["model_name"]["spacy"]
        transformers_model = model["model_name"]["transformers"]
        self._download_spacy_model_if_needed(spacy_model)

        nlp = spacy.load(spacy_model, disable=["parser", "ner"])

        pipe_config = {
            "model": transformers_model,
            "annotate": "spans",
            "stride": self.ner_model_configuration.stride,
            "alignment_mode": self.ner_model_configuration.alignment_mode,
            "aggregation_strategy": self.ner_model_configuration.aggregation_strategy,  # noqa: E501
            "annotate_spans_key": self.entity_key,
        }

        nlp.add_pipe("hf_token_pipe", config=pipe_config)
        self.nlp[model["lang_code"]] = nlp

NlpEngineProvider

Create different NLP engines from configuration.

:example: configuration: { "nlp_engine_name": "spacy", "models": [{"lang_code": "en", "model_name": "en_core_web_lg" }] } Nlp engine names available by default: spacy, stanza.

PARAMETER DESCRIPTION
nlp_engines

List of available NLP engines Default: (SpacyNlpEngine, StanzaNlpEngine)

TYPE: Optional[Tuple] DEFAULT: None

nlp_configuration

Dict containing nlp configuration

TYPE: Optional[Dict] DEFAULT: None

conf_file

Path to yaml file containing nlp engine configuration.

TYPE: Optional[Union[Path, str]] DEFAULT: None

METHOD DESCRIPTION
create_engine

Create an NLP engine instance.

Source code in presidio_analyzer/nlp_engine/nlp_engine_provider.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class NlpEngineProvider:
    """Create different NLP engines from configuration.

    :param nlp_engines: List of available NLP engines
    Default: (SpacyNlpEngine, StanzaNlpEngine)
    :param nlp_configuration: Dict containing nlp configuration
    :example: configuration:
            {
                "nlp_engine_name": "spacy",
                "models": [{"lang_code": "en",
                            "model_name": "en_core_web_lg"
                          }]
            }
    Nlp engine names available by default: spacy, stanza.
    :param conf_file: Path to yaml file containing nlp engine configuration.
    """

    def __init__(
        self,
        nlp_engines: Optional[Tuple] = None,
        conf_file: Optional[Union[Path, str]] = None,
        nlp_configuration: Optional[Dict] = None,
    ):
        if nlp_engines is None:
            nlp_engines = (
                SpacyNlpEngine,
                StanzaNlpEngine,
                TransformersNlpEngine,
                SlimSpacyNlpEngine,
            )

        self.nlp_engines = {
            engine.engine_name: engine for engine in nlp_engines if engine.is_available
        }
        logger.debug(
            f"Loaded these available nlp engines: {list(self.nlp_engines.keys())}"
        )

        if conf_file and nlp_configuration:
            raise ValueError(
                "Either conf_file or nlp_configuration should be provided, not both."
            )

        if nlp_configuration:
            ConfigurationValidator.validate_nlp_configuration(nlp_configuration)
            self.nlp_configuration = nlp_configuration

        if conf_file or conf_file == "":
            if conf_file == "":
                raise ValueError("conf_file is empty")
            ConfigurationValidator.validate_file_path(conf_file)
            self.nlp_configuration = self._read_nlp_conf(conf_file)

        if conf_file is None and nlp_configuration is None:
            conf_file = self._get_full_conf_path()
            logger.debug(f"Reading default conf file from {conf_file}")
            self.nlp_configuration = self._read_nlp_conf(conf_file)
            ConfigurationValidator.validate_nlp_configuration(self.nlp_configuration)

    @staticmethod
    def _read_nlp_conf(conf_file: Union[Path, str]) -> Dict:
        """Read NLP configuration from a YAML file."""
        with open(conf_file) as file:
            return yaml.safe_load(file)

    @staticmethod
    def _get_full_conf_path(
        default_conf_file: Union[Path, str] = "default.yaml",
    ) -> Path:
        """Return a Path to the default conf file."""
        return Path(Path(__file__).parent, "../conf", default_conf_file)

    def create_engine(self) -> NlpEngine:
        """Create an NLP engine instance."""
        # Configuration is already validated by Pydantic in __init__
        nlp_engine_name = self.nlp_configuration["nlp_engine_name"]
        if nlp_engine_name not in self.nlp_engines:
            raise ValueError(
                f"NLP engine '{nlp_engine_name}' is not available. "
                "Make sure you have all required packages installed"
            )

        nlp_engine_class = self.nlp_engines[nlp_engine_name]
        nlp_models = self.nlp_configuration["models"]

        if nlp_engine_name == SlimSpacyNlpEngine.engine_name:
            generic_tokenizer = self.nlp_configuration.get("generic_tokenizer")
            engine = nlp_engine_class(
                models=nlp_models, generic_tokenizer=generic_tokenizer
            )
        else:
            ner_model_configuration = self.nlp_configuration.get(
                "ner_model_configuration"
            )
            if ner_model_configuration:
                ner_model_configuration = NerModelConfiguration.from_dict(
                    ner_model_configuration
                )

            engine = nlp_engine_class(
                models=nlp_models, ner_model_configuration=ner_model_configuration
            )

        engine.load()
        logger.info(
            f"Created NLP engine: {engine.engine_name}. "
            f"Loaded models: {list(engine.nlp.keys())}"
        )
        return engine

create_engine

create_engine() -> NlpEngine

Create an NLP engine instance.

Source code in presidio_analyzer/nlp_engine/nlp_engine_provider.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def create_engine(self) -> NlpEngine:
    """Create an NLP engine instance."""
    # Configuration is already validated by Pydantic in __init__
    nlp_engine_name = self.nlp_configuration["nlp_engine_name"]
    if nlp_engine_name not in self.nlp_engines:
        raise ValueError(
            f"NLP engine '{nlp_engine_name}' is not available. "
            "Make sure you have all required packages installed"
        )

    nlp_engine_class = self.nlp_engines[nlp_engine_name]
    nlp_models = self.nlp_configuration["models"]

    if nlp_engine_name == SlimSpacyNlpEngine.engine_name:
        generic_tokenizer = self.nlp_configuration.get("generic_tokenizer")
        engine = nlp_engine_class(
            models=nlp_models, generic_tokenizer=generic_tokenizer
        )
    else:
        ner_model_configuration = self.nlp_configuration.get(
            "ner_model_configuration"
        )
        if ner_model_configuration:
            ner_model_configuration = NerModelConfiguration.from_dict(
                ner_model_configuration
            )

        engine = nlp_engine_class(
            models=nlp_models, ner_model_configuration=ner_model_configuration
        )

    engine.load()
    logger.info(
        f"Created NLP engine: {engine.engine_name}. "
        f"Loaded models: {list(engine.nlp.keys())}"
    )
    return engine

Predefined Recognizers

presidio_analyzer.predefined_recognizers

Predefined recognizers package. Holds all the default recognizers.

TransformersRecognizer

Bases: SpacyRecognizer

Recognize entities using the spacy-huggingface-pipeline package.

The recognizer doesn't run transformers models, but loads the output from the NlpArtifacts See: - https://huggingface.co/docs/transformers/main/en/index for transformer models - https://github.com/explosion/spacy-huggingface-pipelines on the spaCy wrapper to transformers

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

build_explanation

Create explanation for why this result was detected.

Source code in presidio_analyzer/predefined_recognizers/nlp_engine_recognizers/transformers_recognizer.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class TransformersRecognizer(SpacyRecognizer):
    """
    Recognize entities using the spacy-huggingface-pipeline package.

    The recognizer doesn't run transformers models,
    but loads the output from the NlpArtifacts
    See:
     - https://huggingface.co/docs/transformers/main/en/index for transformer models
     - https://github.com/explosion/spacy-huggingface-pipelines on the spaCy wrapper to transformers
    """  # noqa: E501

    ENTITIES = [
        "PERSON",
        "LOCATION",
        "ORGANIZATION",
        "AGE",
        "ID",
        "EMAIL",
        "DATE_TIME",
        "PHONE_NUMBER",
    ]

    def __init__(self, name: Optional[str] = None, **kwargs):
        self.DEFAULT_EXPLANATION = self.DEFAULT_EXPLANATION.replace(
            "Spacy", "Transformers"
        )
        super().__init__(name=name, **kwargs)

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

build_explanation

build_explanation(
    original_score: float, explanation: str
) -> AnalysisExplanation

Create explanation for why this result was detected.

PARAMETER DESCRIPTION
original_score

Score given by this recognizer

TYPE: float

explanation

Explanation string

TYPE: str

RETURNS DESCRIPTION
AnalysisExplanation
Source code in presidio_analyzer/predefined_recognizers/nlp_engine_recognizers/spacy_recognizer.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def build_explanation(
    self, original_score: float, explanation: str
) -> AnalysisExplanation:
    """
    Create explanation for why this result was detected.

    :param original_score: Score given by this recognizer
    :param explanation: Explanation string
    :return:
    """
    explanation = AnalysisExplanation(
        recognizer=self.name,
        original_score=original_score,
        textual_explanation=explanation,
    )
    return explanation

AuAbnRecognizer

Bases: PatternRecognizer

Recognizes Australian Business Number ("ABN").

The Australian Business Number (ABN) is a unique 11 digit identifier issued to all entities registered in the Australian Business Register (ABR). The 11 digit ABN is structured as a 9 digit identifier with two leading check digits. The leading check digits are derived using a modulus 89 calculation. This recognizer identifies ABN using regex, context words and checksum. Reference: https://abr.business.gov.au/Help/AbnFormat

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'AU_ABN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_abn_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class AuAbnRecognizer(PatternRecognizer):
    """
    Recognizes Australian Business Number ("ABN").

    The Australian Business Number (ABN) is a unique 11
    digit identifier issued to all entities registered in
    the Australian Business Register (ABR).
    The 11 digit ABN is structured as a 9 digit identifier
    with two leading check digits.
    The leading check digits are derived using a modulus 89 calculation.
    This recognizer identifies ABN using regex, context words and checksum.
    Reference: https://abr.business.gov.au/Help/AbnFormat

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "au"

    PATTERNS = [
        Pattern(
            "ABN (Medium)",
            r"\b\d{2}\s\d{3}\s\d{3}\s\d{3}\b",
            0.1,
        ),
        Pattern(
            "ABN (Low)",
            r"\b\d{11}\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "australian business number",
        "abn",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "AU_ABN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """
        # Pre-processing before validation checks
        text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
        abn_list = [int(digit) for digit in text if not digit.isspace()]

        # Set weights based on digit position
        weight = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

        # Perform checksums
        abn_list[0] = 9 if abn_list[0] == 0 else abn_list[0] - 1
        sum_product = 0
        for i in range(11):
            sum_product += abn_list[i] * weight[i]
        remainder = sum_product % 89
        return remainder == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
bool

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_abn_recognizer.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    # Pre-processing before validation checks
    text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
    abn_list = [int(digit) for digit in text if not digit.isspace()]

    # Set weights based on digit position
    weight = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

    # Perform checksums
    abn_list[0] = 9 if abn_list[0] == 0 else abn_list[0] - 1
    sum_product = 0
    for i in range(11):
        sum_product += abn_list[i] * weight[i]
    remainder = sum_product % 89
    return remainder == 0

AuAcnRecognizer

Bases: PatternRecognizer

Recognizes Australian Company Number ("ACN").

The Australian Company Number (ACN) is a nine digit number with the last digit being a check digit calculated using a modified modulus 10 calculation. This recognizer identifies ACN using regex, context words, and checksum. Reference: https://asic.gov.au/

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'AU_ACN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_acn_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class AuAcnRecognizer(PatternRecognizer):
    """
    Recognizes Australian Company Number ("ACN").

    The Australian Company Number (ACN) is a nine digit number
    with the last digit being a check digit calculated using a
    modified modulus 10 calculation.
    This recognizer identifies ACN using regex, context words, and checksum.
    Reference: https://asic.gov.au/

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "au"

    PATTERNS = [
        Pattern(
            "ACN (Medium)",
            r"\b\d{3}\s\d{3}\s\d{3}\b",
            0.1,
        ),
        Pattern(
            "ACN (Low)",
            r"\b\d{9}\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "australian company number",
        "acn",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "AU_ACN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """
        # Pre-processing before validation checks
        text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
        acn_list = [int(digit) for digit in text if not digit.isspace()]

        # Set weights based on digit position
        weight = [8, 7, 6, 5, 4, 3, 2, 1]

        # Perform checksums
        sum_product = 0
        for i in range(8):
            sum_product += acn_list[i] * weight[i]
        remainder = sum_product % 10
        # The complement must be reduced mod 10: when the weighted sum is a
        # multiple of 10 the check digit is 0, not 10. Without the outer "% 10"
        # every valid ACN whose check digit is 0 (e.g. 000000180) is rejected.
        complement = (10 - remainder) % 10
        return complement == acn_list[-1]

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
bool

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_acn_recognizer.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    # Pre-processing before validation checks
    text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
    acn_list = [int(digit) for digit in text if not digit.isspace()]

    # Set weights based on digit position
    weight = [8, 7, 6, 5, 4, 3, 2, 1]

    # Perform checksums
    sum_product = 0
    for i in range(8):
        sum_product += acn_list[i] * weight[i]
    remainder = sum_product % 10
    # The complement must be reduced mod 10: when the weighted sum is a
    # multiple of 10 the check digit is 0, not 10. Without the outer "% 10"
    # every valid ACN whose check digit is 0 (e.g. 000000180) is rejected.
    complement = (10 - remainder) % 10
    return complement == acn_list[-1]

AuMedicareRecognizer

Bases: PatternRecognizer

Recognizes Australian Medicare number using regex, context words, and checksum.

Medicare number is a unique identifier issued by Australian Government that enables the cardholder to receive a rebates of medical expenses under Australia's Medicare system. It uses a modulus 10 checksum scheme to validate the number. Reference: https://en.wikipedia.org/wiki/Medicare_card_(Australia)

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'AU_MEDICARE'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_medicare_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class AuMedicareRecognizer(PatternRecognizer):
    """
    Recognizes Australian Medicare number using regex, context words, and checksum.

    Medicare number is a unique identifier issued by Australian Government
    that enables the cardholder to receive a rebates of medical expenses
    under Australia's Medicare system.
    It uses a modulus 10 checksum scheme to validate the number.
    Reference: https://en.wikipedia.org/wiki/Medicare_card_(Australia)


    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "au"

    PATTERNS = [
        Pattern(
            "Australian Medicare Number (Medium)",
            r"\b[2-6]\d{3}\s\d{5}\s\d\b",
            0.1,
        ),
        Pattern(
            "Australian Medicare Number (Low)",
            r"\b[2-6]\d{9}\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "medicare",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "AU_MEDICARE",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """
        # Pre-processing before validation checks
        text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
        medicare_list = [int(digit) for digit in text if not digit.isspace()]

        # Set weights based on digit position
        weight = [1, 3, 7, 9, 1, 3, 7, 9]

        # Perform checksums
        sum_product = 0
        for i in range(8):
            sum_product += medicare_list[i] * weight[i]
        remainder = sum_product % 10
        return remainder == medicare_list[8]

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
bool

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_medicare_recognizer.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    # Pre-processing before validation checks
    text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
    medicare_list = [int(digit) for digit in text if not digit.isspace()]

    # Set weights based on digit position
    weight = [1, 3, 7, 9, 1, 3, 7, 9]

    # Perform checksums
    sum_product = 0
    for i in range(8):
        sum_product += medicare_list[i] * weight[i]
    remainder = sum_product % 10
    return remainder == medicare_list[8]

AuTfnRecognizer

Bases: PatternRecognizer

Recognizes Australian Tax File Numbers ("TFN").

The tax file number (TFN) is a unique identifier issued by the Australian Taxation Office to each taxpaying entity — an individual, company, superannuation fund, partnership, or trust. The TFN consists of a nine digit number, usually presented in the format NNN NNN NNN. TFN includes a check digit for detecting erroneous number based on simple modulo 11. This recognizer uses regex, context words, and checksum to identify TFN. Reference: https://www.ato.gov.au/individuals/tax-file-number/

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'AU_TFN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_tfn_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
class AuTfnRecognizer(PatternRecognizer):
    """
    Recognizes Australian Tax File Numbers ("TFN").

    The tax file number (TFN) is a unique identifier
    issued by the Australian Taxation Office
    to each taxpaying entity — an individual, company,
    superannuation fund, partnership, or trust.
    The TFN consists of a nine digit number, usually
    presented in the format NNN NNN NNN.
    TFN includes a check digit for detecting erroneous
    number based on simple modulo 11.
    This recognizer uses regex, context words,
    and checksum to identify TFN.
    Reference: https://www.ato.gov.au/individuals/tax-file-number/

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "au"

    PATTERNS = [
        Pattern(
            "TFN (Medium)",
            r"\b\d{3}\s\d{3}\s\d{3}\b",
            0.1,
        ),
        Pattern(
            "TFN (Low)",
            r"\b\d{9}\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "tax file number",
        "tfn",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "AU_TFN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """
        # Pre-processing before validation checks
        text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
        tfn_list = [int(digit) for digit in text if not digit.isspace()]

        # Set weights based on digit position
        weight = [1, 4, 3, 7, 5, 8, 6, 9, 10]

        # Perform checksums
        sum_product = 0
        for i in range(9):
            sum_product += tfn_list[i] * weight[i]
        remainder = sum_product % 11
        return remainder == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
bool

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/australia/au_tfn_recognizer.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    # Pre-processing before validation checks
    text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
    tfn_list = [int(digit) for digit in text if not digit.isspace()]

    # Set weights based on digit position
    weight = [1, 4, 3, 7, 5, 8, 6, 9, 10]

    # Perform checksums
    sum_product = 0
    for i in range(9):
        sum_product += tfn_list[i] * weight[i]
    remainder = sum_product % 11
    return remainder == 0

CaSinRecognizer

Bases: PatternRecognizer

Recognize Canadian Social Insurance Number (SIN) using regex + Luhn checksum.

A SIN is a 9-digit number issued by Employment and Social Development Canada (ESDC) to administer various government programs. The last digit is a Luhn check digit computed over the first 8 digits. SINs beginning with 0 or 8 are reserved and not currently issued to individuals.

Format: DDD DDD DDD or DDD-DDD-DDD or DDDDDDDDD First digit valid range: 1-7, 9

Reference: https://www.canada.ca/en/employment-social-development/services/sin.html

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'CA_SIN'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

build_regex_explanation

Construct an explanation for why this entity was detected.

invalidate_result

Check if the pattern text cannot be validated as a CA_SIN entity.

Source code in presidio_analyzer/predefined_recognizers/country_specific/canada/ca_sin_recognizer.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class CaSinRecognizer(PatternRecognizer):
    """Recognize Canadian Social Insurance Number (SIN) using regex + Luhn checksum.

    A SIN is a 9-digit number issued by Employment and Social Development Canada
    (ESDC) to administer various government programs. The last digit is a Luhn
    check digit computed over the first 8 digits. SINs beginning with 0 or 8 are
    reserved and not currently issued to individuals.

    Format: DDD DDD DDD or DDD-DDD-DDD or DDDDDDDDD
    First digit valid range: 1-7, 9

    Reference: https://www.canada.ca/en/employment-social-development/services/sin.html

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "ca"

    PATTERNS = [
        Pattern("SIN (weak)", r"\b[1-79]\d{8}\b", 0.05),
        Pattern("SIN (medium)", r"\b[1-79]\d{2}([- ])\d{3}\1\d{3}\b", 0.5),
    ]

    CONTEXT = [
        "sin",
        "sin number",
        "social insurance",
        "social insurance number",
        "canada",
        # French equivalents
        "nas",
        "numéro nas",
        "numéro d'assurance sociale",
        "assurance sociale",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "CA_SIN",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def invalidate_result(self, pattern_text: str) -> bool:
        """
        Check if the pattern text cannot be validated as a CA_SIN entity.

        :param pattern_text: Text detected as pattern by regex
        :return: True if invalidated
        """
        only_digits = "".join(c for c in pattern_text if c.isdigit())
        return not self._luhn_valid(only_digits)

    @staticmethod
    def _luhn_valid(digits: str) -> bool:
        """Validate using the Luhn checksum."""
        total = 0
        for i, digit in enumerate(reversed(digits)):
            n = int(digit)
            if i % 2 == 1:
                n *= 2
                if n > 9:
                    n -= 9
            total += n
        return total % 10 == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

invalidate_result

invalidate_result(pattern_text: str) -> bool

Check if the pattern text cannot be validated as a CA_SIN entity.

PARAMETER DESCRIPTION
pattern_text

Text detected as pattern by regex

TYPE: str

RETURNS DESCRIPTION
bool

True if invalidated

Source code in presidio_analyzer/predefined_recognizers/country_specific/canada/ca_sin_recognizer.py
65
66
67
68
69
70
71
72
73
def invalidate_result(self, pattern_text: str) -> bool:
    """
    Check if the pattern text cannot be validated as a CA_SIN entity.

    :param pattern_text: Text detected as pattern by regex
    :return: True if invalidated
    """
    only_digits = "".join(c for c in pattern_text if c.isdigit())
    return not self._luhn_valid(only_digits)

FiPersonalIdentityCodeRecognizer

Bases: PatternRecognizer

Recognizes and validates Finnish Personal Identity Codes (Henkilötunnus).

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'fi'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'FI_PERSONAL_IDENTITY_CODE'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern by using the control character.

Source code in presidio_analyzer/predefined_recognizers/country_specific/finland/fi_personal_identity_code_recognizer.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class FiPersonalIdentityCodeRecognizer(PatternRecognizer):
    """
    Recognizes and validates Finnish Personal Identity Codes (Henkilötunnus).

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "fi"

    PATTERNS = [
        Pattern(
            "Finnish Personal Identity Code (Medium)",
            r"\b(\d{6})([+-ABCDEFYXWVU])(\d{3})([0123456789ABCDEFHJKLMNPRSTUVWXY])\b",
            0.5,
        ),
        Pattern(
            "Finnish Personal Identity Code (Very Weak)",
            r"(\d{6})([+-ABCDEFYXWVU])(\d{3})([0123456789ABCDEFHJKLMNPRSTUVWXY])",
            0.1,
        ),
    ]
    CONTEXT = ["hetu", "henkilötunnus", "personbeteckningen", "personal identity code"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "fi",
        supported_entity: str = "FI_PERSONAL_IDENTITY_CODE",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """Validate the pattern by using the control character."""

        # More information on the validation logic from:
        # https://dvv.fi/en/personal-identity-code
        # Under "How is the control character for a personal identity code calculated?".
        if len(pattern_text) != 11:
            return False

        date_part = pattern_text[0:6]
        try:
            # Checking if we do not have invalid dates e.g. 310211.
            datetime.strptime(date_part, "%d%m%y")
        except ValueError:
            return False
        individual_number = pattern_text[7:10]
        control_character = pattern_text[-1]
        valid_control_characters = "0123456789ABCDEFHJKLMNPRSTUVWXY"
        number_to_check = int(date_part + individual_number)
        return valid_control_characters[number_to_check % 31] == control_character

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern by using the control character.

Source code in presidio_analyzer/predefined_recognizers/country_specific/finland/fi_personal_identity_code_recognizer.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """Validate the pattern by using the control character."""

    # More information on the validation logic from:
    # https://dvv.fi/en/personal-identity-code
    # Under "How is the control character for a personal identity code calculated?".
    if len(pattern_text) != 11:
        return False

    date_part = pattern_text[0:6]
    try:
        # Checking if we do not have invalid dates e.g. 310211.
        datetime.strptime(date_part, "%d%m%y")
    except ValueError:
        return False
    individual_number = pattern_text[7:10]
    control_character = pattern_text[-1]
    valid_control_characters = "0123456789ABCDEFHJKLMNPRSTUVWXY"
    number_to_check = int(date_part + individual_number)
    return valid_control_characters[number_to_check % 31] == control_character

DeBsnrRecognizer

Bases: PatternRecognizer

Recognizes German Betriebsstättennummer (BSNR).

The BSNR is a 9-digit practice / site-of-care number assigned by the regional Kassenärztliche Vereinigung (KV) to each approved practice location (Betriebsstätte) participating in the German statutory health insurance system. It appears in billing records, treatment documents, and statutory healthcare communications and reveals the treating facility, making it sensitive under DSGVO.

Legal basis: § 75 Abs. 7 SGB V (Sozialgesetzbuch Fünftes Buch). Standard: KBV-Richtlinie nach § 75 Abs. 7 SGB V zur Vergabe der Arzt-, Betriebsstätten-, Praxisnetz- sowie der Netzverbundnummern. Data protection: DSGVO Art. 9 (Gesundheitsdaten), BDSG § 22.

Format (9 digits): Pos 1–2: KV-Bereichskennzeichen (regional KV code, e.g. 02 Hamburg, 38 Nordrhein, 72 Berlin; see VALID_KV_CODES below for the full whitelist per KBV Arztnummern-Richtlinie Anlage 1) Pos 3–9: Laufende Nummer (sequential number assigned by KV)

Examples (fictitious): 021234568, 381789045, 721234567

Accuracy note: The BSNR has no public Prüfziffer algorithm, so validate_result cannot give positive evidence of a real BSNR; it can only drop clearly invalid inputs (wrong length, non-digit, all-zero). All structurally-plausible 9-digit inputs therefore return None from validate_result: the match keeps its base pattern score (0.2) and the ContextAwareEnhancer drives final confidence via context words ("Betriebsstättennummer", "BSNR", "Praxis", …).

VALID_KV_CODES below lists the 2-digit regional codes documented in KBV Arztnummern-Richtlinie Anlage 1. It is retained for reference and future opt-in strict validation but is intentionally NOT used to upgrade whitelisted-prefix matches to MAX_SCORE — the \b\d{9}\b pattern is too broad to justify that upgrade on a 2-digit-prefix check alone.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_BSNR'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the BSNR structurally.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_bsnr_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
class DeBsnrRecognizer(PatternRecognizer):
    r"""
    Recognizes German Betriebsstättennummer (BSNR).

    The BSNR is a 9-digit practice / site-of-care number assigned by the
    regional Kassenärztliche Vereinigung (KV) to each approved practice
    location (Betriebsstätte) participating in the German statutory health
    insurance system.  It appears in billing records, treatment documents, and
    statutory healthcare communications and reveals the treating facility,
    making it sensitive under DSGVO.

    Legal basis: § 75 Abs. 7 SGB V (Sozialgesetzbuch Fünftes Buch).
    Standard: KBV-Richtlinie nach § 75 Abs. 7 SGB V zur Vergabe der Arzt-,
    Betriebsstätten-, Praxisnetz- sowie der Netzverbundnummern.
    Data protection: DSGVO Art. 9 (Gesundheitsdaten), BDSG § 22.

    Format (9 digits):
        Pos 1–2:  KV-Bereichskennzeichen (regional KV code, e.g. 02 Hamburg,
                  38 Nordrhein, 72 Berlin; see VALID_KV_CODES below for the
                  full whitelist per KBV Arztnummern-Richtlinie Anlage 1)
        Pos 3–9:  Laufende Nummer (sequential number assigned by KV)

    Examples (fictitious): 021234568, 381789045, 721234567

    Accuracy note: The BSNR has no public Prüfziffer algorithm, so
    validate_result cannot give positive evidence of a real BSNR; it can
    only drop clearly invalid inputs (wrong length, non-digit, all-zero).
    All structurally-plausible 9-digit inputs therefore return ``None``
    from validate_result: the match keeps its base pattern score (0.2)
    and the ContextAwareEnhancer drives final confidence via context
    words ("Betriebsstättennummer", "BSNR", "Praxis", …).

    VALID_KV_CODES below lists the 2-digit regional codes documented in
    KBV Arztnummern-Richtlinie Anlage 1. It is retained for reference
    and future opt-in strict validation but is intentionally NOT used
    to upgrade whitelisted-prefix matches to MAX_SCORE — the `\b\d{9}\b`
    pattern is too broad to justify that upgrade on a 2-digit-prefix
    check alone.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    # Valid KV regional codes per KBV Arztnummern-Richtlinie Anlage 1.
    # Includes the standard 17 KV regions, the KBV itself, and Anlage-8 BMV-Ä
    # special codes for Krankenhäuser.
    VALID_KV_CODES = frozenset({
        "01",  # Schleswig-Holstein
        "02",  # Hamburg
        "03",  # Bremen
        "17",  # Niedersachsen
        "20",  # Westfalen-Lippe
        "35",  # Krankenhäuser (Anlage 8 BMV-Ä)
        "38",  # Nordrhein
        "46",  # Hessen
        "51",  # Rheinland-Pfalz
        "52",  # Baden-Württemberg
        "71",  # Bayern
        "72",  # Berlin
        "73",  # Saarland
        "74",  # KBV (Kassenärztliche Bundesvereinigung)
        "78",  # Mecklenburg-Vorpommern
        "83",  # Brandenburg
        "88",  # Sachsen-Anhalt
        "93",  # Thüringen
        "98",  # Sachsen
    })

    PATTERNS = [
        Pattern(
            "Betriebsstättennummer BSNR (9 digits)",
            r"\b\d{9}\b",
            0.2,
        ),
    ]

    CONTEXT = [
        "betriebsstättennummer",
        "betriebsstätten-nummer",
        "bsnr",
        "betriebsstätte",
        "praxisnummer",
        "arztpraxis",
        "praxis",
        "kassenärztliche vereinigung",
        "kv-nummer",
        "kv nummer",
        "praxisadresse",
        "praxisstandort",
        "nebenbetriebsstätte",
        "hauptbetriebsstätte",
        "behandlungsort",
        "vertragsarztpraxis",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_BSNR",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        r"""
        Validate the BSNR structurally.

        BSNR has no publicly documented Prüfziffer algorithm, so this
        method can only drop clearly invalid inputs. It does NOT promote
        structurally-valid matches to MAX_SCORE — the ``\b\d{9}\b``
        base pattern is too broad for that to be safe on a 2-digit
        prefix check alone. Final confidence on valid-shaped BSNRs is
        driven by context words via the ContextAwareEnhancer.

        :param pattern_text: the text to validate (9 digits)
        :return: False if the input is malformed (wrong length,
                 non-digit, or all-zero); None otherwise (keep pattern
                 score, let context drive confidence).
        """
        pattern_text = pattern_text.strip()

        if len(pattern_text) != 9 or not pattern_text.isdigit():
            return False

        if pattern_text == "000000000":
            return False

        return None

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the BSNR structurally.

BSNR has no publicly documented Prüfziffer algorithm, so this method can only drop clearly invalid inputs. It does NOT promote structurally-valid matches to MAX_SCORE — the \b\d{9}\b base pattern is too broad for that to be safe on a 2-digit prefix check alone. Final confidence on valid-shaped BSNRs is driven by context words via the ContextAwareEnhancer.

PARAMETER DESCRIPTION
pattern_text

the text to validate (9 digits)

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

False if the input is malformed (wrong length, non-digit, or all-zero); None otherwise (keep pattern score, let context drive confidence).

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_bsnr_recognizer.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def validate_result(self, pattern_text: str) -> Optional[bool]:
    r"""
    Validate the BSNR structurally.

    BSNR has no publicly documented Prüfziffer algorithm, so this
    method can only drop clearly invalid inputs. It does NOT promote
    structurally-valid matches to MAX_SCORE — the ``\b\d{9}\b``
    base pattern is too broad for that to be safe on a 2-digit
    prefix check alone. Final confidence on valid-shaped BSNRs is
    driven by context words via the ContextAwareEnhancer.

    :param pattern_text: the text to validate (9 digits)
    :return: False if the input is malformed (wrong length,
             non-digit, or all-zero); None otherwise (keep pattern
             score, let context drive confidence).
    """
    pattern_text = pattern_text.strip()

    if len(pattern_text) != 9 or not pattern_text.isdigit():
        return False

    if pattern_text == "000000000":
        return False

    return None

DeFuehrerscheinRecognizer

Bases: PatternRecognizer

Recognizes German Führerscheinnummern (driving license numbers).

The Führerscheinnummer is the document number printed in Field 5 of the German driving license card. Since the EU-harmonized credit-card format was introduced on 19 January 2013 (EU Directive 2006/126/EC, implemented via FeV reform), the number follows a fixed 11-character structure:

Pos 1–2:   Behördenkürzel – 2 uppercase letters identifying the issuing
           Fahrerlaubnisbehörde (derived from the Kfz-Zulassungskürzel of
           the issuing Kreis/Stadt, e.g. "B0" Berlin, "MU" München,
           "HH" Hamburg, "KO" Koblenz)
Pos 3–5:   Behördennummer – 3-digit authority code within the Bundesland
           (assigned by the Kraftfahrt-Bundesamt, KBA)
Pos 6–10:  Laufende Nummer – 5-digit sequential issue number
Pos 11:    Prüfzeichen – 1 check character (uppercase letter A–Z or
           digit 0–9); the derivation algorithm is not published by KBA

Legal basis: FeV Anlage 8 (Fahrerlaubnis-Verordnung, Anlage 8 – Muster des Führerscheins), KBA Schlüsselverzeichnis der Fahrerlaubnisbehörden. EU standard: Annex I to Directive 2006/126/EC (Field 5). Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

Examples (fictitious): B012345678A, MU12345678B, HH98765432C

Scope note: Pre-2013 German driving licenses (pink folded card, laminated card) used locally defined, non-standardized number formats and remain legally valid until 2033 under EU grandfathering rules. Their numbers do not reliably fit the 11-character pattern and are therefore out of scope for this recognizer. Context words (e.g. "Führerschein", "Fahrerlaubnis") remain the primary means of distinguishing true license numbers from other 11-character alphanumeric codes.

No checksum validation is implemented because the Prüfzeichen derivation formula is not published in FeV Anlage 8 or KBA administrative documents.

Accuracy note: The pattern [A-Z]{2}\\d{8}[A-Z0-9] (11 characters) is fairly specific, but context words are required for high-confidence matches because no computable checksum is available. The base confidence is set to 0.35. Formal accuracy evaluation has not been performed on a labelled dataset.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_FUEHRERSCHEIN'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_fuehrerschein_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
class DeFuehrerscheinRecognizer(PatternRecognizer):
    r"""
    Recognizes German Führerscheinnummern (driving license numbers).

    The Führerscheinnummer is the document number printed in Field 5 of the
    German driving license card.  Since the EU-harmonized credit-card format
    was introduced on 19 January 2013 (EU Directive 2006/126/EC, implemented
    via FeV reform), the number follows a fixed 11-character structure:

        Pos 1–2:   Behördenkürzel – 2 uppercase letters identifying the issuing
                   Fahrerlaubnisbehörde (derived from the Kfz-Zulassungskürzel of
                   the issuing Kreis/Stadt, e.g. "B0" Berlin, "MU" München,
                   "HH" Hamburg, "KO" Koblenz)
        Pos 3–5:   Behördennummer – 3-digit authority code within the Bundesland
                   (assigned by the Kraftfahrt-Bundesamt, KBA)
        Pos 6–10:  Laufende Nummer – 5-digit sequential issue number
        Pos 11:    Prüfzeichen – 1 check character (uppercase letter A–Z or
                   digit 0–9); the derivation algorithm is not published by KBA

    Legal basis: FeV Anlage 8 (Fahrerlaubnis-Verordnung, Anlage 8 – Muster des
    Führerscheins), KBA Schlüsselverzeichnis der Fahrerlaubnisbehörden.
    EU standard: Annex I to Directive 2006/126/EC (Field 5).
    Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

    Examples (fictitious): B012345678A, MU12345678B, HH98765432C

    Scope note: Pre-2013 German driving licenses (pink folded card, laminated
    card) used locally defined, non-standardized number formats and remain
    legally valid until 2033 under EU grandfathering rules.  Their numbers do
    not reliably fit the 11-character pattern and are therefore out of scope
    for this recognizer.  Context words (e.g. "Führerschein", "Fahrerlaubnis")
    remain the primary means of distinguishing true license numbers from
    other 11-character alphanumeric codes.

    No checksum validation is implemented because the Prüfzeichen derivation
    formula is not published in FeV Anlage 8 or KBA administrative documents.

    Accuracy note: The pattern ``[A-Z]{2}\\d{8}[A-Z0-9]`` (11 characters) is
    fairly specific, but context words are required for high-confidence matches
    because no computable checksum is available.  The base confidence is set to
    0.35.  Formal accuracy evaluation has not been performed on a labelled dataset.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "Führerscheinnummer (Post-2013 EU-Format, 11 Zeichen)",
            r"\b[A-Z]{2}\d{8}[A-Z0-9]\b",
            0.35,
        ),
    ]

    CONTEXT = [
        "führerscheinnummer",
        "führerschein",
        "fahrerlaubnis",
        "fahrerlaubnisnummer",
        "fahrerlaubnisklasse",
        "führerscheininhaber",
        "fev",
        "kba",
        "kraftfahrt-bundesamt",
        "driving licence",
        "driving license",
        "driver's license",
        "licence number",
        "license number",
        "dokument nr",
        "dokument-nr",
        "feld 5",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_FUEHRERSCHEIN",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

DeHandelsregisterRecognizer

Bases: PatternRecognizer

Recognizes German commercial register numbers (Handelsregisternummer).

The Handelsregisternummer identifies legal entities and sole traders registered in the German Handelsregister (commercial register), maintained by local Amtsgerichte (district courts). It is divided into two sections:

- Abteilung A (HRA): Einzelkaufleute (sole traders) and Personengesellschaften
  (partnerships: OHG, KG, GmbH & Co. KG, etc.). For Einzelkaufleute, the HRA
  number directly identifies a natural person, making it personal data under
  DSGVO Art. 4 Nr. 1.

- Abteilung B (HRB): Kapitalgesellschaften (corporations: GmbH, AG, KGaA,
  UG (haftungsbeschränkt), etc.). Identifies legal entities; not personal data
  unless the entity is a sole shareholder / sole director whose identity is
  directly derivable.

Legal basis: § 9, § 14 HGB (Handelsgesetzbuch), Handelsregisterverordnung (HRV). Data protection: DSGVO Art. 4 Nr. 1 for HRA entries linked to natural persons; BDSG.

Format: HR[AB][optional space] [1–6 digits] Examples: HRA 12345, HRB 123456, HRA12345, HRB 1234 Köln

The HR[AB] prefix makes the pattern highly specific, resulting in low false positive rates even without a checksum. No formal accuracy evaluation has been performed on a labelled dataset.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_HANDELSREGISTER'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_handelsregister_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class DeHandelsregisterRecognizer(PatternRecognizer):
    """
    Recognizes German commercial register numbers (Handelsregisternummer).

    The Handelsregisternummer identifies legal entities and sole traders registered
    in the German Handelsregister (commercial register), maintained by local
    Amtsgerichte (district courts). It is divided into two sections:

        - Abteilung A (HRA): Einzelkaufleute (sole traders) and Personengesellschaften
          (partnerships: OHG, KG, GmbH & Co. KG, etc.). For Einzelkaufleute, the HRA
          number directly identifies a natural person, making it personal data under
          DSGVO Art. 4 Nr. 1.

        - Abteilung B (HRB): Kapitalgesellschaften (corporations: GmbH, AG, KGaA,
          UG (haftungsbeschränkt), etc.). Identifies legal entities; not personal data
          unless the entity is a sole shareholder / sole director whose identity is
          directly derivable.

    Legal basis: § 9, § 14 HGB (Handelsgesetzbuch), Handelsregisterverordnung (HRV).
    Data protection: DSGVO Art. 4 Nr. 1 for HRA entries linked to natural persons; BDSG.

    Format:
        HR[AB] [optional space] [1–6 digits]
        Examples: HRA 12345, HRB 123456, HRA12345, HRB 1234 Köln

    The `HR[AB]` prefix makes the pattern highly specific, resulting in low false
    positive rates even without a checksum. No formal accuracy evaluation has been
    performed on a labelled dataset.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "Handelsregisternummer HRA/HRB",
            r"\bHR[AB]\s*\d{1,6}\b",
            0.5,
        ),
    ]

    CONTEXT = [
        "handelsregister",
        "handelsregisternummer",
        "amtsgericht",
        "registergericht",
        "hra",
        "hrb",
        "hr-nummer",
        "registerauszug",
        "handelsregistereintrag",
        "firma",
        "gesellschaft",
        "gmbh",
        "ag",
        "ug",
        "kg",
        "ohg",
        "einzelkaufmann",
        "einzelkauffrau",
        "handelsregisterblattnummer",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_HANDELSREGISTER",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

DeHealthInsuranceRecognizer

Bases: PatternRecognizer

Recognizes German statutory health insurance numbers (KVNR).

Also called Krankenversicherungsnummer, Krankenversichertennummer, or Versichertennummer.

The KVNR is assigned to every person insured under the German statutory health insurance system (gesetzliche Krankenversicherung, GKV). It is printed on the Gesundheitskarte (eGK – elektronische Gesundheitskarte).

Legal basis: § 290 SGB V (Sozialgesetzbuch Fünftes Buch – Gesetzliche Krankenversicherung). Data protection: DSGVO Art. 9 (besondere Kategorien personenbezogener Daten – Gesundheitsdaten), BDSG § 22.

Format (10 characters): Pos 1: Buchstabe (first letter of birth surname, A–Z) Pos 2–9: 8 digits (birth date encoded + serial) Pos 10: Prüfziffer (check digit, 0–9)

Example: A000500015 (from § 290 SGB V Anlage 1, Stand 02.01.2023)

Check digit algorithm (§ 290 SGB V Anlage 1, GKV-Spitzenverband): 1. Convert the letter at position 1 to its 2-digit ordinal value (A=01, B=02, …, Z=26). Concatenated with the 8 data digits at positions 2–9, this yields 10 effective digits. 2. Apply alternating factors [1, 2, 1, 2, 1, 2, 1, 2, 1, 2] to those 10 effective digits. 3. For each product ≥ 10, replace it with the cross-sum of its digits (Quersumme). 4. Sum the 10 values; compute sum mod 10. 5. The result must equal the check digit at position 10.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_HEALTH_INSURANCE'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the KVNR using the GKV-Spitzenverband checksum algorithm.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_health_insurance_recognizer.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class DeHealthInsuranceRecognizer(PatternRecognizer):
    """
    Recognizes German statutory health insurance numbers (KVNR).

    Also called Krankenversicherungsnummer, Krankenversichertennummer, or
    Versichertennummer.

    The KVNR is assigned to every person insured under the German statutory
    health insurance system (gesetzliche Krankenversicherung, GKV). It is printed on the
    Gesundheitskarte (eGK – elektronische Gesundheitskarte).

    Legal basis: § 290 SGB V (Sozialgesetzbuch Fünftes Buch – Gesetzliche
    Krankenversicherung).
    Data protection: DSGVO Art. 9 (besondere Kategorien personenbezogener Daten –
    Gesundheitsdaten), BDSG § 22.

    Format (10 characters):
        Pos 1:    Buchstabe (first letter of birth surname, A–Z)
        Pos 2–9:  8 digits (birth date encoded + serial)
        Pos 10:   Prüfziffer (check digit, 0–9)

    Example: A000500015 (from § 290 SGB V Anlage 1, Stand 02.01.2023)

    Check digit algorithm (§ 290 SGB V Anlage 1, GKV-Spitzenverband):
        1. Convert the letter at position 1 to its 2-digit ordinal value
           (A=01, B=02, …, Z=26). Concatenated with the 8 data digits at
           positions 2–9, this yields 10 effective digits.
        2. Apply alternating factors [1, 2, 1, 2, 1, 2, 1, 2, 1, 2] to those
           10 effective digits.
        3. For each product ≥ 10, replace it with the cross-sum of its digits
           (Quersumme).
        4. Sum the 10 values; compute sum mod 10.
        5. The result must equal the check digit at position 10.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    # Accuracy note: The base pattern `[A-Z]\d{9}` is intentionally broad (any
    # uppercase letter followed by 9 digits) because no more specific structural
    # constraint exists in the KVNR format beyond length and the leading letter.
    # The GKV checksum validation in validate_result() is the primary defence
    # against false positives; the base confidence is therefore kept low (0.3)
    # and context words are required for high-confidence matches.
    # Formal accuracy evaluation has not been performed on a labelled dataset.
    PATTERNS = [
        Pattern(
            "Krankenversicherungsnummer KVNR (letter + 9 digits)",
            r"\b[A-Z]\d{9}\b",
            0.3,
        ),
    ]

    CONTEXT = [
        "krankenversicherungsnummer",
        "krankenversichertennummer",
        "versichertennummer",
        "kvnr",
        "krankenkasse",
        "krankenversicherung",
        "gesundheitskarte",
        "egk",
        "elektronische gesundheitskarte",
        "gkv",
        "gesetzliche krankenversicherung",
        "krankenversicherungsausweis",
        "versichertenausweis",
        "versichertenkarte",
        "aok",
        "tkk",
        "barmer",
        "dak",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_HEALTH_INSURANCE",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the KVNR using the GKV-Spitzenverband checksum algorithm.

        Algorithm source: GKV-Spitzenverband technical specification (§ 290 SGB V).

        :param pattern_text: the text to validate (10 characters: 1 letter + 9 digits)
        :return: True if valid, False if invalid
        """
        pattern_text = pattern_text.upper().strip()

        if len(pattern_text) != 10:
            return False

        if not re.match(r"^[A-Z]\d{9}$", pattern_text):
            return False

        letter = pattern_text[0]
        letter_val = str(ord(letter) - ord("A") + 1).zfill(2)

        # Letter expanded to 2 digits + 8 data digits = 10 effective digits
        effective = letter_val + pattern_text[1:9]

        check_digit = int(pattern_text[9])
        factors = [1, 2, 1, 2, 1, 2, 1, 2, 1, 2]

        total = 0
        for digit_char, factor in zip(effective, factors):
            product = int(digit_char) * factor
            if product >= 10:
                product = (product // 10) + (product % 10)
            total += product

        return (total % 10) == check_digit

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the KVNR using the GKV-Spitzenverband checksum algorithm.

Algorithm source: GKV-Spitzenverband technical specification (§ 290 SGB V).

PARAMETER DESCRIPTION
pattern_text

the text to validate (10 characters: 1 letter + 9 digits)

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True if valid, False if invalid

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_health_insurance_recognizer.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the KVNR using the GKV-Spitzenverband checksum algorithm.

    Algorithm source: GKV-Spitzenverband technical specification (§ 290 SGB V).

    :param pattern_text: the text to validate (10 characters: 1 letter + 9 digits)
    :return: True if valid, False if invalid
    """
    pattern_text = pattern_text.upper().strip()

    if len(pattern_text) != 10:
        return False

    if not re.match(r"^[A-Z]\d{9}$", pattern_text):
        return False

    letter = pattern_text[0]
    letter_val = str(ord(letter) - ord("A") + 1).zfill(2)

    # Letter expanded to 2 digits + 8 data digits = 10 effective digits
    effective = letter_val + pattern_text[1:9]

    check_digit = int(pattern_text[9])
    factors = [1, 2, 1, 2, 1, 2, 1, 2, 1, 2]

    total = 0
    for digit_char, factor in zip(effective, factors):
        product = int(digit_char) * factor
        if product >= 10:
            product = (product // 10) + (product % 10)
        total += product

    return (total % 10) == check_digit

DeIdCardRecognizer

Bases: PatternRecognizer

Recognizes German national ID card numbers (Personalausweisnummern) using regex.

The German Personalausweis (nPA – neuer Personalausweis) has been issued since November 2010. Its document number (Seriennummer/Dokumentennummer) is printed on the front of the card and encoded in the Machine Readable Zone (MRZ).

Legal basis: Personalausweisgesetz (PAuswG), Personalausweisverordnung (PAuswV). Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

Format (nPA, since November 2010): - 9 characters: first 8 from the ICAO restricted uppercase charset (excludes A, B, D, E, I, O, Q, S, U) plus 1 digit at position 9 (the ICAO Doc 9303 check digit). - Example: L01X00T44 (verifies against ICAO)

Format (old Personalausweis, before November 2010): - Letter T followed by 8 digits (legacy 9-char format; no ICAO check digit — the trailing digit is part of the serial). - Example: T22000124

Check digit algorithm (ICAO Doc 9303, nPA only): Weights 7, 3, 1 repeating on positions 1–8 with letters mapped A=10 … Z=35; the sum modulo 10 must equal the digit at position 9.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_ID_CARD'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the nPA ICAO Doc 9303 check digit.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_id_card_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
class DeIdCardRecognizer(PatternRecognizer):
    """
    Recognizes German national ID card numbers (Personalausweisnummern) using regex.

    The German Personalausweis (nPA – neuer Personalausweis) has been issued since
    November 2010. Its document number (Seriennummer/Dokumentennummer) is printed on
    the front of the card and encoded in the Machine Readable Zone (MRZ).

    Legal basis: Personalausweisgesetz (PAuswG), Personalausweisverordnung (PAuswV).
    Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

    Format (nPA, since November 2010):
        - 9 characters: first 8 from the ICAO restricted uppercase charset
          (excludes A, B, D, E, I, O, Q, S, U) plus 1 digit at position 9
          (the ICAO Doc 9303 check digit).
        - Example: L01X00T44 (verifies against ICAO)

    Format (old Personalausweis, before November 2010):
        - Letter T followed by 8 digits (legacy 9-char format; no ICAO
          check digit — the trailing digit is part of the serial).
        - Example: T22000124

    Check digit algorithm (ICAO Doc 9303, nPA only):
        Weights 7, 3, 1 repeating on positions 1–8 with letters mapped
        A=10 … Z=35; the sum modulo 10 must equal the digit at position 9.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "Personalausweisnummer nPA (ICAO charset + check digit)",
            r"\b[CFGHJKLMNPRTVWXYZ][CFGHJKLMNPRTVWXYZ0-9]{7}[0-9]\b",
            0.4,
        ),
        Pattern(
            "Personalausweisnummer alt (T + 8 Ziffern)",
            r"\bT\d{8}\b",
            0.5,
        ),
    ]

    CONTEXT = [
        "personalausweis",
        "ausweis",
        "personalausweisnummer",
        "ausweisnummer",
        "ausweisdokument",
        "dokumentennummer",
        "seriennummer",
        "npa",
        "neuer personalausweis",
        "personalausweisgesetz",
        "pauwsg",
        "bundespersonalausweis",
        "identity card",
        "national id",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_ID_CARD",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the nPA ICAO Doc 9303 check digit.

        Legacy "T + 8 digits" numbers (pre-2010) are accepted at pattern
        confidence (return ``None``) because they predate ICAO and do not
        carry a check digit.

        :param pattern_text: the text to validate (9 characters)
        :return: True if the ICAO check matches; False if the nPA-shaped
                 value fails the check; None for the legacy T-format which
                 cannot be structurally validated here.
        """
        pattern_text = pattern_text.upper().strip()

        if len(pattern_text) != 9:
            return False

        if pattern_text[0] == "T" and pattern_text[1:].isdigit():
            return None

        if not pattern_text[-1].isdigit():
            return False

        weights = [7, 3, 1]
        total = 0
        for i, c in enumerate(pattern_text[:-1]):
            if c.isdigit():
                value = int(c)
            elif "A" <= c <= "Z":
                value = ord(c) - ord("A") + 10
            else:
                return False
            total += value * weights[i % 3]

        return (total % 10) == int(pattern_text[-1])

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the nPA ICAO Doc 9303 check digit.

Legacy "T + 8 digits" numbers (pre-2010) are accepted at pattern confidence (return None) because they predate ICAO and do not carry a check digit.

PARAMETER DESCRIPTION
pattern_text

the text to validate (9 characters)

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True if the ICAO check matches; False if the nPA-shaped value fails the check; None for the legacy T-format which cannot be structurally validated here.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_id_card_recognizer.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the nPA ICAO Doc 9303 check digit.

    Legacy "T + 8 digits" numbers (pre-2010) are accepted at pattern
    confidence (return ``None``) because they predate ICAO and do not
    carry a check digit.

    :param pattern_text: the text to validate (9 characters)
    :return: True if the ICAO check matches; False if the nPA-shaped
             value fails the check; None for the legacy T-format which
             cannot be structurally validated here.
    """
    pattern_text = pattern_text.upper().strip()

    if len(pattern_text) != 9:
        return False

    if pattern_text[0] == "T" and pattern_text[1:].isdigit():
        return None

    if not pattern_text[-1].isdigit():
        return False

    weights = [7, 3, 1]
    total = 0
    for i, c in enumerate(pattern_text[:-1]):
        if c.isdigit():
            value = int(c)
        elif "A" <= c <= "Z":
            value = ord(c) - ord("A") + 10
        else:
            return False
        total += value * weights[i % 3]

    return (total % 10) == int(pattern_text[-1])

DeKfzRecognizer

Bases: PatternRecognizer

Recognizes German vehicle registration plates (KFZ-Kennzeichen).

German license plates are issued by local Zulassungsbehörden (vehicle registration authorities). While not exclusively personal (vehicles can be owned by companies), they can be linked to natural persons and are considered personally identifiable information in the context of data protection law.

Legal basis: Fahrzeug-Zulassungsverordnung (FZV) § 8, § 9. Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten) – license plates constitute personal data when they can be linked to an identifiable person (ECJ ruling C-582/14, Breyer v. Germany).

Format: [Unterscheidungszeichen][Erkennungszeichen] [Ziffern][Suffix]

Unterscheidungszeichen: 1–3 uppercase letters (district/city code)
Erkennungszeichen:      1–2 uppercase letters
Ziffern:               1–4 digits
Suffix (optional):     E (electric vehicle) or H (Oldtimer/historic, ≥30 years)

Examples B AB 1234 (Berlin) M XY 999 (München) HH AB 1234 (Hamburg) KA EF 1H (Karlsruhe, historic) MIL E 1234E (Miltenberg, electric) S AB 12 (Stuttgart)

Note: Seasonal plates (Saison-Kennzeichen) also contain month ranges but are not covered by this pattern.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_KFZ'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_kfz_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
class DeKfzRecognizer(PatternRecognizer):
    """
    Recognizes German vehicle registration plates (KFZ-Kennzeichen).

    German license plates are issued by local Zulassungsbehörden (vehicle registration
    authorities). While not exclusively personal (vehicles can be owned by companies),
    they can be linked to natural persons and are considered personally identifiable
    information in the context of data protection law.

    Legal basis: Fahrzeug-Zulassungsverordnung (FZV) § 8, § 9.
    Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten) – license plates
    constitute personal data when they can be linked to an identifiable person (ECJ
    ruling C-582/14, Breyer v. Germany).

    Format:
        [Unterscheidungszeichen] [Erkennungszeichen] [Ziffern] [Suffix]

        Unterscheidungszeichen: 1–3 uppercase letters (district/city code)
        Erkennungszeichen:      1–2 uppercase letters
        Ziffern:               1–4 digits
        Suffix (optional):     E (electric vehicle) or H (Oldtimer/historic, ≥30 years)

    Examples
            B AB 1234      (Berlin)
            M XY 999       (München)
            HH AB 1234     (Hamburg)
            KA EF 1H       (Karlsruhe, historic)
            MIL E 1234E    (Miltenberg, electric)
            S AB 12        (Stuttgart)

    Note: Seasonal plates (Saison-Kennzeichen) also contain month ranges but are not
    covered by this pattern.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "KFZ-Kennzeichen (mit Leerzeichen)",
            r"(?<![\w-])[A-ZÄÖÜ]{1,3}\s[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)",
            0.3,
        ),
        Pattern(
            "KFZ-Kennzeichen (mit Bindestrich)",
            r"(?<![\w-])[A-ZÄÖÜ]{1,3}-[A-Z]{1,2}-\d{1,4}[EH]?(?!\w)",
            0.3,
        ),
        Pattern(
            "KFZ-Kennzeichen (Bindestrich + Leerzeichen)",
            r"(?<![\w-])[A-ZÄÖÜ]{1,3}-[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)",
            0.3,
        ),
        Pattern(
            "KFZ-Kennzeichen (ASCII only, mit Leerzeichen)",
            r"(?<![\w-])[A-Z]{1,3}\s[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)",
            0.2,
        ),
        Pattern(
            "KFZ-Kennzeichen (ASCII only, Bindestrich + Leerzeichen)",
            r"(?<![\w-])[A-Z]{1,3}-[A-Z]{1,2}\s\d{1,4}[EH]?(?!\w)",
            0.2,
        ),
    ]

    CONTEXT = [
        "kennzeichen",
        "kfz-kennzeichen",
        "kraftfahrzeugkennzeichen",
        "nummernschild",
        "fahrzeugkennzeichen",
        "zulassung",
        "kfz",
        "fahrzeug",
        "auto",
        "pkw",
        "lkw",
        "fahrzeugschein",
        "fahrzeugbrief",
        "zulassungsbescheinigung",
        "amtliches kennzeichen",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_KFZ",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

DeLanrRecognizer

Bases: PatternRecognizer

Recognizes German Lebenslange Arztnummer (LANR).

The LANR is a 9-digit lifetime physician number assigned by the Kassenärztliche Vereinigung (KV) to every licensed physician participating in the German statutory health insurance system (Vertragsarzt). It appears on prescriptions (Rezepte), billing records, discharge letters, and other statutory healthcare documents.

Legal basis: § 75 Abs. 7 SGB V (Sozialgesetzbuch Fünftes Buch). Standard: KBV-Richtlinie nach § 75 Abs. 7 SGB V zur Vergabe der Arzt-, Betriebsstätten-, Praxisnetz- sowie der Netzverbundnummern. Data protection: DSGVO Art. 9 (Gesundheitsdaten), BDSG § 22.

Format (9 digits): Pos 1–6: Arztnummer (physician identifier, assigned by KV) Pos 7: Prüfziffer (check digit, derived from pos 1–6) Pos 8–9: Arztgruppe / Fachgruppe (specialty / physician group code)

Examples: 123456601, 234567701, 100000601

Check digit algorithm (KBV Arztnummern-Richtlinie): 1. Multiply digits at positions 1–6 alternately by 4 and 9 from the left: weights [4, 9, 4, 9, 4, 9]. 2. Sum the six products (no cross-sum step). 3. Check digit (pos 7) = (10 − sum mod 10) mod 10, i.e. the difference to 10; if the difference is 10, the check digit is 0.

Worked example for physician digits 123456: products = 4, 18, 12, 36, 20, 54 → sum = 144 144 mod 10 = 4, 10 − 4 = 6 → check digit 6, so LANR = 123456601

Accuracy note: The base pattern \\b\\d{9}\\b matches any 9-digit token. Because LANRs share the same surface form as other 9-digit identifiers (e.g. DE_BSNR), the checksum in validate_result() is the primary guard against false positives; context words are required for high-confidence results without a valid checksum. Formal accuracy evaluation has not been performed on a labelled dataset.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_LANR'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the LANR using the KBV Arztnummern-Richtlinie checksum.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_lanr_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class DeLanrRecognizer(PatternRecognizer):
    r"""
    Recognizes German Lebenslange Arztnummer (LANR).

    The LANR is a 9-digit lifetime physician number assigned by the
    Kassenärztliche Vereinigung (KV) to every licensed physician participating
    in the German statutory health insurance system (Vertragsarzt).  It appears
    on prescriptions (Rezepte), billing records, discharge letters, and other
    statutory healthcare documents.

    Legal basis: § 75 Abs. 7 SGB V (Sozialgesetzbuch Fünftes Buch).
    Standard: KBV-Richtlinie nach § 75 Abs. 7 SGB V zur Vergabe der Arzt-,
    Betriebsstätten-, Praxisnetz- sowie der Netzverbundnummern.
    Data protection: DSGVO Art. 9 (Gesundheitsdaten), BDSG § 22.

    Format (9 digits):
        Pos 1–6:  Arztnummer (physician identifier, assigned by KV)
        Pos 7:    Prüfziffer (check digit, derived from pos 1–6)
        Pos 8–9:  Arztgruppe / Fachgruppe (specialty / physician group code)

    Examples: 123456601, 234567701, 100000601

    Check digit algorithm (KBV Arztnummern-Richtlinie):
        1. Multiply digits at positions 1–6 alternately by 4 and 9 from the
           left: weights [4, 9, 4, 9, 4, 9].
        2. Sum the six products (no cross-sum step).
        3. Check digit (pos 7) = (10 − sum mod 10) mod 10, i.e. the
           difference to 10; if the difference is 10, the check digit is 0.

    Worked example for physician digits 123456:
        products = 4, 18, 12, 36, 20, 54 → sum = 144
        144 mod 10 = 4, 10 − 4 = 6 → check digit 6, so LANR = 123456601

    Accuracy note: The base pattern ``\\b\\d{9}\\b`` matches any 9-digit token.
    Because LANRs share the same surface form as other 9-digit identifiers
    (e.g. DE_BSNR), the checksum in ``validate_result()`` is the primary guard
    against false positives; context words are required for high-confidence
    results without a valid checksum.  Formal accuracy evaluation has not been
    performed on a labelled dataset.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "Lebenslange Arztnummer LANR (9 digits)",
            r"\b\d{9}\b",
            0.3,
        ),
    ]

    CONTEXT = [
        "arztnummer",
        "lanr",
        "lebenslange arztnummer",
        "arzt-nr",
        "arzt nr",
        "arzt-nummer",
        "vertragsarzt",
        "kassenarzt",
        "niedergelassener arzt",
        "kbv",
        "kassenärztliche vereinigung",
        "kv-nummer",
        "rezept",
        "verschreibung",
        "behandelnder arzt",
        "hausarzt",
        "facharzt",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_LANR",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the LANR using the KBV Arztnummern-Richtlinie checksum.

        Algorithm source: KBV Arztnummern-Richtlinie nach § 75 Abs. 7 SGB V.

        :param pattern_text: the text to validate (9 digits)
        :return: True if check digit is valid, False otherwise
        """
        pattern_text = pattern_text.strip()

        if len(pattern_text) != 9 or not pattern_text.isdigit():
            return False

        weights = [4, 9, 4, 9, 4, 9]
        total = sum(int(d) * w for d, w in zip(pattern_text[:6], weights))
        expected_check = (10 - total % 10) % 10

        return int(pattern_text[6]) == expected_check

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the LANR using the KBV Arztnummern-Richtlinie checksum.

Algorithm source: KBV Arztnummern-Richtlinie nach § 75 Abs. 7 SGB V.

PARAMETER DESCRIPTION
pattern_text

the text to validate (9 digits)

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True if check digit is valid, False otherwise

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_lanr_recognizer.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the LANR using the KBV Arztnummern-Richtlinie checksum.

    Algorithm source: KBV Arztnummern-Richtlinie nach § 75 Abs. 7 SGB V.

    :param pattern_text: the text to validate (9 digits)
    :return: True if check digit is valid, False otherwise
    """
    pattern_text = pattern_text.strip()

    if len(pattern_text) != 9 or not pattern_text.isdigit():
        return False

    weights = [4, 9, 4, 9, 4, 9]
    total = sum(int(d) * w for d, w in zip(pattern_text[:6], weights))
    expected_check = (10 - total % 10) % 10

    return int(pattern_text[6]) == expected_check

DePassportRecognizer

Bases: PatternRecognizer

Recognizes German passport numbers (Reisepassnummern) using regex.

German passports are issued by the Bundesdruckerei on behalf of the Bundesrepublik Deutschland. The document number consists of 9 alphanumeric characters and appears on the data page and in the Machine Readable Zone (MRZ).

Legal basis: Passgesetz (PassG) § 4, Passverordnung (PassV). Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

Format (9 characters total): - 8 alphanumeric characters (uppercase letters from the limited set C, F, G, H, J, K, L, M, N, P, R, T, V, W, X, Y, Z and digits 0–9) followed by - 1 digit at position 9 — the ICAO Doc 9303 check digit over the first 8 characters. - Example: C01X00T41 (F20400481 verifies against ICAO)

Character set excludes visually ambiguous letters (A, B, D, E, I, O, Q, S, U) per ICAO Doc 9303 Machine Readable Travel Documents.

Check digit algorithm (ICAO Doc 9303): - Letters A=10, B=11, …, Z=35; digits keep their face value. - Apply weights 7, 3, 1 repeating to the first 8 characters. - Sum the products, take sum mod 10 — that is the 9th digit.

Worked example for C01X00T41: values = 12, 0, 1, 33, 0, 0, 29, 4 weights = 7, 3, 1, 7, 3, 1, 7, 3 products = 84, 0, 1, 231, 0, 0, 203, 12 → sum = 531 531 mod 10 = 1 → matches check digit '1'

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_PASSPORT'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the ICAO Doc 9303 check digit at position 9.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_passport_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class DePassportRecognizer(PatternRecognizer):
    """
    Recognizes German passport numbers (Reisepassnummern) using regex.

    German passports are issued by the Bundesdruckerei on behalf of the
    Bundesrepublik Deutschland. The document number consists of 9 alphanumeric
    characters and appears on the data page and in the Machine Readable Zone (MRZ).

    Legal basis: Passgesetz (PassG) § 4, Passverordnung (PassV).
    Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

    Format (9 characters total):
        - 8 alphanumeric characters (uppercase letters from the limited set
          C, F, G, H, J, K, L, M, N, P, R, T, V, W, X, Y, Z and digits 0–9)
          followed by
        - 1 digit at position 9 — the ICAO Doc 9303 check digit over the
          first 8 characters.
        - Example: C01X00T41 (F20400481 verifies against ICAO)

    Character set excludes visually ambiguous letters (A, B, D, E, I, O, Q,
    S, U) per ICAO Doc 9303 Machine Readable Travel Documents.

    Check digit algorithm (ICAO Doc 9303):
        - Letters A=10, B=11, …, Z=35; digits keep their face value.
        - Apply weights 7, 3, 1 repeating to the first 8 characters.
        - Sum the products, take sum mod 10 — that is the 9th digit.

    Worked example for C01X00T41:
        values = 12, 0, 1, 33, 0, 0, 29, 4
        weights = 7, 3, 1, 7, 3, 1, 7, 3
        products = 84, 0, 1, 231, 0, 0, 203, 12 → sum = 531
        531 mod 10 = 1 → matches check digit '1'

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    # Only the ICAO-restricted charset is used. A previous relaxed
    # pattern allowing any [A-Z] first character was removed: it would
    # accept forbidden letters (A, B, D, E, I, O, Q, S, U) and
    # validate_result would still compute a MRZ check digit for them,
    # occasionally upgrading non-German or obviously-invalid strings to
    # MAX_SCORE. The strict pattern already covers every legitimate
    # German passport number.
    PATTERNS = [
        Pattern(
            "Reisepassnummer (Strict ICAO charset)",
            r"\b[CFGHJKLMNPRTVWXYZ][CFGHJKLMNPRTVWXYZ0-9]{7}[0-9]\b",
            0.4,
        ),
    ]

    CONTEXT = [
        "reisepass",
        "pass",
        "passnummer",
        "reisepassnummer",
        "passport",
        "passport number",
        "pass-nr",
        "dokumentennummer",
        "bundesrepublik deutschland",
        "ausweisdokument",
        "mrz",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_PASSPORT",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the ICAO Doc 9303 check digit at position 9.

        Algorithm source: ICAO Doc 9303 Part 3 — Machine Readable Travel
        Documents. Weights 7, 3, 1 repeating applied to positions 1–8 with
        letters mapped A=10 … Z=35; the sum modulo 10 must equal the digit
        at position 9.

        :param pattern_text: the text to validate (9 characters)
        :return: True if check digit is valid, False otherwise
        """
        pattern_text = pattern_text.upper().strip()

        if len(pattern_text) != 9 or not pattern_text[-1].isdigit():
            return False

        # ICAO Doc 9303 excludes these visually-ambiguous letters from
        # travel-document serial numbers. Reject outright so the weighted
        # checksum cannot accidentally mark a non-ICAO string as valid.
        forbidden = set("ABDEIOQSU")
        if any(c in forbidden for c in pattern_text[:-1]):
            return False

        weights = [7, 3, 1]
        total = 0
        for i, c in enumerate(pattern_text[:-1]):
            if c.isdigit():
                value = int(c)
            elif "A" <= c <= "Z":
                value = ord(c) - ord("A") + 10
            else:
                return False
            total += value * weights[i % 3]

        return (total % 10) == int(pattern_text[-1])

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the ICAO Doc 9303 check digit at position 9.

Algorithm source: ICAO Doc 9303 Part 3 — Machine Readable Travel Documents. Weights 7, 3, 1 repeating applied to positions 1–8 with letters mapped A=10 … Z=35; the sum modulo 10 must equal the digit at position 9.

PARAMETER DESCRIPTION
pattern_text

the text to validate (9 characters)

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True if check digit is valid, False otherwise

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_passport_recognizer.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the ICAO Doc 9303 check digit at position 9.

    Algorithm source: ICAO Doc 9303 Part 3 — Machine Readable Travel
    Documents. Weights 7, 3, 1 repeating applied to positions 1–8 with
    letters mapped A=10 … Z=35; the sum modulo 10 must equal the digit
    at position 9.

    :param pattern_text: the text to validate (9 characters)
    :return: True if check digit is valid, False otherwise
    """
    pattern_text = pattern_text.upper().strip()

    if len(pattern_text) != 9 or not pattern_text[-1].isdigit():
        return False

    # ICAO Doc 9303 excludes these visually-ambiguous letters from
    # travel-document serial numbers. Reject outright so the weighted
    # checksum cannot accidentally mark a non-ICAO string as valid.
    forbidden = set("ABDEIOQSU")
    if any(c in forbidden for c in pattern_text[:-1]):
        return False

    weights = [7, 3, 1]
    total = 0
    for i, c in enumerate(pattern_text[:-1]):
        if c.isdigit():
            value = int(c)
        elif "A" <= c <= "Z":
            value = ord(c) - ord("A") + 10
        else:
            return False
        total += value * weights[i % 3]

    return (total % 10) == int(pattern_text[-1])

DePlzRecognizer

Bases: PatternRecognizer

Recognizes German postal codes (Postleitzahl, PLZ).

German postal codes consist of exactly 5 digits in the range 01001–99998, assigned by Deutsche Post AG. A PLZ alone is generally not sufficient to identify a specific natural person and is therefore not directly personal data under DSGVO Art. 4 Nr. 1. However, in combination with other data (e.g., street address, name), it contributes to identifying an individual, and in very rural areas a single PLZ may cover only a handful of addresses.

Legal basis: DSGVO Art. 4 Nr. 1 in combination with other address data; BDSG.

Format: 5 digits, 01001–99998 (boundary values 01000 and 99999 are excluded). Examples: 10115 (Berlin Mitte), 80331 (München), 22085 (Hamburg)

!! ACCURACY WARNING !! The pattern [0-9]{5} is extremely generic and will produce a very high number of false positives when used without context (e.g., year numbers, prices, order IDs, phone number fragments, reference numbers). The base confidence is therefore set to 0.05 – the recognizer is only actionable when strong context words such as "PLZ", "Postleitzahl" or "Postanschrift" are present nearby. This recognizer should only be enabled in pipelines where German address data is expected. Formal accuracy evaluation has not been performed on a labelled dataset.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_PLZ'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_plz_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class DePlzRecognizer(PatternRecognizer):
    """
    Recognizes German postal codes (Postleitzahl, PLZ).

    German postal codes consist of exactly 5 digits in the range 01001–99998,
    assigned by Deutsche Post AG. A PLZ alone is generally not sufficient to
    identify a specific natural person and is therefore not directly personal data
    under DSGVO Art. 4 Nr. 1. However, in combination with other data (e.g., street
    address, name), it contributes to identifying an individual, and in very rural
    areas a single PLZ may cover only a handful of addresses.

    Legal basis: DSGVO Art. 4 Nr. 1 in combination with other address data; BDSG.

    Format:
        5 digits, 01001–99998 (boundary values 01000 and 99999 are excluded).
        Examples: 10115 (Berlin Mitte), 80331 (München), 22085 (Hamburg)

    !! ACCURACY WARNING !!
    The pattern `[0-9]{5}` is extremely generic and will produce a very high number
    of false positives when used without context (e.g., year numbers, prices, order
    IDs, phone number fragments, reference numbers). The base confidence is
    therefore set to 0.05 – the recognizer is only actionable when strong context
    words such as "PLZ", "Postleitzahl" or "Postanschrift" are present nearby.
    This recognizer should only be enabled in pipelines where German address data
    is expected. Formal accuracy evaluation has not been performed on a labelled
    dataset.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    # Regex covers 01001–09999 (leading zero) and 10000–99998.
    # Does NOT match 00000 (not a valid PLZ), 01000, 99999, or 6-digit numbers.
    # Reference: Deutsche Post AG PLZ-Verzeichnis.
    PATTERNS = [
        Pattern(
            "Postleitzahl (5 digits, very low base confidence – context required)",
            r"\b(?!01000\b|99999\b)(0[1-9]\d{3}|[1-9]\d{4})\b",
            0.05,
        ),
    ]

    CONTEXT = [
        "plz",
        "postleitzahl",
        "postanschrift",
        "adresse",
        "wohnort",
        "ort",
        "wohnanschrift",
        "lieferadresse",
        "rechnungsadresse",
        "straße",
        "strasse",
        "hausnummer",
        "postfach",
        "bundesland",
        "gemeinde",
        "stadt",
        "dorf",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_PLZ",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

DeSocialSecurityRecognizer

Bases: PatternRecognizer

Recognizes German Rentenversicherungsnummer (RVNR / Sozialversicherungsnummer).

The Rentenversicherungsnummer (also called Versicherungsnummer or RVNR) is a unique 12-character identifier assigned to every person insured under the German statutory pension insurance scheme (gesetzliche Rentenversicherung). It encodes date of birth, gender information, and a serial number.

Legal basis: § 147 SGB VI (Sozialgesetzbuch Sechstes Buch – Gesetzliche Rentenversicherung), § 33a SGB I. Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

Format (12 characters): Pos 1–2: Bereichsnummer (2 digits, issuing regional office code, 01–99) Pos 3–4: Geburtstag (birth day, 01–31; or 51–81 with +50 Ergänzungsmerkmal to disambiguate otherwise identical numbers — gender-agnostic) Pos 5–6: Geburtsmonat (birth month, 01–12) Pos 7–8: Geburtsjahr (last 2 digits of birth year) Pos 9: Buchstabenkennung (first letter of birth surname, A–Z) Pos 10–11: Seriennummer / Geschlechtskennung (00–49 male, 50–99 female) Pos 12: Prüfziffer (check digit)

Example: 15070649C103 (canonical example, DRV technical documentation)

Check digit algorithm (VKVV § 4 / Deutsche Rentenversicherung): 1. Replace the letter at position 9 with its 2-digit ordinal value (A=01, B=02, …, Z=26). This yields 12 data digits (positions 1–8 of the original, plus 2 letter-ordinal digits, plus positions 10–11) followed by the check digit at position 12. 2. Apply weights [2, 1, 2, 5, 7, 1, 2, 1, 2, 1, 2, 1] to the 12 data digits (the check digit itself is not part of the weighted sum). 3. For each product, take the cross-sum (Quersumme) of its digits (products < 10 remain unchanged; e.g. 35 → 3+5 = 8). 4. Sum all 12 cross-sums, compute sum mod 10. 5. The result must equal the check digit at position 12.

Worked example for 15070649C103: effective = '15070649' + '03' (C=03) + '10' = '150706490310' × weights = 2,5,0,35,0,6,8,9,0,3,2,0 cross-sums = 2,5,0, 8,0,6,8,9,0,3,2,0 = 43 43 mod 10 = 3 → matches check digit '3'

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_SOCIAL_SECURITY'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the Rentenversicherungsnummer using the VKVV § 4 checksum.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_social_security_recognizer.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
class DeSocialSecurityRecognizer(PatternRecognizer):
    """
    Recognizes German Rentenversicherungsnummer (RVNR / Sozialversicherungsnummer).

    The Rentenversicherungsnummer (also called Versicherungsnummer or RVNR) is a
    unique 12-character identifier assigned to every person insured under the German
    statutory pension insurance scheme (gesetzliche Rentenversicherung). It encodes
    date of birth, gender information, and a serial number.

    Legal basis: § 147 SGB VI (Sozialgesetzbuch Sechstes Buch – Gesetzliche
    Rentenversicherung), § 33a SGB I.
    Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

    Format (12 characters):
        Pos 1–2:   Bereichsnummer (2 digits, issuing regional office code, 01–99)
        Pos 3–4:   Geburtstag (birth day, 01–31; or 51–81 with +50
                   Ergänzungsmerkmal to disambiguate otherwise identical
                   numbers — gender-agnostic)
        Pos 5–6:   Geburtsmonat (birth month, 01–12)
        Pos 7–8:   Geburtsjahr (last 2 digits of birth year)
        Pos 9:     Buchstabenkennung (first letter of birth surname, A–Z)
        Pos 10–11: Seriennummer / Geschlechtskennung (00–49 male,
                   50–99 female)
        Pos 12:    Prüfziffer (check digit)

    Example: 15070649C103 (canonical example, DRV technical documentation)

    Check digit algorithm (VKVV § 4 / Deutsche Rentenversicherung):
        1. Replace the letter at position 9 with its 2-digit ordinal value
           (A=01, B=02, …, Z=26). This yields 12 data digits (positions
           1–8 of the original, plus 2 letter-ordinal digits, plus
           positions 10–11) followed by the check digit at position 12.
        2. Apply weights [2, 1, 2, 5, 7, 1, 2, 1, 2, 1, 2, 1] to the 12
           data digits (the check digit itself is not part of the
           weighted sum).
        3. For each product, take the cross-sum (Quersumme) of its digits
           (products < 10 remain unchanged; e.g. 35 → 3+5 = 8).
        4. Sum all 12 cross-sums, compute sum mod 10.
        5. The result must equal the check digit at position 12.

    Worked example for 15070649C103:
        effective = '15070649' + '03' (C=03) + '10' = '150706490310'
        × weights = 2,5,0,35,0,6,8,9,0,3,2,0
        cross-sums = 2,5,0, 8,0,6,8,9,0,3,2,0 = 43
        43 mod 10 = 3 → matches check digit '3'

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "Rentenversicherungsnummer (Strict, with birth date structure)",
            r"\b\d{2}"
            r"(0[1-9]|[12]\d|3[01]|5[1-9]|[67]\d|8[01])"  # day: 01-31 or 51-81
            r"(0[1-9]|1[0-2])"                              # month 01-12
            r"\d{2}"                                  # year
            r"[A-Z]"                                  # surname initial
            r"\d{2}"                                  # serial
            r"[0-9]\b",                               # check digit
            0.5,
        ),
        Pattern(
            "Rentenversicherungsnummer (Relaxed)",
            r"\b\d{8}[A-Z]\d{3}\b",
            0.3,
        ),
    ]

    CONTEXT = [
        "rentenversicherungsnummer",
        "sozialversicherungsnummer",
        "versicherungsnummer",
        "rvnr",
        "svnr",
        "sv-nummer",
        "rente",
        "rentenversicherung",
        "deutsche rentenversicherung",
        "drv",
        "sozialversicherung",
        "sozialversicherungsausweis",
        "rentenausweis",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_SOCIAL_SECURITY",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the Rentenversicherungsnummer using the VKVV § 4 checksum.

        Algorithm source: Verordnung über die Vergabe der Versicherungsnummer
        (VKVV) § 4, Deutsche Rentenversicherung technical specification.

        Additionally enforces the birth-day (01–31 or 51–81 with +50
        Ergänzungsmerkmal) and birth-month (01–12) ranges from the spec
        so that a relaxed-pattern match with an impossible date cannot
        be promoted to MAX_SCORE by a lucky checksum collision.

        :param pattern_text: the text to validate (12 characters)
        :return: True if valid, False if invalid
        """
        pattern_text = pattern_text.upper().strip()

        if len(pattern_text) != 12:
            return False

        if not re.match(r"^\d{8}[A-Z]\d{3}$", pattern_text):
            return False

        # VKVV § 4: Pos 3–4 encodes the birth day (01–31, or 51–81 with
        # +50 Ergänzungsmerkmal); Pos 5–6 the birth month (01–12). These
        # are structural invariants of a real RVNR — enforce them here so
        # that a relaxed-pattern match with an impossible date cannot be
        # promoted to MAX_SCORE by a lucky checksum.
        day = int(pattern_text[2:4])
        month = int(pattern_text[4:6])
        if not (1 <= day <= 31 or 51 <= day <= 81):
            return False
        if not 1 <= month <= 12:
            return False

        letter = pattern_text[8]
        letter_val = str(ord(letter) - ord("A") + 1).zfill(2)

        effective = pattern_text[:8] + letter_val + pattern_text[9:11]

        check_digit = int(pattern_text[11])
        weights = [2, 1, 2, 5, 7, 1, 2, 1, 2, 1, 2, 1]

        total = 0
        for digit_char, weight in zip(effective, weights):
            product = int(digit_char) * weight
            total += (product // 10) + (product % 10)

        return (total % 10) == check_digit

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the Rentenversicherungsnummer using the VKVV § 4 checksum.

Algorithm source: Verordnung über die Vergabe der Versicherungsnummer (VKVV) § 4, Deutsche Rentenversicherung technical specification.

Additionally enforces the birth-day (01–31 or 51–81 with +50 Ergänzungsmerkmal) and birth-month (01–12) ranges from the spec so that a relaxed-pattern match with an impossible date cannot be promoted to MAX_SCORE by a lucky checksum collision.

PARAMETER DESCRIPTION
pattern_text

the text to validate (12 characters)

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True if valid, False if invalid

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_social_security_recognizer.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the Rentenversicherungsnummer using the VKVV § 4 checksum.

    Algorithm source: Verordnung über die Vergabe der Versicherungsnummer
    (VKVV) § 4, Deutsche Rentenversicherung technical specification.

    Additionally enforces the birth-day (01–31 or 51–81 with +50
    Ergänzungsmerkmal) and birth-month (01–12) ranges from the spec
    so that a relaxed-pattern match with an impossible date cannot
    be promoted to MAX_SCORE by a lucky checksum collision.

    :param pattern_text: the text to validate (12 characters)
    :return: True if valid, False if invalid
    """
    pattern_text = pattern_text.upper().strip()

    if len(pattern_text) != 12:
        return False

    if not re.match(r"^\d{8}[A-Z]\d{3}$", pattern_text):
        return False

    # VKVV § 4: Pos 3–4 encodes the birth day (01–31, or 51–81 with
    # +50 Ergänzungsmerkmal); Pos 5–6 the birth month (01–12). These
    # are structural invariants of a real RVNR — enforce them here so
    # that a relaxed-pattern match with an impossible date cannot be
    # promoted to MAX_SCORE by a lucky checksum.
    day = int(pattern_text[2:4])
    month = int(pattern_text[4:6])
    if not (1 <= day <= 31 or 51 <= day <= 81):
        return False
    if not 1 <= month <= 12:
        return False

    letter = pattern_text[8]
    letter_val = str(ord(letter) - ord("A") + 1).zfill(2)

    effective = pattern_text[:8] + letter_val + pattern_text[9:11]

    check_digit = int(pattern_text[11])
    weights = [2, 1, 2, 5, 7, 1, 2, 1, 2, 1, 2, 1]

    total = 0
    for digit_char, weight in zip(effective, weights):
        product = int(digit_char) * weight
        total += (product // 10) + (product % 10)

    return (total % 10) == check_digit

DeTaxIdRecognizer

Bases: PatternRecognizer

Recognizes German Steueridentifikationsnummer (Steuer-IdNr.).

The Steueridentifikationsnummer is a unique 11-digit personal tax identification number issued by the Bundeszentralamt für Steuern to every person registered in Germany. It does not change over a person's lifetime.

Legal basis: §§ 139a–139e Abgabenordnung (AO), in force since 2007. Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

Format: - 11 digits - First digit: 1–9 (never 0) - Digits 1–10: each digit may appear at most three times (BZSt rule in force since the 2016 format revision; previously exactly one digit repeated twice or three times and all others appeared exactly once). - Digit 11: check digit (ISO 7064 Mod 11, 10 variant)

Examples (fictitious): 86095742719, 12345678903

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_TAX_ID'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the Steueridentifikationsnummer using the official checksum algorithm.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_tax_id_recognizer.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
class DeTaxIdRecognizer(PatternRecognizer):
    """
    Recognizes German Steueridentifikationsnummer (Steuer-IdNr.).

    The Steueridentifikationsnummer is a unique 11-digit personal tax identification
    number issued by the Bundeszentralamt für Steuern to every person registered in
    Germany. It does not change over a person's lifetime.

    Legal basis: §§ 139a–139e Abgabenordnung (AO), in force since 2007.
    Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

    Format:
        - 11 digits
        - First digit: 1–9 (never 0)
        - Digits 1–10: each digit may appear at most three times (BZSt rule
          in force since the 2016 format revision; previously exactly one
          digit repeated twice or three times and all others appeared
          exactly once).
        - Digit 11: check digit (ISO 7064 Mod 11, 10 variant)

    Examples (fictitious): 86095742719, 12345678903

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "Steueridentifikationsnummer (High)",
            r"\b[1-9]\d{10}\b",
            0.5,
        ),
    ]

    CONTEXT = [
        "steueridentifikationsnummer",
        "steuer-id",
        "steuerid",
        "steuerliche identifikationsnummer",
        "steuerliche identifikation",
        "persönliche identifikationsnummer",
        "steuer identifikation",
        "idnr",
        "steuer-idnr",
        "steuernummer",
        "bzst",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_TAX_ID",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the Steueridentifikationsnummer using the official checksum algorithm.

        Algorithm: ISO 7064 Mod 11, 10 variant as specified by the
        Bundeszentralamt für Steuern.

        :param pattern_text: the text to validate (11 digits)
        :return: True if valid, False if invalid
        """
        if len(pattern_text) != 11 or not pattern_text.isdigit():
            return False
        if pattern_text[0] == "0":
            return False

        digits = [int(d) for d in pattern_text]

        # Post-2016 BZSt rule: no digit may appear more than three times in
        # positions 1-10. Also rejects the all-identical case that the
        # pre-2016 rule forbade.
        if max(Counter(digits[:10]).values()) > 3:
            return False

        # ISO 7064 Mod 11, 10 checksum
        product = 10
        for i in range(10):
            total = (digits[i] + product) % 10
            if total == 0:
                total = 10
            product = (total * 2) % 11

        check = 11 - product
        if check == 10:
            check = 0

        return check == digits[10]

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the Steueridentifikationsnummer using the official checksum algorithm.

Algorithm: ISO 7064 Mod 11, 10 variant as specified by the Bundeszentralamt für Steuern.

PARAMETER DESCRIPTION
pattern_text

the text to validate (11 digits)

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True if valid, False if invalid

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_tax_id_recognizer.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the Steueridentifikationsnummer using the official checksum algorithm.

    Algorithm: ISO 7064 Mod 11, 10 variant as specified by the
    Bundeszentralamt für Steuern.

    :param pattern_text: the text to validate (11 digits)
    :return: True if valid, False if invalid
    """
    if len(pattern_text) != 11 or not pattern_text.isdigit():
        return False
    if pattern_text[0] == "0":
        return False

    digits = [int(d) for d in pattern_text]

    # Post-2016 BZSt rule: no digit may appear more than three times in
    # positions 1-10. Also rejects the all-identical case that the
    # pre-2016 rule forbade.
    if max(Counter(digits[:10]).values()) > 3:
        return False

    # ISO 7064 Mod 11, 10 checksum
    product = 10
    for i in range(10):
        total = (digits[i] + product) % 10
        if total == 0:
            total = 10
        product = (total * 2) % 11

    check = 11 - product
    if check == 10:
        check = 0

    return check == digits[10]

DeTaxNumberRecognizer

Bases: PatternRecognizer

Recognizes German Steuernummer using regex.

The Steuernummer is a tax number assigned by the local Finanzamt (tax office) to individuals and businesses. Unlike the Steueridentifikationsnummer, it can change (e.g., upon moving to a different Finanzamt district).

Legal basis: § 139a Abgabenordnung (AO). Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

Formats: - ELSTER unified (bundeseinheitlich, 13 digits): BB FFF UUUUU P → 2-digit Bundesland code (01–16) + 11 digits Example: 2181508150X → normalised as 02181508150X - State-specific human-readable (with slashes/spaces): NW: 123/4567/8901 (3/4/4 digits) BY: 123/456/78901 (3/3/5 digits) BE: 12/345/67890 (2/3/5 digits) HH: 12/345/67890 (2/3/5 digits)

Bundesland codes (ELSTER): 01=SH, 02=HH, 03=NI, 04=HB, 05=NW, 06=HE, 07=RP, 08=BW, 09=BY, 10=SL, 11=BE, 12=BB, 13=MV, 14=SN, 15=ST, 16=TH

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_TAX_NUMBER'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_tax_number_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class DeTaxNumberRecognizer(PatternRecognizer):
    """
    Recognizes German Steuernummer using regex.

    The Steuernummer is a tax number assigned by the local Finanzamt (tax office)
    to individuals and businesses. Unlike the Steueridentifikationsnummer, it can
    change (e.g., upon moving to a different Finanzamt district).

    Legal basis: § 139a Abgabenordnung (AO).
    Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG.

    Formats:
        - ELSTER unified (bundeseinheitlich, 13 digits):
            BB FFF UUUUU P  →  2-digit Bundesland code (01–16) + 11 digits
            Example: 2181508150X → normalised as 02181508150X
        - State-specific human-readable (with slashes/spaces):
            NW:  123/4567/8901  (3/4/4 digits)
            BY:  123/456/78901  (3/3/5 digits)
            BE:  12/345/67890   (2/3/5 digits)
            HH:  12/345/67890   (2/3/5 digits)

    Bundesland codes (ELSTER):
        01=SH, 02=HH, 03=NI, 04=HB, 05=NW, 06=HE, 07=RP,
        08=BW, 09=BY, 10=SL, 11=BE, 12=BB, 13=MV, 14=SN, 15=ST, 16=TH

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "de"

    PATTERNS = [
        Pattern(
            "Steuernummer ELSTER (bundeseinheitlich, 13-stellig)",
            r"\b(0[1-9]|1[0-6])\d{11}\b",
            0.5,
        ),
        Pattern(
            "Steuernummer mit Schrägstrich (Bayern/BW: 3/3/5)",
            r"(?<!\w)\d{3}/\d{3}/\d{5}(?!\w)",
            0.4,
        ),
        Pattern(
            "Steuernummer mit Schrägstrich (NW: 3/4/4 oder allgemein 2-3/3-4/4-5)",
            r"(?<!\w)\d{2,3}/\d{3,4}/\d{4,5}(?!\w)",
            0.2,
        ),
    ]

    CONTEXT = [
        "steuernummer",
        "steuer-nr",
        "steuer nr",
        "st.-nr",
        "st-nr",
        "finanzamt",
        "umsatzsteuer",
        "einkommensteuer",
        "körperschaftsteuer",
        "gewerbesteuer",
        "steuerveranlagung",
        "steuerbescheid",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_TAX_NUMBER",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

DeVatIdRecognizer

Bases: PatternRecognizer

Recognizes German Umsatzsteuer-Identifikationsnummer (USt-IdNr.).

The USt-IdNr. is issued by the Bundeszentralamt für Steuern (BZSt) to VAT-registered businesses and self-employed persons in Germany. It is used on invoices and cross-border EU transactions. While primarily a business identifier, it can identify sole traders and freelancers (natural persons) and may therefore constitute personal data under DSGVO Art. 4 Nr. 1 when linked to an individual.

Legal basis: § 27a UStG (Umsatzsteuergesetz). Format documentation: BZSt (Bundeszentralamt für Steuern). Data protection: DSGVO Art. 4 Nr. 1 (if linked to a natural person), BDSG.

Format (11 characters after normalisation): "DE" + 9 digits, where the 9th digit is conventionally a check digit.

Real-world formatting on invoices and Impressum pages varies: DE123456789, DE 123456789, DE-123-456-789, DE 123 456 789, de123456789, DE.123.456.789. The recognizer matches all of these via a lenient pattern and normalises them (uppercase, strip whitespace/dashes/dots) before applying the structural check.

Check-digit policy (IMPORTANT — heuristic, not normative): The BZSt does NOT publish the USt-IdNr. Prüfziffer algorithm in any Merkblatt or normative document. The ISO 7064 Mod 11,10 implemented here is the de-facto community consensus (python-stdnum, VIES- adjacent validators) and matches every officially-publicised test vector the authors are aware of. It is empirically reliable for the modern digit ranges used by BZSt but has no normative status.

Rejection policy therefore deliberately errs on the side of keeping
matches rather than dropping them:

  - Structural failure  (wrong prefix, wrong length after
    normalisation, non-digit body)  → return False (match dropped).
    This is safe — no spec ambiguity.
  - Checksum PASS        → return True (max_score, high confidence).
  - Checksum FAIL        → depends on ``strict_checksum`` parameter:
      * default  (strict_checksum=False): return None. Match keeps
        its base pattern score. A real USt-IdNr that happens to
        fail the heuristic is NEVER silently dropped.
      * strict  (strict_checksum=True):  return False (match
        dropped). Use when false-positive reduction matters more
        than false-negative prevention.

The default is the enterprise-safe choice: preserves recall on an
identifier whose authoritative validation path is BZSt/VIES, not
a locally-implemented checksum.
PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'de'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DE_VAT_ID'

strict_checksum

When True, treat the ISO 7064 Mod 11,10 check as authoritative and drop matches that fail it. Default False (heuristic mode — see policy above).

TYPE: bool DEFAULT: False

name

Optional recognizer instance name

TYPE: Optional[str] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the USt-IdNr. after real-world-tolerant normalisation.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_vat_id_recognizer.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
class DeVatIdRecognizer(PatternRecognizer):
    """
    Recognizes German Umsatzsteuer-Identifikationsnummer (USt-IdNr.).

    The USt-IdNr. is issued by the Bundeszentralamt für Steuern (BZSt) to
    VAT-registered businesses and self-employed persons in Germany.  It is
    used on invoices and cross-border EU transactions.  While primarily a
    business identifier, it can identify sole traders and freelancers (natural
    persons) and may therefore constitute personal data under DSGVO Art. 4
    Nr. 1 when linked to an individual.

    Legal basis: § 27a UStG (Umsatzsteuergesetz).
    Format documentation: BZSt (Bundeszentralamt für Steuern).
    Data protection: DSGVO Art. 4 Nr. 1 (if linked to a natural person), BDSG.

    Format (11 characters after normalisation):
        "DE" + 9 digits, where the 9th digit is conventionally a check digit.

    Real-world formatting on invoices and Impressum pages varies:
      DE123456789, DE 123456789, DE-123-456-789, DE 123 456 789, de123456789,
      DE.123.456.789.  The recognizer matches all of these via a lenient
      pattern and normalises them (uppercase, strip whitespace/dashes/dots)
      before applying the structural check.

    Check-digit policy (IMPORTANT — heuristic, not normative):
        The BZSt does NOT publish the USt-IdNr. Prüfziffer algorithm in any
        Merkblatt or normative document. The ISO 7064 Mod 11,10 implemented
        here is the de-facto community consensus (``python-stdnum``, VIES-
        adjacent validators) and matches every officially-publicised test
        vector the authors are aware of. It is empirically reliable for the
        modern digit ranges used by BZSt but has no normative status.

        Rejection policy therefore deliberately errs on the side of keeping
        matches rather than dropping them:

          - Structural failure  (wrong prefix, wrong length after
            normalisation, non-digit body)  → return False (match dropped).
            This is safe — no spec ambiguity.
          - Checksum PASS        → return True (max_score, high confidence).
          - Checksum FAIL        → depends on ``strict_checksum`` parameter:
              * default  (strict_checksum=False): return None. Match keeps
                its base pattern score. A real USt-IdNr that happens to
                fail the heuristic is NEVER silently dropped.
              * strict  (strict_checksum=True):  return False (match
                dropped). Use when false-positive reduction matters more
                than false-negative prevention.

        The default is the enterprise-safe choice: preserves recall on an
        identifier whose authoritative validation path is BZSt/VIES, not
        a locally-implemented checksum.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param strict_checksum: When True, treat the ISO 7064 Mod 11,10 check
        as authoritative and drop matches that fail it. Default False
        (heuristic mode — see policy above).
    :param name: Optional recognizer instance name
    """

    COUNTRY_CODE = "de"

    # Matches in order: continuous form (high confidence), grouped /
    # separator form (slightly lower because separators are rarer). Both
    # are routed through the same validate_result() which normalises first.
    PATTERNS = [
        Pattern(
            "Umsatzsteuer-Identifikationsnummer USt-IdNr. (DE + 9 digits)",
            r"\bDE\d{9}\b",
            0.5,
        ),
        Pattern(
            "Umsatzsteuer-Identifikationsnummer USt-IdNr. (with separators)",
            r"\bDE[\s.\-]?\d{3}[\s.\-]?\d{3}[\s.\-]?\d{3}\b",
            0.4,
        ),
    ]

    CONTEXT = [
        "umsatzsteuer-identifikationsnummer",
        "umsatzsteueridentifikationsnummer",
        "ust-idnr",
        "ust-id",
        "ustidnr",
        "umsatzsteuer-id",
        "mehrwertsteuer",
        "vat",
        "vat-id",
        "vat id",
        "steueridentifikation",
        "bzst",
        "bundeszentralamt für steuern",
        "finanzamt",
        "invoice",
        "rechnung",
    ]

    # Characters stripped during normalisation. Covers the common real-world
    # formatting variants: "DE 123 456 789", "DE-123-456-789",
    # "DE.123.456.789", and any mixture thereof.
    _NORMALIZATION_STRIP = re.compile(r"[\s.\-]")

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "de",
        supported_entity: str = "DE_VAT_ID",
        strict_checksum: bool = False,
        name: Optional[str] = None,
    ):
        self.strict_checksum = strict_checksum
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the USt-IdNr. after real-world-tolerant normalisation.

        Returns are tri-state to reflect spec uncertainty (see class
        docstring):

          True   — structural check + ISO 7064 Mod 11,10 checksum pass.
          False  — structural check failed, OR checksum failed in strict
                   mode.
          None   — structural check passed but checksum failed in the
                   default (non-strict) mode: the match keeps its pattern
                   score rather than being silently dropped.

        :param pattern_text: the raw matched text (possibly with spaces,
            dashes, dots and mixed case).
        :return: True / False / None per the semantics above.
        """
        # Normalise: uppercase, drop separators. Covers real-world formatting
        # such as "DE 123 456 789", "de-123-456-789", "DE.123.456.789".
        normalized = self._NORMALIZATION_STRIP.sub("", pattern_text.upper())

        # Structural checks — unambiguous, safe to reject outright.
        if len(normalized) != 11 or not normalized.startswith("DE"):
            return False

        digits = normalized[2:]
        if not digits.isdigit():
            return False

        # Heuristic check digit (ISO 7064 Mod 11,10). Not published by BZSt.
        # See class docstring for the rejection policy rationale.
        product = 10
        for i in range(8):
            total = (int(digits[i]) + product) % 10
            if total == 0:
                total = 10
            product = (total * 2) % 11

        check = 11 - product
        if check == 10:
            check = 0

        if check == int(digits[8]):
            return True

        # Checksum mismatch: strict mode rejects, default mode abstains.
        # Default mode (None) preserves recall against the real-world risk
        # that BZSt uses a wider algorithm than the community consensus.
        return False if self.strict_checksum else None

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the USt-IdNr. after real-world-tolerant normalisation.

Returns are tri-state to reflect spec uncertainty (see class docstring):

True — structural check + ISO 7064 Mod 11,10 checksum pass. False — structural check failed, OR checksum failed in strict mode. None — structural check passed but checksum failed in the default (non-strict) mode: the match keeps its pattern score rather than being silently dropped.

PARAMETER DESCRIPTION
pattern_text

the raw matched text (possibly with spaces, dashes, dots and mixed case).

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True / False / None per the semantics above.

Source code in presidio_analyzer/predefined_recognizers/country_specific/germany/de_vat_id_recognizer.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the USt-IdNr. after real-world-tolerant normalisation.

    Returns are tri-state to reflect spec uncertainty (see class
    docstring):

      True   — structural check + ISO 7064 Mod 11,10 checksum pass.
      False  — structural check failed, OR checksum failed in strict
               mode.
      None   — structural check passed but checksum failed in the
               default (non-strict) mode: the match keeps its pattern
               score rather than being silently dropped.

    :param pattern_text: the raw matched text (possibly with spaces,
        dashes, dots and mixed case).
    :return: True / False / None per the semantics above.
    """
    # Normalise: uppercase, drop separators. Covers real-world formatting
    # such as "DE 123 456 789", "de-123-456-789", "DE.123.456.789".
    normalized = self._NORMALIZATION_STRIP.sub("", pattern_text.upper())

    # Structural checks — unambiguous, safe to reject outright.
    if len(normalized) != 11 or not normalized.startswith("DE"):
        return False

    digits = normalized[2:]
    if not digits.isdigit():
        return False

    # Heuristic check digit (ISO 7064 Mod 11,10). Not published by BZSt.
    # See class docstring for the rejection policy rationale.
    product = 10
    for i in range(8):
        total = (int(digits[i]) + product) % 10
        if total == 0:
            total = 10
        product = (total * 2) % 11

    check = 11 - product
    if check == 10:
        check = 0

    if check == int(digits[8]):
        return True

    # Checksum mismatch: strict mode rejects, default mode abstains.
    # Default mode (None) preserves recall against the real-world risk
    # that BZSt uses a wider algorithm than the community consensus.
    return False if self.strict_checksum else None

InVehicleRegistrationRecognizer

Bases: PatternRecognizer

Recognizes Indian Vehicle Registration Number issued by RTO.

Reference(s): https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_India https://en.wikipedia.org/wiki/Regional_Transport_Office https://en.wikipedia.org/wiki/List_of_Regional_Transport_Office_districts_in_India

The registration scheme changed over time with multiple formats in play over the years India has multiple active patterns for registration plates issued to different vehicle categories

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IN_VEHICLE_REGISTRATION'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input e.g. by removing dashes or spaces

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Determine absolute value based on calculation.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_vehicle_registration_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
class InVehicleRegistrationRecognizer(PatternRecognizer):
    """
    Recognizes Indian Vehicle Registration Number issued by RTO.

    Reference(s):
    https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_India
    https://en.wikipedia.org/wiki/Regional_Transport_Office
    https://en.wikipedia.org/wiki/List_of_Regional_Transport_Office_districts_in_India

    The registration scheme changed over time with multiple formats
    in play over the years
    India has multiple active patterns for registration plates issued to different
    vehicle categories

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
        for different strings to be used during pattern matching.
    This can allow a greater variety in input e.g. by removing dashes or spaces
    """

    COUNTRY_CODE = "in"

    PATTERNS = [
        Pattern(
            "India Vehicle Registration (Very Weak)",
            r"\b[A-Z]{1}(?!0000)[0-9]{4}\b",
            0.01,
        ),
        Pattern(
            "India Vehicle Registration (Very Weak)",
            r"\b[A-Z]{2}(?!0000)\d{4}\b",
            0.01,
        ),
        Pattern(
            "India Vehicle Registration (Very Weak)",
            r"\b(I)(?!00000)\d{5}\b",
            0.01,
        ),
        Pattern(
            "India Vehicle Registration (Weak)",
            r"\b[A-Z]{3}(?!0000)\d{4}\b",
            0.20,
        ),
        Pattern(
            "India Vehicle Registration (Medium)",
            r"\b\d{1,3}(CD|CC|UN)[1-9]{1}[0-9]{1,3}\b",
            0.40,
        ),
        Pattern(
            "India Vehicle Registration",
            r"\b[A-Z]{2}\d{1}[A-Z]{1,3}(?!0000)\d{4}\b",
            0.50,
        ),
        Pattern(
            "India Vehicle Registration",
            r"\b[A-Z]{2}\d{2}[A-Z]{1,2}(?!0000)\d{4}\b",
            0.50,
        ),
        Pattern(
            "India Vehicle Registration",
            r"\b[2-9]{1}[1-9]{1}(BH)(?!0000)\d{4}[A-HJ-NP-Z]{2}\b",
            0.85,
        ),
        Pattern(
            "India Vehicle Registration",
            r"\b(?!00)\d{2}(A|B|C|D|E|F|H|K|P|R|X)\d{6}[A-Z]{1}\b",
            0.85,
        ),
    ]

    CONTEXT = ["RTO", "vehicle", "plate", "registration"]

    # fmt: off
    in_vehicle_foreign_mission_codes = {
        84, 85, 89, 93, 94, 95, 97, 98, 99, 102, 104, 105, 106, 109, 111, 112,
        113, 117, 119, 120, 121, 122, 123, 125, 126, 128, 133, 134, 135, 137,
        141, 145, 147, 149, 152, 153, 155, 156, 157, 159, 160
    }

    in_vehicle_armed_forces_codes = {
        'A', 'B', 'C', 'D', 'E', 'F', 'H', 'K', 'P', 'R', 'X'}
    in_vehicle_diplomatic_codes = {"CC", "CD", "UN"}
    in_vehicle_dist_an = {"01"}
    in_vehicle_dist_ap = {"39", "40"}
    in_vehicle_dist_ar = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "19", "20", "22"
    }
    in_vehicle_dist_as = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34"
    }
    in_vehicle_dist_br = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "19",
        "21", "22", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33",
        "34", "37", "38", "39", "43", "44", "45", "46", "50", "51", "52", "53",
        "55", "56"
    }
    in_vehicle_dist_cg = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24",
        "25", "26", "27", "28", "29", "30"
    }
    in_vehicle_dist_ch = {"01", "02", "03", "04"}
    in_vehicle_dist_dd = {"01", "02", "03"}
    in_vehicle_dist_dn = {"09"}  # old list
    in_vehicle_dist_dl = {
        "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"}
    in_vehicle_dist_ga = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"}
    in_vehicle_dist_gj = {
        "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13",
        "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39"
    }
    in_vehicle_dist_hp = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73",
        "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85",
        "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
        "98", "99"
    }
    in_vehicle_dist_hr = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73",
        "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85",
        "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
        "98", "99"
    }
    in_vehicle_dist_jh = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24"
    }
    in_vehicle_dist_jk = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22"
    }
    in_vehicle_dist_ka = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71"
    }
    in_vehicle_dist_kl = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73",
        "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85",
        "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
        "98", "99"
    }
    in_vehicle_dist_la = {"01", "02"}
    in_vehicle_dist_ld = {"01", "02", "03", "04", "05", "06", "07", "08", "09"}
    in_vehicle_dist_mh = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51"
    }
    in_vehicle_dist_ml = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10"}
    in_vehicle_dist_mn = {"01", "02", "03", "04", "05", "06", "07"}
    in_vehicle_dist_mp = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71"
    }
    in_vehicle_dist_mz = {"01", "02", "03", "04", "05", "06", "07", "08"}
    in_vehicle_dist_nl = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10"}
    in_vehicle_dist_od = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35"
    }
    in_vehicle_dist_or = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31"
    }  # old list
    in_vehicle_dist_pb = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73",
        "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85",
        "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
        "98", "99"
    }
    in_vehicle_dist_py = {"01", "02", "03", "04", "05"}
    in_vehicle_dist_rj = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58"
    }
    in_vehicle_dist_sk = {"01", "02", "03", "04", "05", "06", "07", "08"}
    in_vehicle_dist_tn = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73",
        "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85",
        "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
        "98", "99"
    }
    in_vehicle_dist_tr = {"01", "02", "03", "04", "05", "06", "07", "08"}
    in_vehicle_dist_ts = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38"
    }
    in_vehicle_dist_uk = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20"
    }
    in_vehicle_dist_up = {
        "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "22", "23",
        "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35",
        "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47",
        "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
        "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71",
        "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83",
        "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95",
        "96"
    }
    in_vehicle_dist_wb = {
        "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
        "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25",
        "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37",
        "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
        "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73",
        "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85",
        "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
        "98"
    }
    in_union_territories = {"AN", "CH", "DH", "DL", "JK", "LA", "LD", "PY"}
    in_old_union_territories = {"CT", "DN"}
    in_states = {
        "AP", "AR", "AS", "BR", "CG", "GA", "GJ", "HR", "HP", "JH", "KA", "KL",
        "MP", "MH", "MN", "ML", "MZ", "NL", "OD", "PB", "RJ", "SK", "TN", "TS",
        "TR", "UP", "UK", "WB", "UT"
    }
    in_old_states = {"UL", "OR", "UA"}
    in_non_standard_state_or_ut = {"DD"}

    state_rto_district_map = {
        "AN": in_vehicle_dist_an,
        "AP": in_vehicle_dist_ap,
        "AR": in_vehicle_dist_ar,
        "AS": in_vehicle_dist_as,
        "BR": in_vehicle_dist_br,
        "CG": in_vehicle_dist_cg,
        "CH": in_vehicle_dist_ch,
        "DD": in_vehicle_dist_dd,
        "DN": in_vehicle_dist_dn,
        "DL": in_vehicle_dist_dl,
        "GA": in_vehicle_dist_ga,
        "GJ": in_vehicle_dist_gj,
        "HP": in_vehicle_dist_hp,
        "HR": in_vehicle_dist_hr,
        "JH": in_vehicle_dist_jh,
        "JK": in_vehicle_dist_jk,
        "KA": in_vehicle_dist_ka,
        "KL": in_vehicle_dist_kl,
        "LA": in_vehicle_dist_la,
        "LD": in_vehicle_dist_ld,
        "MH": in_vehicle_dist_mh,
        "ML": in_vehicle_dist_ml,
        "MN": in_vehicle_dist_mn,
        "MP": in_vehicle_dist_mp,
        "MZ": in_vehicle_dist_mz,
        "NL": in_vehicle_dist_nl,
        "OD": in_vehicle_dist_od,
        "OR": in_vehicle_dist_or,
        "PB": in_vehicle_dist_pb,
        "PY": in_vehicle_dist_py,
        "RJ": in_vehicle_dist_rj,
        "SK": in_vehicle_dist_sk,
        "TN": in_vehicle_dist_tn,
        "TR": in_vehicle_dist_tr,
        "TS": in_vehicle_dist_ts,
        "UK": in_vehicle_dist_uk,
        "UP": in_vehicle_dist_up,
        "WB": in_vehicle_dist_wb,
    }

    two_factor_registration_prefix = set()
    two_factor_registration_prefix |= in_union_territories
    two_factor_registration_prefix |= in_states
    two_factor_registration_prefix |= in_old_states
    two_factor_registration_prefix |= in_old_union_territories
    two_factor_registration_prefix |= in_non_standard_state_or_ut
    # fmt: on

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "IN_VEHICLE_REGISTRATION",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs
            if replacement_pairs
            else [("-", ""), (" ", ""), (":", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """Determine absolute value based on calculation."""
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        return self.__check_vehicle_registration(sanitized_value)

    def __check_vehicle_registration(self, sanitized_value: str) -> bool:
        # print('check function called')
        is_valid_registration = None  # deliberately not typecasted or set to bool False
        # logic here
        if len(sanitized_value) >= 8:
            first_two_char = sanitized_value[:2].upper()
            dist_code: str = ""

            if first_two_char in self.two_factor_registration_prefix:
                if sanitized_value[2].isdigit():
                    if sanitized_value[3].isdigit():
                        dist_code = sanitized_value[2:4]
                    else:
                        dist_code = sanitized_value[2:3]

                    registration_digits = sanitized_value[-4:]
                    if registration_digits.isnumeric():
                        if 0 < int(registration_digits) <= 9999:
                            if (
                                dist_code
                                and dist_code
                                in self.state_rto_district_map.get(first_two_char, "")
                            ):
                                is_valid_registration = True

                for diplomatic_vehicle_code in self.in_vehicle_diplomatic_codes:
                    if diplomatic_vehicle_code in sanitized_value:
                        vehicle_prefix = sanitized_value.partition(
                            diplomatic_vehicle_code
                        )[0]
                        if vehicle_prefix.isnumeric() and (
                            1 <= int(vehicle_prefix) <= 80
                            or int(vehicle_prefix)
                            in self.in_vehicle_foreign_mission_codes
                        ):
                            is_valid_registration = True

        return is_valid_registration

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Determine absolute value based on calculation.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_vehicle_registration_recognizer.py
353
354
355
356
357
358
def validate_result(self, pattern_text: str) -> bool:
    """Determine absolute value based on calculation."""
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )
    return self.__check_vehicle_registration(sanitized_value)

InAadhaarRecognizer

Bases: PatternRecognizer

Recognizes Indian UIDAI Person Identification Number ("AADHAAR").

Reference: https://en.wikipedia.org/wiki/Aadhaar A 12 digit unique number that is issued to each individual by Government of India

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IN_AADHAAR'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Determine absolute value based on calculation.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_aadhaar_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
class InAadhaarRecognizer(PatternRecognizer):
    """
    Recognizes Indian UIDAI Person Identification Number ("AADHAAR").

    Reference: https://en.wikipedia.org/wiki/Aadhaar
    A 12 digit unique number that is issued to each individual by Government of India
    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "in"

    PATTERNS = [
        Pattern(
            "AADHAAR (Very Weak)",
            r"\b[0-9]{12}\b",
            0.01,
        ),
        Pattern("AADHAR (Very Weak)", r"\b[0-9]{4}[- :][0-9]{4}[- :][0-9]{4}\b", 0.01),
    ]

    CONTEXT = [
        "aadhaar",
        "uidai",
    ]

    utils = None

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "IN_AADHAAR",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ) -> None:
        self.replacement_pairs = (
            replacement_pairs
            if replacement_pairs
            else [("-", ""), (" ", ""), (":", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """Determine absolute value based on calculation."""
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        return self.__check_aadhaar(sanitized_value)

    def __check_aadhaar(self, sanitized_value: str) -> bool:
        is_valid_aadhaar: bool = False
        if (
            len(sanitized_value) == 12
            and sanitized_value.isnumeric() is True
            and int(sanitized_value[0]) >= 2
            and self._is_verhoeff_number(int(sanitized_value)) is True
            and self._is_palindrome(sanitized_value) is False
        ):
            is_valid_aadhaar = True
        return is_valid_aadhaar

    @staticmethod
    def _is_palindrome(text: str, case_insensitive: bool = False) -> bool:
        """
        Validate if input text is a true palindrome.

        :param text: input text string to check for palindrome
        :param case_insensitive: optional flag to check palindrome with no case
        :return: True / False
        """
        palindrome_text = text
        if case_insensitive:
            palindrome_text = palindrome_text.replace(" ", "").lower()
        return palindrome_text == palindrome_text[::-1]

    @staticmethod
    def _is_verhoeff_number(input_number: int) -> bool:
        """
        Check if the input number is a true verhoeff number.

        :param input_number:
        :return:
        """
        __d__ = [
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
            [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
            [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
            [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
            [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
            [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
            [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
            [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
            [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
        ]
        __p__ = [
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
            [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
            [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
            [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
            [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
            [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
            [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
        ]
        __inv__ = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]

        c = 0
        inverted_number = list(map(int, reversed(str(input_number))))
        for i in range(len(inverted_number)):
            c = __d__[c][__p__[i % 8][inverted_number[i]]]
        return __inv__[c] == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Determine absolute value based on calculation.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_aadhaar_recognizer.py
63
64
65
66
67
68
def validate_result(self, pattern_text: str) -> bool:
    """Determine absolute value based on calculation."""
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )
    return self.__check_aadhaar(sanitized_value)

InGstinRecognizer

Bases: PatternRecognizer

Recognizes Indian Goods and Services Tax Identification Number ("GSTIN").

The GSTIN is a 15-character identifier with the following structure: - First 2 digits: State code (01-37) - Next 10 digits: PAN of the entity - 13th digit: Registration number for same PAN in the state - 14th digit: 'Z' - 15th digit: Checksum

Reference: https://www.gst.gov.in/ This recognizer identifies GSTIN using regex and context words.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IN_GSTIN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the GSTIN format and structure.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_gstin_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
class InGstinRecognizer(PatternRecognizer):
    """
    Recognizes Indian Goods and Services Tax Identification Number ("GSTIN").

    The GSTIN is a 15-character identifier with the following structure:
    - First 2 digits: State code (01-37)
    - Next 10 digits: PAN of the entity
    - 13th digit: Registration number for same PAN in the state
    - 14th digit: 'Z'
    - 15th digit: Checksum

    Reference: https://www.gst.gov.in/
    This recognizer identifies GSTIN using regex and context words.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "in"

    PATTERNS = [
        Pattern(
            "GSTIN (High)",
            r"\b((?:0[1-9]|[1-3][0-7])[A-Za-z0-9]{10}[A-Za-z0-9]{1}Z[A-Za-z0-9]{1})\b",
            0.8,
        ),
        Pattern(
            "GSTIN (Medium)",
            r"\b((?:0[1-9]|[1-3][0-7])[A-Za-z0-9]{11}Z[A-Za-z0-9]{1})\b",
            0.4,
        ),
        Pattern(
            "GSTIN (Low)",
            r"\b([0-9]{2}[A-Za-z0-9]{11}Z[A-Za-z0-9]{1})\b",
            0.1,
        ),
    ]

    CONTEXT = [
        "gstin",
        "gst",
        "goods and services tax",
        "tax identification",
        "gst number",
        "gst registration",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "IN_GSTIN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )
        self.supported_entity = supported_entity

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate the GSTIN format and structure.

        :param pattern_text: The text pattern to validate
        :return: True if the GSTIN is valid, False otherwise
        """
        sanitized_value = self._sanitize_value(pattern_text)
        return self._validate_gstin(sanitized_value)

    def _sanitize_value(self, text: str) -> str:
        """Remove common separators and normalize the text."""
        import re

        # First, try to extract GSTIN pattern from the text
        gstin_pattern = (
            r"\b((?:0[1-9]|[1-3][0-7])[A-Za-z]{5}[0-9]{4}[A-Za-z]{1}"
            r"[0-9A-Za-z]{1}Z[0-9A-Za-z]{1})\b"
        )
        match = re.search(gstin_pattern, text.upper())
        if match:
            return match.group(1)

        # If no GSTIN pattern found, sanitize the entire text
        sanitized = text.upper()
        for old, new in self.replacement_pairs:
            sanitized = sanitized.replace(old, new)
        return sanitized

    def _validate_gstin(self, gstin: str) -> bool:
        """
        Validate GSTIN structure and format.

        :param gstin: The GSTIN string to validate
        :return: True if valid, False otherwise
        """
        if len(gstin) != 15:
            return False

        # Check state code (first 2 digits should be 01-37)
        state_code = gstin[:2]
        if not state_code.isdigit() or not (1 <= int(state_code) <= 37):
            return False

        # Check PAN format (characters 3-12)
        pan_part = gstin[2:12]
        if not self._validate_pan_format(pan_part):
            return False

        # Check 13th character (registration number)
        reg_number = gstin[12]
        if not reg_number.isalnum():
            return False

        # Check 14th character should be 'Z'
        if gstin[13] != "Z":
            return False

        # Check 15th character (checksum)
        checksum = gstin[14]
        if not checksum.isalnum():
            return False

        return True

    def _validate_pan_format(self, pan: str) -> bool:
        """
        Validate PAN format within GSTIN.

        PAN format: 5 letters + 4 digits + 1 letter
        However, some valid PANs may have digits in the first 5 characters.

        :param pan: The PAN part of GSTIN (10 characters)
        :return: True if valid PAN format, False otherwise
        """
        if len(pan) != 10:
            return False

        # Check that it contains a mix of letters and digits
        # At least 3 letters in the first 5 characters
        first_five = pan[:5]
        letter_count = sum(1 for c in first_five if c.isalpha())
        if letter_count < 3:
            return False

        # Characters 6-9 should be digits
        if not pan[5:9].isdigit():
            return False

        # 10th character should be a letter
        if not pan[9].isalpha():
            return False

        return True

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the GSTIN format and structure.

PARAMETER DESCRIPTION
pattern_text

The text pattern to validate

TYPE: str

RETURNS DESCRIPTION
bool

True if the GSTIN is valid, False otherwise

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_gstin_recognizer.py
81
82
83
84
85
86
87
88
89
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate the GSTIN format and structure.

    :param pattern_text: The text pattern to validate
    :return: True if the GSTIN is valid, False otherwise
    """
    sanitized_value = self._sanitize_value(pattern_text)
    return self._validate_gstin(sanitized_value)

InPanRecognizer

Bases: PatternRecognizer

Recognizes Indian Permanent Account Number ("PAN").

The Permanent Account Number (PAN) is a ten digit alpha-numeric code with the last digit being a check digit calculated using a modified modulus 10 calculation. This recognizer identifies PAN using regex and context words. Reference: https://en.wikipedia.org/wiki/Permanent_account_number, https://incometaxindia.gov.in/Forms/tps/1.Permanent%20Account%20Number%20(PAN).pdf

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IN_PAN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_pan_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class InPanRecognizer(PatternRecognizer):
    """
    Recognizes Indian Permanent Account Number ("PAN").

    The Permanent Account Number (PAN) is a ten digit alpha-numeric code
    with the last digit being a check digit calculated using a
    modified modulus 10 calculation.
    This recognizer identifies PAN using regex and context words.
    Reference: https://en.wikipedia.org/wiki/Permanent_account_number,
               https://incometaxindia.gov.in/Forms/tps/1.Permanent%20Account%20Number%20(PAN).pdf

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "in"

    PATTERNS = [
        Pattern(
            "PAN (High)",
            r"\b([A-Za-z]{3}[AaBbCcFfGgHhJjLlPpTt]{1}[A-Za-z]{1}[0-9]{4}[A-Za-z]{1})\b",
            0.5,
        ),
        Pattern(
            "PAN (Medium)",
            r"\b([A-Za-z]{5}[0-9]{4}[A-Za-z]{1})\b",
            0.1,
        ),
        Pattern(
            "PAN (Low)",
            r"\b((?=.*?[a-zA-Z])(?=.*?[0-9]{4})[\w@#$%^?~-]{10})\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "permanent account number",
        "pan",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "IN_PAN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

InPassportRecognizer

Bases: PatternRecognizer

Recognizes Indian Passport Number.

Indian Passport Number is a eight digit alphanumeric number.

Reference: https://www.bajajallianz.com/blog/travel-insurance-articles/where-is-passport-number-in-indian-passport.html

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IN_PASSPORT'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_passport_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class InPassportRecognizer(PatternRecognizer):
    """
    Recognizes Indian Passport Number.

    Indian Passport Number is a eight digit alphanumeric number.

    Reference:
    https://www.bajajallianz.com/blog/travel-insurance-articles/where-is-passport-number-in-indian-passport.html

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "in"

    PATTERNS = [
        Pattern(
            "PASSPORT",
            r"\b[A-Z][1-9]\d\s?\d{4}[1-9]\b",
            0.1,
        ),
    ]

    CONTEXT = ["passport", "indian passport", "passport number"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "IN_PASSPORT",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

InVoterRecognizer

Bases: PatternRecognizer

Recognize Indian Voter/Election Id(EPIC).

The Elector's Photo Identity Card or Voter id is a ten digit alpha-numeric code issued by Election Commission of India to adult domiciles who have reached the age of 18 Ref: https://en.wikipedia.org/wiki/Voter_ID_(India)

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IN_VOTER'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/india/in_voter_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class InVoterRecognizer(PatternRecognizer):
    """
    Recognize Indian Voter/Election Id(EPIC).

    The Elector's Photo Identity Card or Voter id is a ten digit
    alpha-numeric code issued by Election Commission of India
    to adult domiciles who have reached the age of 18
    Ref: https://en.wikipedia.org/wiki/Voter_ID_(India)

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "in"

    PATTERNS = [
        Pattern(
            "VOTER",
            r"\b([A-Za-z]{1}[ABCDGHJKMNPRSYabcdghjkmnprsy]{1}[A-Za-z]{1}([0-9]){7})\b",
            0.4,
        ),
        Pattern(
            "VOTER",
            r"\b([A-Za-z]){3}([0-9]){7}\b",
            0.3,
        ),
    ]

    CONTEXT = [
        "voter",
        "epic",
        "elector photo identity card",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "IN_VOTER",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            supported_entity=supported_entity,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

ItDriverLicenseRecognizer

Bases: PatternRecognizer

Recognizes IT Driver License using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'it'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IT_DRIVER_LICENSE'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/italy/it_driver_license_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class ItDriverLicenseRecognizer(PatternRecognizer):
    """
    Recognizes IT Driver License using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "it"

    PATTERNS = [
        Pattern(
            "Driver License",
            (
                r"\b(?i)(([A-Z]{2}\d{7}[A-Z])"
                r"|(U1[BCDEFGHLJKMNPRSTUWYXZ0-9]{7}[A-Z]))\b"
            ),
            0.2,
        ),
    ]
    CONTEXT = ["patente", "patente di guida", "licenza", "licenza di guida"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "it",
        supported_entity: str = "IT_DRIVER_LICENSE",
        name: Optional[str] = None,
    ) -> None:
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

ItFiscalCodeRecognizer

Bases: PatternRecognizer

Recognizes IT Fiscal Code using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'it'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IT_FISCAL_CODE'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/italy/it_fiscal_code_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
class ItFiscalCodeRecognizer(PatternRecognizer):
    """
    Recognizes IT Fiscal Code using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "it"

    PATTERNS = [
        Pattern(
            "Fiscal Code",
            (
                r"(?i)((?:[A-Z][AEIOU][AEIOUX]|[AEIOU]X{2}"
                r"|[B-DF-HJ-NP-TV-Z]{2}[A-Z]){2}"
                r"(?:[\dLMNP-V]{2}(?:[A-EHLMPR-T](?:[04LQ][1-9MNP-V]|[15MR][\dLMNP-V]"
                r"|[26NS][0-8LMNP-U])|[DHPS][37PT][0L]|[ACELMRT][37PT][01LM]"
                r"|[AC-EHLMPR-T][26NS][9V])|(?:[02468LNQSU][048LQU]"
                r"|[13579MPRTV][26NS])B[26NS][9V])(?:[A-MZ][1-9MNP-V][\dLMNP-V]{2}"
                r"|[A-M][0L](?:[1-9MNP-V][\dLMNP-V]|[0L][1-9MNP-V]))[A-Z])"
            ),
            0.3,
        ),
    ]
    CONTEXT = ["codice fiscale", "cf"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "it",
        supported_entity: str = "IT_FISCAL_CODE",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """
        pattern_text = pattern_text.upper()
        control = pattern_text[-1]
        text_to_validate = pattern_text[:-1]
        odd_values = text_to_validate[0::2]
        even_values = text_to_validate[1::2]

        # Odd values
        map_odd = {
            "0": 1,
            "1": 0,
            "2": 5,
            "3": 7,
            "4": 9,
            "5": 13,
            "6": 15,
            "7": 17,
            "8": 19,
            "9": 21,
            "A": 1,
            "B": 0,
            "C": 5,
            "D": 7,
            "E": 9,
            "F": 13,
            "G": 15,
            "H": 17,
            "I": 19,
            "J": 21,
            "K": 2,
            "L": 4,
            "M": 18,
            "N": 20,
            "O": 11,
            "P": 3,
            "Q": 6,
            "R": 8,
            "S": 12,
            "T": 14,
            "U": 16,
            "V": 10,
            "W": 22,
            "X": 25,
            "Y": 24,
            "Z": 23,
        }

        odd_sum = 0
        for char in odd_values:
            odd_sum += map_odd[char]

        # Even values
        map_even = {
            "0": 0,
            "1": 1,
            "2": 2,
            "3": 3,
            "4": 4,
            "5": 5,
            "6": 6,
            "7": 7,
            "8": 8,
            "9": 9,
            "A": 0,
            "B": 1,
            "C": 2,
            "D": 3,
            "E": 4,
            "F": 5,
            "G": 6,
            "H": 7,
            "I": 8,
            "J": 9,
            "K": 10,
            "L": 11,
            "M": 12,
            "N": 13,
            "O": 14,
            "P": 15,
            "Q": 16,
            "R": 17,
            "S": 18,
            "T": 19,
            "U": 20,
            "V": 21,
            "W": 22,
            "X": 23,
            "Y": 24,
            "Z": 25,
        }

        even_sum = 0
        for char in even_values:
            even_sum += map_even[char]

        # Mod value
        map_mod = {
            0: "A",
            1: "B",
            2: "C",
            3: "D",
            4: "E",
            5: "F",
            6: "G",
            7: "H",
            8: "I",
            9: "J",
            10: "K",
            11: "L",
            12: "M",
            13: "N",
            14: "O",
            15: "P",
            16: "Q",
            17: "R",
            18: "S",
            19: "T",
            20: "U",
            21: "V",
            22: "W",
            23: "X",
            24: "Y",
            25: "Z",
        }
        check_value = map_mod[((odd_sum + even_sum) % 26)]

        if check_value == control:
            result = True
        else:
            result = None

        return result

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/italy/it_fiscal_code_recognizer.py
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    pattern_text = pattern_text.upper()
    control = pattern_text[-1]
    text_to_validate = pattern_text[:-1]
    odd_values = text_to_validate[0::2]
    even_values = text_to_validate[1::2]

    # Odd values
    map_odd = {
        "0": 1,
        "1": 0,
        "2": 5,
        "3": 7,
        "4": 9,
        "5": 13,
        "6": 15,
        "7": 17,
        "8": 19,
        "9": 21,
        "A": 1,
        "B": 0,
        "C": 5,
        "D": 7,
        "E": 9,
        "F": 13,
        "G": 15,
        "H": 17,
        "I": 19,
        "J": 21,
        "K": 2,
        "L": 4,
        "M": 18,
        "N": 20,
        "O": 11,
        "P": 3,
        "Q": 6,
        "R": 8,
        "S": 12,
        "T": 14,
        "U": 16,
        "V": 10,
        "W": 22,
        "X": 25,
        "Y": 24,
        "Z": 23,
    }

    odd_sum = 0
    for char in odd_values:
        odd_sum += map_odd[char]

    # Even values
    map_even = {
        "0": 0,
        "1": 1,
        "2": 2,
        "3": 3,
        "4": 4,
        "5": 5,
        "6": 6,
        "7": 7,
        "8": 8,
        "9": 9,
        "A": 0,
        "B": 1,
        "C": 2,
        "D": 3,
        "E": 4,
        "F": 5,
        "G": 6,
        "H": 7,
        "I": 8,
        "J": 9,
        "K": 10,
        "L": 11,
        "M": 12,
        "N": 13,
        "O": 14,
        "P": 15,
        "Q": 16,
        "R": 17,
        "S": 18,
        "T": 19,
        "U": 20,
        "V": 21,
        "W": 22,
        "X": 23,
        "Y": 24,
        "Z": 25,
    }

    even_sum = 0
    for char in even_values:
        even_sum += map_even[char]

    # Mod value
    map_mod = {
        0: "A",
        1: "B",
        2: "C",
        3: "D",
        4: "E",
        5: "F",
        6: "G",
        7: "H",
        8: "I",
        9: "J",
        10: "K",
        11: "L",
        12: "M",
        13: "N",
        14: "O",
        15: "P",
        16: "Q",
        17: "R",
        18: "S",
        19: "T",
        20: "U",
        21: "V",
        22: "W",
        23: "X",
        24: "Y",
        25: "Z",
    }
    check_value = map_mod[((odd_sum + even_sum) % 26)]

    if check_value == control:
        result = True
    else:
        result = None

    return result

ItIdentityCardRecognizer

Bases: PatternRecognizer

Recognizes Italian Identity Card number using case-insensitive regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'it'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IT_IDENTITY_CARD'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/italy/it_identity_card_recognizer.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class ItIdentityCardRecognizer(PatternRecognizer):
    """
    Recognizes Italian Identity Card number using case-insensitive regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "it"

    PATTERNS = [
        Pattern(
            "Paper-based Identity Card (very weak)",
            # The number is composed of 2 letters, space (optional), 7 digits
            r"(?i)\b[A-Z]{2}\s?\d{7}\b",
            0.01,
        ),
        Pattern(
            "Electronic Identity Card (CIE) 2.0 (very weak)",
            r"(?i)\b\d{7}[A-Z]{2}\b",
            0.01,
        ),
        Pattern(
            "Electronic Identity Card (CIE) 3.0 (very weak)",
            r"(?i)\b[A-Z]{2}\d{5}[A-Z]{2}\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "carta",
        "identità",
        "elettronica",
        "cie",
        "documento",
        "riconoscimento",
        "espatrio",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "it",
        supported_entity: str = "IT_IDENTITY_CARD",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

ItPassportRecognizer

Bases: PatternRecognizer

Recognizes IT Passport number using case-insensitive regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'it'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IT_PASSPORT'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/italy/it_passport_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class ItPassportRecognizer(PatternRecognizer):
    """
    Recognizes IT Passport number using case-insensitive regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "it"

    PATTERNS = [
        Pattern(
            "Passport (very weak)",
            r"(?i)\b[A-Z]{2}\d{7}\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "passaporto",
        "elettronico",
        "italiano",
        "viaggio",
        "viaggiare",
        "estero",
        "documento",
        "dogana",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "it",
        supported_entity: str = "IT_PASSPORT",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

ItVatCodeRecognizer

Bases: PatternRecognizer

Recognizes Italian VAT code using regex and checksum.

For more information about italian VAT code: https://en.wikipedia.org/wiki/VAT_identification_number#:~:text=%5B2%5D)-,Italy,-Partita%20IVA

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'it'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IT_VAT_CODE'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/italy/it_vat_code.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
class ItVatCodeRecognizer(PatternRecognizer):
    """
    Recognizes Italian VAT code using regex and checksum.

    For more information about italian VAT code:
        https://en.wikipedia.org/wiki/VAT_identification_number#:~:text=%5B2%5D)-,Italy,-Partita%20IVA

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "it"

    # Class variables
    PATTERNS = [
        Pattern(
            "IT Vat code (piva)",
            r"\b([0-9][ _]?){11}\b",
            0.1,
        )
    ]

    CONTEXT = ["piva", "partita iva", "pi"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "it",
        supported_entity: str = "IT_VAT_CODE",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
        version: str = "0.0.1",
    ):
        self.replacement_pairs = (
            replacement_pairs
            if replacement_pairs
            else [("-", ""), (" ", ""), ("_", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
            version=version,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """

        # Pre-processing before validation checks
        text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)

        # Edge-case that passes the checksum even though it is not a
        # valid italian vat code.
        if text == "00000000000":
            return False

        x = 0
        y = 0

        for i in range(0, 5):
            x += int(text[2 * i])
            tmp_y = int(text[2 * i + 1]) * 2
            if tmp_y > 9:
                tmp_y = tmp_y - 9
            y += tmp_y

        t = (x + y) % 10
        c = (10 - t) % 10

        if c == int(text[10]):
            result = True
        else:
            result = False

        return result

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
bool

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/italy/it_vat_code.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """

    # Pre-processing before validation checks
    text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)

    # Edge-case that passes the checksum even though it is not a
    # valid italian vat code.
    if text == "00000000000":
        return False

    x = 0
    y = 0

    for i in range(0, 5):
        x += int(text[2 * i])
        tmp_y = int(text[2 * i + 1]) * 2
        if tmp_y > 9:
            tmp_y = tmp_y - 9
        y += tmp_y

    t = (x + y) % 10
    c = (10 - t) % 10

    if c == int(text[10]):
        result = True
    else:
        result = False

    return result

KrBrnRecognizer

Bases: PatternRecognizer

Recognize Korean Business Registration Number (BRN).

The Korean Business Registration Number (BRN) is a 10-digit number assigned to businesses in South Korea for taxation purposes.

The format is AAA-BB-CCCCC where: - AAA: District office code - BB: Type of business code - CCCCC: Serial number and check digit

Reference: https://org-id.guide/list/KR-BRN

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'ko'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'KR_BRN'

replacement_pairs

List of tuples with potential replacement values to be used during pattern matching (e.g., removing dashes).

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic by running a checksum on a detected BRN.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_brn_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
class KrBrnRecognizer(PatternRecognizer):
    """
    Recognize Korean Business Registration Number (BRN).

    The Korean Business Registration Number (BRN) is a 10-digit number
    assigned to businesses in South Korea for taxation purposes.

    The format is AAA-BB-CCCCC where:
    - AAA: District office code
    - BB: Type of business code
    - CCCCC: Serial number and check digit

    Reference: https://org-id.guide/list/KR-BRN

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    to be used during pattern matching (e.g., removing dashes).
    """

    COUNTRY_CODE = "kr"

    PATTERNS = [
        Pattern(
            "BRN (Weak)",
            r"(?<!\d)\d{3}-\d{2}-\d{5}(?!\d)",
            0.1,
        ),
        Pattern(
            "BRN (Very weak)",
            r"(?<!\d)\d{10}(?!\d)",
            0.05,
        ),
    ]

    CONTEXT = [
        "사업자등록번호",
        "사업자번호",
        "사업자",
        "BRN",
        "Business Registration Number",
        "Korean BRN",
        "business number",
        "tax registration number",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "ko",
        supported_entity: str = "KR_BRN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
    ):
        self.replacement_pairs = replacement_pairs if replacement_pairs else [("-", "")]

        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
        )

    def validate_result(self, pattern_text: str) -> Union[bool, None]:
        """
        Validate the pattern logic by running a checksum on a detected BRN.

        :param pattern_text: The text detected by the regex engine.
        :return: True if validation is successful, False otherwise.
        """
        # Sanitize the value by removing dashes
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        # BRN must be exactly 10 digits
        if len(sanitized_value) != 10:
            return False

        if not sanitized_value.isdigit():
            return False

        return self._validate_checksum(sanitized_value)

    def _validate_checksum(self, brn: str) -> bool:
        """
        Validate the checksum of Korean Business Registration Number.

        The validation algorithm:
        1. Multiply the first 9 digits by the magic keys: [1, 3, 7, 1, 3, 7, 1, 3, 5].
        2. Sum the results of the first 8 multiplications.
        3. For the 9th digit (index 8):
            Add the product (digit * 5) AND the quotient of (digit * 5) / 10 to the sum.
        4. Take the remainder of the total sum divided by 10.
        5. Subtract the remainder from 10.
            The result (mod 10) should match the 10th digit.

        :param brn: The 10-digit BRN string to validate.
        :return: True if checksum is valid, False otherwise.
        """
        digits = [int(d) for d in brn]
        magic_keys = [1, 3, 7, 1, 3, 7, 1, 3, 5]

        # Step 1 & 2: Calculate sum for the first 8 digits
        total_sum = 0
        for i in range(8):
            total_sum += digits[i] * magic_keys[i]

        # Step 3: Special handling for the 9th digit
        last_key_mul = digits[8] * magic_keys[8]
        total_sum += (last_key_mul // 10) + last_key_mul

        # Step 4 & 5: Validate against the 10th check digit
        remainder = total_sum % 10
        check_digit = (10 - remainder) % 10

        return check_digit == digits[9]

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Union[bool, None]

Validate the pattern logic by running a checksum on a detected BRN.

PARAMETER DESCRIPTION
pattern_text

The text detected by the regex engine.

TYPE: str

RETURNS DESCRIPTION
Union[bool, None]

True if validation is successful, False otherwise.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_brn_recognizer.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def validate_result(self, pattern_text: str) -> Union[bool, None]:
    """
    Validate the pattern logic by running a checksum on a detected BRN.

    :param pattern_text: The text detected by the regex engine.
    :return: True if validation is successful, False otherwise.
    """
    # Sanitize the value by removing dashes
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    # BRN must be exactly 10 digits
    if len(sanitized_value) != 10:
        return False

    if not sanitized_value.isdigit():
        return False

    return self._validate_checksum(sanitized_value)

KrDriverLicenseRecognizer

Bases: PatternRecognizer

Recognize Korean Driver's License Number.

The Korean Driver's License Number consists of 12 digits, typically formatted as AA-BB-CCCCCC-DD.

Format Breakdown: - AA: Regional code - BB: Year of issuance (last two digits of the year) - CCCCCC: Serial number - DD: Check digit (Verification number; not publicly disclosed)

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'ko'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'KR_DRIVER_LICENSE'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate length, region code.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_driver_license_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
class KrDriverLicenseRecognizer(PatternRecognizer):
    """
    Recognize Korean Driver's License Number.

    The Korean Driver's License Number consists of 12 digits,
    typically formatted as AA-BB-CCCCCC-DD.

    Format Breakdown:
    - AA: Regional code
    - BB: Year of issuance (last two digits of the year)
    - CCCCCC: Serial number
    - DD: Check digit (Verification number; not publicly disclosed)

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes.
    """

    COUNTRY_CODE = "kr"

    PATTERNS = [
        Pattern(
            "Driver License (very weak)",
            r"(?<!\d)(\d{2})[- ]?(\d{2})[- ]?(\d{6})[- ]?(\d{2})(?!\d)",
            0.05,
        )
    ]

    CONTEXT = [
        "운전면허",
        "운전면허번호",
        "면허번호",
        "Korean driver license",
        "Korean driver's license",
    ]

    REGION_CODES = {
        "11",
        "12",
        "13",
        "14",
        "15",
        "16",
        "17",
        "18",
        "19",
        "20",
        "21",
        "22",
        "23",
        "24",
        "25",
        "26",
        "28",
    }

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "ko",
        supported_entity: str = "KR_DRIVER_LICENSE",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )

        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate length, region code.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool or None, indicating whether the validation was successful.
        """
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        if len(sanitized_value) != 12:
            return False

        if not sanitized_value.isdigit():
            return False

        region_code = sanitized_value[:2]
        if region_code not in self.REGION_CODES:
            return False

        return True

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate length, region code.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
bool

A bool or None, indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_driver_license_recognizer.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate length, region code.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool or None, indicating whether the validation was successful.
    """
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    if len(sanitized_value) != 12:
        return False

    if not sanitized_value.isdigit():
        return False

    region_code = sanitized_value[:2]
    if region_code not in self.REGION_CODES:
        return False

    return True

KrFrnRecognizer

Bases: KrRrnRecognizer

Recognize Korean Foreigner Registration Number (FRN).

The Korean FRN is a 13-digit number issued to registered foreigners in Korea. The format is YYMMDD-GHIJKLX where: - YYMMDD represents the birth date. - G determines gender and century (5-8). - 5: Male, 1900-1999 birth - 6: Female, 1900-1999 birth - 7: Male, 2000-2099 birth - 8: Female, 2000-2099 birth - (Note: 9 and 0 are sometimes used for 1800s birth, but rare)

For FRNs issued before October 2020: - HIJKL is a serial number assigned by district - X is a check digit calculated using the preceding 12 digits

For FRNs issued after October 2020: - HIJKLX is a random number

Reference: https://en.wikipedia.org/wiki/Resident_registration_number

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'ko'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'KR_FRN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_frn_recognizer.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class KrFrnRecognizer(KrRrnRecognizer):
    """
    Recognize Korean Foreigner Registration Number (FRN).

    The Korean FRN is a 13-digit number issued to registered foreigners in Korea.
    The format is YYMMDD-GHIJKLX where:
    - YYMMDD represents the birth date.
    - G determines gender and century (5-8).
        - 5: Male, 1900-1999 birth
        - 6: Female, 1900-1999 birth
        - 7: Male, 2000-2099 birth
        - 8: Female, 2000-2099 birth
        - (Note: 9 and 0 are sometimes used for 1800s birth, but rare)

    For FRNs issued before October 2020:
    - HIJKL is a serial number assigned by district
    - X is a check digit calculated using the preceding 12 digits

    For FRNs issued after October 2020:
    - HIJKLX is a random number

    Reference: https://en.wikipedia.org/wiki/Resident_registration_number

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes.
    """

    COUNTRY_CODE = "kr"

    PATTERNS = [
        Pattern(
            "FRN (Medium)",
            r"(?<!\d)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])(-?)[5-8]\d{6}(?!\d)",
            0.5,
        )
    ]

    CONTEXT = [
        "외국인등록번호",
        "Korean FRN",
        "FRN",
        "Foreigner Registration Number",
        "Korean Foreigner Registration Number",
        "외국인번호",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "ko",
        supported_entity: str = "KR_FRN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        super().__init__(
            patterns=patterns if patterns else self.PATTERNS,
            context=context if context else self.CONTEXT,
            supported_language=supported_language,
            supported_entity=supported_entity,
            replacement_pairs=replacement_pairs,
            name=name,
        )

    def _validate_checksum(self, frn: str) -> bool:
        """
        Validate the checksum of Korean FRN.

        The checksum is calculated using the preceding 12 digits.
        X = (13 - (2A+3B+4C+5D+6E+7F+8G+9H+2I+3J+4K+5L) mod 11) mod 10

        :param frn: The FRN to validate
        :return: True if checksum is valid, False otherwise
        """
        digit_sum = super()._compute_checksum(frn)
        checksum = (13 - (digit_sum % 11)) % 10
        return checksum == int(frn[12])

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

This validation is only for RRNs issued before October 2020. Therefore, it returns None, not False, at the end of the method.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool or None, indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_rrn_recognizer.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    This validation is only for RRNs issued before October 2020.
    Therefore, it returns None, not False, at the end of the method.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool or None, indicating whether the validation was successful.
    """
    # Pre-processing before validation checks
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    # Check if the sanitized value has the correct length (13 digits)
    if len(sanitized_value) != 13:
        return False

    # Check if all characters are digits
    if not sanitized_value.isdigit():
        return False

    # Validate region code (HI) and checksum (X)
    region_code = int(sanitized_value[7:9])  # HI
    if self._validate_region_code(region_code) and self._validate_checksum(
        sanitized_value
    ):
        return True

    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

KrPassportRecognizer

Bases: PatternRecognizer

Recognize Korean Passport Number.

https://learn.microsoft.com/en-us/purview/sit-defn-south-korea-passport-number

the current passport number format: - one letter 'M' or 'm' or 'S' or 's' or 'R' or 'r' or 'O' or 'o' or 'D' or 'd' - three digits - one letter (A-Z, a-z) - four digits

the previous passport number format: - one letter 'M' or 'm' or 'S' or 's' or 'R' or 'r' or 'O' or 'o' or 'D' or 'd' - eight digits

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_passport_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class KrPassportRecognizer(PatternRecognizer):
    """
    Recognize Korean Passport Number.

    https://learn.microsoft.com/en-us/purview/sit-defn-south-korea-passport-number

    the current passport number format:
        - one letter 'M' or 'm' or 'S' or 's' or 'R' or 'r' or 'O' or 'o' or 'D' or 'd'
        - three digits
        - one letter (A-Z, a-z)
        - four digits

    the previous passport number format:
        - one letter 'M' or 'm' or 'S' or 's' or 'R' or 'r' or 'O' or 'o' or 'D' or 'd'
        - eight digits
    """

    COUNTRY_CODE = "kr"

    PATTERNS = [
        Pattern(
            "Passport Number (Current)",
            r"(?<![A-Z0-9a-z])[MmSsRrOoDd]\d{3}[A-Za-z]\d{4}(?![0-9])",
            0.1,
        ),
        Pattern(
            "Passport Number (Previous)",
            r"(?<![A-Z0-9a-z])[MmSsRrOoDd]\d{8}(?![0-9])",
            0.05,
        ),
    ]

    CONTEXT = [
        "Korean passport",
        "Korean passport number",
        "대한민국 여권",
        "여권",
        "passport",
        "passport number",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "kr",
        supported_entity: str = "KR_PASSPORT",
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

KrRrnRecognizer

Bases: PatternRecognizer

Recognize Korean Resident Registration Number (RRN).

The Korean Resident Registration Number (RRN) is a 13-digit number issued to all Korean residents.

The format is YYMMDD-GHIJKLX where: - YYMMDD represents the birth date - G determines gender and century of birth

For RRNs issued before October 2020: - HIJKL is a serial number assigned by district - X is a check digit calculated using the preceding 12 digits

For RRNs issued after October 2020: - HIJKLX is a random number

Reference: https://en.wikipedia.org/wiki/Resident_registration_number

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'ko'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'KR_RRN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_rrn_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
class KrRrnRecognizer(PatternRecognizer):
    """
    Recognize Korean Resident Registration Number (RRN).

    The Korean Resident Registration Number (RRN) is
    a 13-digit number issued to all Korean residents.

    The format is YYMMDD-GHIJKLX where:
    - YYMMDD represents the birth date
    - G determines gender and century of birth

    For RRNs issued before October 2020:
    - HIJKL is a serial number assigned by district
    - X is a check digit calculated using the preceding 12 digits

    For RRNs issued after October 2020:
    - HIJKLX is a random number

    Reference: https://en.wikipedia.org/wiki/Resident_registration_number

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes.
    """

    COUNTRY_CODE = "kr"

    PATTERNS = [
        Pattern(
            "RRN (Medium)",
            r"(?<!\d)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])(-?)[1-4]\d{6}(?!\d)",
            0.5,
        )
    ]

    CONTEXT = [
        "Korean RRN",
        "Korean Resident Registration Number",
        "Resident Registration Number",
        "RRN",
        "rrn",
        "rrn#",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "ko",
        supported_entity: str = "KR_RRN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = replacement_pairs if replacement_pairs else [("-", "")]

        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        This validation is only for RRNs issued before October 2020.
        Therefore, it returns None, not False, at the end of the method.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool or None, indicating whether the validation was successful.
        """
        # Pre-processing before validation checks
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        # Check if the sanitized value has the correct length (13 digits)
        if len(sanitized_value) != 13:
            return False

        # Check if all characters are digits
        if not sanitized_value.isdigit():
            return False

        # Validate region code (HI) and checksum (X)
        region_code = int(sanitized_value[7:9])  # HI
        if self._validate_region_code(region_code) and self._validate_checksum(
            sanitized_value
        ):
            return True

        return None

    def _validate_region_code(self, region_code: int) -> bool:
        """
        Validate the region code of Korean RRN.

        :param region_code: The region code to validate
        :return: True if region code is valid, False otherwise
        """
        return True if 0 <= region_code <= 95 else False

    def _compute_checksum(self, rn: str) -> int:
        """
        Compute the weighted digit sum used for the RRN checksum calculation.

        The sum is calculated over the first 12 digits of the RRN using
        the weights [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5].

        :param rn: The resident registration number as a string.
            Only the first 12 digits are used in the computation.
        :return: The integer sum of the products of digits and weights.
        """
        weights = [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5]
        return sum(int(rn[i]) * weights[i] for i in range(12))

    def _validate_checksum(self, rrn: str) -> bool:
        """
        Validate the checksum of Korean RRN.

        The checksum is calculated using the preceding 12 digits.
        X = (11 - (2A+3B+4C+5D+6E+7F+8G+9H+2I+3J+4K+5L) mod 11) mod 10

        :param rrn: The RRN to validate
        :return: True if checksum is valid, False otherwise
        """
        digit_sum = self._compute_checksum(rrn)
        checksum = (11 - (digit_sum % 11)) % 10
        return checksum == int(rrn[12])

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

This validation is only for RRNs issued before October 2020. Therefore, it returns None, not False, at the end of the method.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool or None, indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/korea/kr_rrn_recognizer.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    This validation is only for RRNs issued before October 2020.
    Therefore, it returns None, not False, at the end of the method.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool or None, indicating whether the validation was successful.
    """
    # Pre-processing before validation checks
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    # Check if the sanitized value has the correct length (13 digits)
    if len(sanitized_value) != 13:
        return False

    # Check if all characters are digits
    if not sanitized_value.isdigit():
        return False

    # Validate region code (HI) and checksum (X)
    region_code = int(sanitized_value[7:9])  # HI
    if self._validate_region_code(region_code) and self._validate_checksum(
        sanitized_value
    ):
        return True

    return None

NgNinRecognizer

Bases: PatternRecognizer

Recognizes Nigerian National Identification Number (NIN).

The NIN is an 11-digit number issued by the National Identity Management Commission (NIMC). The last digit is a Verhoeff checksum.

Reference: https://nimc.gov.ng/

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'NG_NIN'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the NIN by checking length, digits, and Verhoeff checksum.

Source code in presidio_analyzer/predefined_recognizers/country_specific/nigeria/ng_nin_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
class NgNinRecognizer(PatternRecognizer):
    """
    Recognizes Nigerian National Identification Number (NIN).

    The NIN is an 11-digit number issued by the National Identity Management
    Commission (NIMC). The last digit is a Verhoeff checksum.

    Reference: https://nimc.gov.ng/

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "ng"

    PATTERNS = [
        Pattern(
            "NIN (Very Weak)",
            r"\b\d{11}\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "nin",
        "national identification number",
        "national identity number",
        "nimc",
        "national identity",
        "nigeria id",
        "nigerian identification",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "NG_NIN",
        name: Optional[str] = None,
    ) -> None:
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """Validate the NIN by checking length, digits, and Verhoeff checksum."""
        return self.__check_nin(pattern_text)

    def __check_nin(self, value: str) -> bool:
        return (
            len(value) == 11
            and value.isnumeric()
            and self._is_verhoeff_number(int(value))
        )

    @staticmethod
    def _is_verhoeff_number(input_number: int) -> bool:
        """
        Check if the input number is a true Verhoeff number.

        :param input_number: Number to validate
        :return: True if the number passes the Verhoeff checksum
        """
        __d__ = [
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
            [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
            [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
            [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
            [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
            [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
            [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
            [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
            [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
        ]
        __p__ = [
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
            [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
            [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
            [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
            [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
            [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
            [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
        ]
        __inv__ = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]

        c = 0
        inverted_number = list(map(int, reversed(str(input_number))))
        for i in range(len(inverted_number)):
            c = __d__[c][__p__[i % 8][inverted_number[i]]]
        return __inv__[c] == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the NIN by checking length, digits, and Verhoeff checksum.

Source code in presidio_analyzer/predefined_recognizers/country_specific/nigeria/ng_nin_recognizer.py
59
60
61
def validate_result(self, pattern_text: str) -> bool:
    """Validate the NIN by checking length, digits, and Verhoeff checksum."""
    return self.__check_nin(pattern_text)

NgVehicleRegistrationRecognizer

Bases: PatternRecognizer

Recognizes Nigerian vehicle registration plate numbers (current format, 2011+).

The current format is: ABC-123DE - 3 letters: LGA (Local Government Area) code - Hyphen separator (may be omitted or replaced with space) - 3 digits: serial number (001-999) - 2 letters: year code + batch code

Reference: https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Nigeria

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'NG_VEHICLE_REGISTRATION'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/nigeria/ng_vehicle_registration_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class NgVehicleRegistrationRecognizer(PatternRecognizer):
    """
    Recognizes Nigerian vehicle registration plate numbers (current format, 2011+).

    The current format is: ABC-123DE
    - 3 letters: LGA (Local Government Area) code
    - Hyphen separator (may be omitted or replaced with space)
    - 3 digits: serial number (001-999)
    - 2 letters: year code + batch code

    Reference: https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Nigeria

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "ng"

    PATTERNS = [
        Pattern(
            "Nigeria Vehicle Registration",
            r"\b[A-Z]{3}[- ]?\d{3}[A-Z]{2}\b",
            0.5,
        ),
    ]

    CONTEXT = [
        "plate number",
        "vehicle registration",
        "license plate",
        "number plate",
        "plate",
        "vehicle",
        "registration",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "NG_VEHICLE_REGISTRATION",
        name: Optional[str] = None,
    ) -> None:
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

PhTinRecognizer

Bases: PatternRecognizer

Recognizes Philippines Taxpayer Identification Number (TIN).

The TIN is a 9 or 12-digit number issued by the Bureau of Internal Revenue (BIR). The 9th digit is a check digit calculated using a weighted modulo 11 algorithm. The last 3 digits (in the 12-digit version) represent the branch code (default 000).

Formats: XXXXXXXXX, XXXXXXXXXXXX, XXX-XXX-XXX, or XXX-XXX-XXX-XXX Reference: https://www.bir.gov.ph/

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'PH_TIN'

replacement_pairs

List of tuples with potential replacement values

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

build_regex_explanation

Construct an explanation for why this entity was detected.

invalidate_result

Check if the Philippines TIN fails weighted modulo 11 validation.

Source code in presidio_analyzer/predefined_recognizers/country_specific/philippines/ph_tin_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
class PhTinRecognizer(PatternRecognizer):
    """
    Recognizes Philippines Taxpayer Identification Number (TIN).

    The TIN is a 9 or 12-digit number issued by the Bureau of Internal Revenue (BIR).
    The 9th digit is a check digit calculated using a weighted modulo 11 algorithm.
    The last 3 digits (in the 12-digit version) represent the branch code (default 000).

    Formats: XXXXXXXXX, XXXXXXXXXXXX, XXX-XXX-XXX, or XXX-XXX-XXX-XXX
    Reference: https://www.bir.gov.ph/

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    """

    PATTERNS = [
        Pattern(
            "TIN (Low)",
            r"\b(\d{3}-\d{3}-\d{3}(-\d{3})?)\b",
            0.05,
        ),
        Pattern(
            "TIN (Very Low)",
            r"\b(\d{9}|\d{12})\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "tin",
        "taxpayer identification number",
        "bir",
        "taxpayer id",
        "tax id",
        "rdo",
        "revenue district office",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "PH_TIN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def invalidate_result(self, pattern_text: str) -> bool:
        """
        Check if the Philippines TIN fails weighted modulo 11 validation.

        :param pattern_text: The text to validate
        :return: True if invalid, False otherwise
        """
        return not self._is_valid_tin(pattern_text)

    def _is_valid_tin(self, pattern_text: str) -> bool:
        """Validate the Philippines TIN using weighted modulo 11."""
        # Clean the input
        for search, replace in self.replacement_pairs:
            pattern_text = pattern_text.replace(search, replace)

        if not pattern_text.isdigit():
            return False

        if len(pattern_text) not in (9, 12):
            return False

        # Weights for the first 8 digits
        weights = [9, 8, 7, 6, 5, 4, 3, 2]

        # Calculate sum of first 8 digits multiplied by weights
        total_sum = 0
        for i in range(8):
            total_sum += int(pattern_text[i]) * weights[i]

        # Modulo 11 of the sum
        remainder = total_sum % 11

        # The 9th digit is the check digit
        # Note: If remainder is 10, it's usually not issued or handled specifically.
        # Most implementations for BIR TIN treat the remainder as the check digit.
        check_digit = int(pattern_text[8])

        return remainder == check_digit

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

invalidate_result

invalidate_result(pattern_text: str) -> bool

Check if the Philippines TIN fails weighted modulo 11 validation.

PARAMETER DESCRIPTION
pattern_text

The text to validate

TYPE: str

RETURNS DESCRIPTION
bool

True if invalid, False otherwise

Source code in presidio_analyzer/predefined_recognizers/country_specific/philippines/ph_tin_recognizer.py
69
70
71
72
73
74
75
76
def invalidate_result(self, pattern_text: str) -> bool:
    """
    Check if the Philippines TIN fails weighted modulo 11 validation.

    :param pattern_text: The text to validate
    :return: True if invalid, False otherwise
    """
    return not self._is_valid_tin(pattern_text)

PlPeselRecognizer

Bases: PatternRecognizer

Recognize PESEL number using regex and checksum.

For more information about PESEL: https://en.wikipedia.org/wiki/PESEL

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'pl'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'PL_PESEL'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/poland/pl_pesel_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class PlPeselRecognizer(PatternRecognizer):
    """
    Recognize PESEL number using regex and checksum.

    For more information about PESEL: https://en.wikipedia.org/wiki/PESEL

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "pl"

    PATTERNS = [
        Pattern(
            "PESEL",
            r"[0-9]{2}([02468][1-9]|[13579][012])(0[1-9]|1[0-9]|2[0-9]|3[01])[0-9]{5}",
            0.4,
        ),
    ]

    CONTEXT = ["PESEL"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "pl",
        supported_entity: str = "PL_PESEL",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:  # noqa: D102
        if len(pattern_text) != 11 or not pattern_text.isdigit():
            return False

        digits = [int(digit) for digit in pattern_text]
        weights = [1, 3, 7, 9, 1, 3, 7, 9, 1, 3]

        weighted_sum = sum(d * w for d, w in zip(digits[:10], weights))
        # PESEL check digit = (10 - weighted_sum % 10) % 10. See
        # https://en.wikipedia.org/wiki/PESEL#Check_digit
        check_digit = (10 - weighted_sum % 10) % 10

        return check_digit == digits[10]

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

SgFinRecognizer

Bases: PatternRecognizer

Recognize SG FIN/NRIC number using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'SG_NRIC_FIN'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/singapore/sg_fin_recognizer.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class SgFinRecognizer(PatternRecognizer):
    """
    Recognize SG FIN/NRIC number using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "sg"

    PATTERNS = [
        Pattern("Nric (weak)", r"(?i)(\b[A-Z][0-9]{7}[A-Z]\b)", 0.3),
        Pattern("Nric (medium)", r"(?i)(\b[STFGM][0-9]{7}[A-Z]\b)", 0.5),
    ]

    CONTEXT = ["fin", "fin#", "nric", "nric#"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "SG_NRIC_FIN",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

SgUenRecognizer

Bases: PatternRecognizer

Recognize Singapore UEN (Unique Entity Number) using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'SG_UEN'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

validate_uen_format_a

Validate the UEN format A using checksum.

validate_uen_format_b

Validate the UEN format B using checksum.

validate_uen_format_c

Validate the UEN format C using checksum.

Source code in presidio_analyzer/predefined_recognizers/country_specific/singapore/sg_uen_recognizer.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
class SgUenRecognizer(PatternRecognizer):
    """
    Recognize Singapore UEN (Unique Entity Number) using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "sg"

    PATTERNS = [
        Pattern(
            "UEN (low)",
            r"\b\d{8}[A-Z]\b|\b\d{9}[A-Z]\b|\b[TSR]\d{2}[A-Z]{2}\d{4}[A-Z]\b",
            0.3,
        )
    ]

    CONTEXT = ["uen", "unique entity number", "business registration", "ACRA"]

    UEN_FORMAT_A_WEIGHT = (10, 4, 9, 3, 8, 2, 7, 1)
    UEN_FORMAT_A_ALPHABET = "XMKECAWLJDB"
    UEN_FORMAT_B_WEIGHT = (10, 8, 6, 4, 9, 7, 5, 3, 1)
    UEN_FORMAT_B_ALPHABET = "ZKCMDNERGWH"
    UEN_FORMAT_C_WEIGHT = (4, 3, 5, 3, 10, 2, 2, 5, 7)
    UEN_FORMAT_C_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWX0123456789"
    UEN_FORMAT_C_PREFIX = {"T", "S", "R"}
    UEN_FORMAT_C_ENTITY_TYPE = {
        "LP",
        "LL",
        "FC",
        "PF",
        "RF",
        "MQ",
        "MM",
        "NB",
        "CC",
        "CS",
        "MB",
        "FM",
        "GS",
        "DP",
        "CP",
        "NR",
        "CM",
        "CD",
        "MD",
        "HS",
        "VH",
        "CH",
        "MH",
        "CL",
        "XL",
        "CX",
        "HC",
        "RP",
        "TU",
        "TC",
        "FB",
        "FN",
        "PA",
        "PB",
        "SS",
        "MC",
        "SM",
        "GA",
        "GB",
    }

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "SG_UEN",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """

        if len(pattern_text) == 9:
            # Checksum validation for UEN format A
            return self.validate_uen_format_a(pattern_text)
        elif len(pattern_text) == 10 and pattern_text[0].isalpha():
            # Checksum validation for UEN format C
            return self.validate_uen_format_c(pattern_text)
        elif len(pattern_text) == 10:
            # Checksum validation for UEN format B
            return self.validate_uen_format_b(pattern_text)

        return False

    @staticmethod
    def validate_uen_format_a(uen: str) -> bool:
        """
        Validate the UEN format A using checksum.

        :param uen: The UEN to validate.
        :return: True if the UEN is valid according to its respective
        format, False otherwise.
        """
        check_digit = uen[-1]

        weighted_sum = sum(
            int(n) * w for n, w in zip(uen[:-1], SgUenRecognizer.UEN_FORMAT_A_WEIGHT)
        )

        checksum = SgUenRecognizer.UEN_FORMAT_A_ALPHABET[weighted_sum % 11]

        return check_digit == checksum

    @staticmethod
    def validate_uen_format_b(uen: str) -> bool:
        """
        Validate the UEN format B using checksum.

        :param uen: The UEN to validate.
        :return: True if the UEN is valid according to its respective
        format, False otherwise.
        """
        check_digit = uen[-1]
        year_of_registration = int(uen[0:4])

        # Check if the year of registration is not in the future
        if year_of_registration > date.today().year:
            return False

        weighted_sum = sum(
            int(n) * w for n, w in zip(uen[:-1], SgUenRecognizer.UEN_FORMAT_B_WEIGHT)
        )

        checksum = SgUenRecognizer.UEN_FORMAT_B_ALPHABET[weighted_sum % 11]

        return check_digit == checksum

    @staticmethod
    def validate_uen_format_c(uen: str) -> bool:
        """
        Validate the UEN format C using checksum.

        :param uen: The UEN to validate.
        :return: True if the UEN is valid according to its respective
        format, False otherwise.
        """
        check_digit = uen[-1]

        if uen[0] not in SgUenRecognizer.UEN_FORMAT_C_PREFIX:
            return False

        entity_type = uen[3:5]

        if entity_type not in SgUenRecognizer.UEN_FORMAT_C_ENTITY_TYPE:
            return False

        weighted_sum = sum(
            SgUenRecognizer.UEN_FORMAT_C_ALPHABET.index(n) * w
            for n, w in zip(uen[:-1], SgUenRecognizer.UEN_FORMAT_C_WEIGHT)
        )

        checksum = SgUenRecognizer.UEN_FORMAT_C_ALPHABET[(weighted_sum - 5) % 11]

        return check_digit == checksum

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/singapore/sg_uen_recognizer.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """

    if len(pattern_text) == 9:
        # Checksum validation for UEN format A
        return self.validate_uen_format_a(pattern_text)
    elif len(pattern_text) == 10 and pattern_text[0].isalpha():
        # Checksum validation for UEN format C
        return self.validate_uen_format_c(pattern_text)
    elif len(pattern_text) == 10:
        # Checksum validation for UEN format B
        return self.validate_uen_format_b(pattern_text)

    return False

validate_uen_format_a staticmethod

validate_uen_format_a(uen: str) -> bool

Validate the UEN format A using checksum.

PARAMETER DESCRIPTION
uen

The UEN to validate.

TYPE: str

RETURNS DESCRIPTION
bool

True if the UEN is valid according to its respective format, False otherwise.

Source code in presidio_analyzer/predefined_recognizers/country_specific/singapore/sg_uen_recognizer.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
@staticmethod
def validate_uen_format_a(uen: str) -> bool:
    """
    Validate the UEN format A using checksum.

    :param uen: The UEN to validate.
    :return: True if the UEN is valid according to its respective
    format, False otherwise.
    """
    check_digit = uen[-1]

    weighted_sum = sum(
        int(n) * w for n, w in zip(uen[:-1], SgUenRecognizer.UEN_FORMAT_A_WEIGHT)
    )

    checksum = SgUenRecognizer.UEN_FORMAT_A_ALPHABET[weighted_sum % 11]

    return check_digit == checksum

validate_uen_format_b staticmethod

validate_uen_format_b(uen: str) -> bool

Validate the UEN format B using checksum.

PARAMETER DESCRIPTION
uen

The UEN to validate.

TYPE: str

RETURNS DESCRIPTION
bool

True if the UEN is valid according to its respective format, False otherwise.

Source code in presidio_analyzer/predefined_recognizers/country_specific/singapore/sg_uen_recognizer.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@staticmethod
def validate_uen_format_b(uen: str) -> bool:
    """
    Validate the UEN format B using checksum.

    :param uen: The UEN to validate.
    :return: True if the UEN is valid according to its respective
    format, False otherwise.
    """
    check_digit = uen[-1]
    year_of_registration = int(uen[0:4])

    # Check if the year of registration is not in the future
    if year_of_registration > date.today().year:
        return False

    weighted_sum = sum(
        int(n) * w for n, w in zip(uen[:-1], SgUenRecognizer.UEN_FORMAT_B_WEIGHT)
    )

    checksum = SgUenRecognizer.UEN_FORMAT_B_ALPHABET[weighted_sum % 11]

    return check_digit == checksum

validate_uen_format_c staticmethod

validate_uen_format_c(uen: str) -> bool

Validate the UEN format C using checksum.

PARAMETER DESCRIPTION
uen

The UEN to validate.

TYPE: str

RETURNS DESCRIPTION
bool

True if the UEN is valid according to its respective format, False otherwise.

Source code in presidio_analyzer/predefined_recognizers/country_specific/singapore/sg_uen_recognizer.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
@staticmethod
def validate_uen_format_c(uen: str) -> bool:
    """
    Validate the UEN format C using checksum.

    :param uen: The UEN to validate.
    :return: True if the UEN is valid according to its respective
    format, False otherwise.
    """
    check_digit = uen[-1]

    if uen[0] not in SgUenRecognizer.UEN_FORMAT_C_PREFIX:
        return False

    entity_type = uen[3:5]

    if entity_type not in SgUenRecognizer.UEN_FORMAT_C_ENTITY_TYPE:
        return False

    weighted_sum = sum(
        SgUenRecognizer.UEN_FORMAT_C_ALPHABET.index(n) * w
        for n, w in zip(uen[:-1], SgUenRecognizer.UEN_FORMAT_C_WEIGHT)
    )

    checksum = SgUenRecognizer.UEN_FORMAT_C_ALPHABET[(weighted_sum - 5) % 11]

    return check_digit == checksum

ZaIdNumberRecognizer

Bases: PatternRecognizer

Recognize South African ID numbers using regex and structural validation.

South African identity numbers are 13 digits in the YYMMDDSSSSCAZ layout: - YYMMDD: date of birth (two-digit year mapped to a full year using a rolling pivot: if YY is greater than the last two digits of the current calendar year, the year is interpreted as 19YY; otherwise 20YY. The resulting date must be valid and cannot be in the future.) - SSSS: sequence number - C: citizenship / status — 0 = SA citizen, 1 = permanent resident, 2 = refugee - A: obsolete race-classification digit (typically 8 or 9) - Z: check digit using the Luhn algorithm

Reference: https://en.wikipedia.org/wiki/South_African_identity_card#Identity_Number https://www.irb-cisr.gc.ca/en/country-information/rir/Pages/index.aspx?doc=454798

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'ZA_ID_NUMBER'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/south_africa/za_id_number_recognizer.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class ZaIdNumberRecognizer(PatternRecognizer):
    """
    Recognize South African ID numbers using regex and structural validation.

    South African identity numbers are 13 digits in the ``YYMMDDSSSSCAZ``
    layout:
    - ``YYMMDD``: date of birth (two-digit year mapped to a full year using a
      rolling pivot: if ``YY`` is greater than the last two digits of the
      current calendar year, the year is interpreted as 19YY; otherwise 20YY.
      The resulting date must be valid and cannot be in the future.)
    - ``SSSS``: sequence number
    - ``C``: citizenship / status — 0 = SA citizen, 1 = permanent resident,
      2 = refugee
    - ``A``: obsolete race-classification digit (typically 8 or 9)
    - ``Z``: check digit using the Luhn algorithm

    Reference:
    https://en.wikipedia.org/wiki/South_African_identity_card#Identity_Number
    https://www.irb-cisr.gc.ca/en/country-information/rir/Pages/index.aspx?doc=454798

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "za"

    ID_LENGTH = 13
    BIRTH_DATE_LENGTH = 6
    CITIZENSHIP_INDEX = 10
    LEGACY_RACE_INDEX = 11
    ALLOWED_CITIZENSHIP_VALUES = frozenset({"0", "1", "2"})
    ALLOWED_LEGACY_RACE_VALUES = frozenset({"8", "9"})

    PATTERNS = [
        Pattern(
            "South African ID Number",
            r"\b\d{10}[0-2][89]\d\b",
            0.2,
        ),
    ]

    CONTEXT = [
        "id",
        "identity",
        "identity number",
        "id number",
        "south african id",
        "rsa id",
        "smart id",
        "national id",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "ZA_ID_NUMBER",
        name: Optional[str] = None,
    ):
        patterns = self.PATTERNS if patterns is None else patterns
        context = self.CONTEXT if context is None else context
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:  # noqa: D102
        if len(pattern_text) != self.ID_LENGTH or not pattern_text.isdigit():
            return False

        if not self._has_valid_birth_date(pattern_text[: self.BIRTH_DATE_LENGTH]):
            return False

        if pattern_text[self.CITIZENSHIP_INDEX] not in self.ALLOWED_CITIZENSHIP_VALUES:
            return False

        if pattern_text[self.LEGACY_RACE_INDEX] not in self.ALLOWED_LEGACY_RACE_VALUES:
            return False

        return self._is_luhn_valid(pattern_text)

    @staticmethod
    def _has_valid_birth_date(date_part: str) -> bool:
        month = int(date_part[2:4])
        day = int(date_part[4:6])
        year_suffix = int(date_part[:2])

        today = date.today()
        pivot = today.year % 100
        century = 1900 if year_suffix > pivot else 2000

        try:
            birth_date = date(century + year_suffix, month, day)
        except ValueError:
            return False

        return birth_date <= today

    @staticmethod
    def _is_luhn_valid(value: str) -> bool:
        digits = [int(digit) for digit in value]
        checksum = 0
        parity = len(digits) % 2

        for index, digit in enumerate(digits):
            if index % 2 == parity:
                digit *= 2
                if digit > 9:
                    digit -= 9
            checksum += digit

        return checksum % 10 == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

EsNieRecognizer

Bases: PatternRecognizer

Recognize NIE number using regex and checksum.

Reference(s): https://es.wikipedia.org/wiki/N%C3%BAmero_de_identidad_de_extranjero https://www.interior.gob.es/opencms/ca/servicios-al-ciudadano/tramites-y-gestiones/dni/calculo-del-digito-de-control-del-nif-nie/

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'es'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'ES_NIE'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern by using the control character.

Source code in presidio_analyzer/predefined_recognizers/country_specific/spain/es_nie_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class EsNieRecognizer(PatternRecognizer):
    """
    Recognize NIE number using regex and checksum.

    Reference(s):
    https://es.wikipedia.org/wiki/N%C3%BAmero_de_identidad_de_extranjero
    https://www.interior.gob.es/opencms/ca/servicios-al-ciudadano/tramites-y-gestiones/dni/calculo-del-digito-de-control-del-nif-nie/

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes
    or spaces.
    """

    COUNTRY_CODE = "es"

    PATTERNS = [
        Pattern(
            "NIE",
            r"\b[X-Z]?[0-9]?[0-9]{7}[-]?[A-Z]\b",
            0.5,
        ),
    ]

    CONTEXT = ["número de identificación de extranjero", "NIE"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "es",
        supported_entity: str = "ES_NIE",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """Validate the pattern by using the control character."""

        pattern_text = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        letters = "TRWAGMYFPDXBNJZSQVHLCKE"
        letter = pattern_text[-1]

        # check last is a letter, and first is in X,Y,Z
        if not pattern_text[1:-1].isdigit or pattern_text[:1] not in "XYZ":
            return False
        # check size is 8 or 9
        if len(pattern_text) < 8 or len(pattern_text) > 9:
            return False

        # replace XYZ with 012, and check the mod 23
        number = int(str("XYZ".index(pattern_text[0])) + pattern_text[1:-1])
        return letter == letters[number % 23]

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the pattern by using the control character.

Source code in presidio_analyzer/predefined_recognizers/country_specific/spain/es_nie_recognizer.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def validate_result(self, pattern_text: str) -> bool:
    """Validate the pattern by using the control character."""

    pattern_text = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    letters = "TRWAGMYFPDXBNJZSQVHLCKE"
    letter = pattern_text[-1]

    # check last is a letter, and first is in X,Y,Z
    if not pattern_text[1:-1].isdigit or pattern_text[:1] not in "XYZ":
        return False
    # check size is 8 or 9
    if len(pattern_text) < 8 or len(pattern_text) > 9:
        return False

    # replace XYZ with 012, and check the mod 23
    number = int(str("XYZ".index(pattern_text[0])) + pattern_text[1:-1])
    return letter == letters[number % 23]

EsNifRecognizer

Bases: PatternRecognizer

Recognize NIF number using regex and checksum.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'es'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'ES_NIF'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/spain/es_nif_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class EsNifRecognizer(PatternRecognizer):
    """
    Recognize NIF number using regex and checksum.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "es"

    PATTERNS = [
        Pattern(
            "NIF",
            r"\b[0-9]?[0-9]{7}[-]?[A-Z]\b",
            0.5,
        ),
    ]

    CONTEXT = ["documento nacional de identidad", "DNI", "NIF", "identificación"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "es",
        supported_entity: str = "ES_NIF",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:  # noqa: D102
        pattern_text = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        letter = pattern_text[-1]
        number = int("".join(filter(str.isdigit, pattern_text)))
        letters = "TRWAGMYFPDXBNJZSQVHLCKE"
        return letter == letters[number % 23]

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

EsPassportRecognizer

Bases: PatternRecognizer

Recognize Spanish passport numbers using regex.

Follows the format: 3 letters followed by 6 digits (e.g. AAA123456).

Reference(s): https://en.wikipedia.org/wiki/Spanish_passport

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'es'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'ES_PASSPORT'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/spain/es_passport_recognizer.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class EsPassportRecognizer(PatternRecognizer):
    """
    Recognize Spanish passport numbers using regex.

    Follows the format: 3 letters followed by 6 digits (e.g. AAA123456).

    Reference(s):
    https://en.wikipedia.org/wiki/Spanish_passport

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "es"

    PATTERNS = [
        Pattern(
            "ES_PASSPORT",
            r"\b[A-Z]{3}[0-9]{6}\b",
            0.05,
        ),
    ]

    CONTEXT = ["pasaporte", "passport", "número de pasaporte", "passport number"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "es",
        supported_entity: str = "ES_PASSPORT",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
            global_regex_flags=re.DOTALL | re.MULTILINE | re.IGNORECASE,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

SeOrganisationsnummerRecognizer

Bases: PatternRecognizer

Recognizes and validates Swedish Organisationsnummer.

Rules: * 10 digits: NNNNNNXXXX (optional hyphen) * Third digit >= 2 * Luhn checksum validation

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate Organisationsnummer.

Source code in presidio_analyzer/predefined_recognizers/country_specific/sweden/se_organisationsnummer_recognizer.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
class SeOrganisationsnummerRecognizer(PatternRecognizer):
    """Recognizes and validates Swedish Organisationsnummer.

    Rules:
    * 10 digits: NNNNNNXXXX (optional hyphen)
    * Third digit >= 2
    * Luhn checksum validation
    """

    COUNTRY_CODE = "se"

    PATTERNS = [
        Pattern(
            "Swedish Organisationsnummer (Medium)",
            r"\b\d{6}[-]?\d{4}\b",
            0.6,
        ),
        Pattern(
            "Swedish Organisationsnummer (Weak)",
            r"\d{6}[-]?\d{4}",
            0.2,
        ),
    ]

    CONTEXT = [
        "organisationsnummer",
        "orgnr",
        "org nr",
        "företagsnummer",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "sv",
        supported_entity: str = "SE_ORGANISATIONSNUMMER",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT

        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    # ---------------------------------------------------------
    # Helpers
    # ---------------------------------------------------------
    @staticmethod
    def _numeric_part(orgnr: str) -> str:
        """Extract only digits."""
        return "".join(filter(str.isdigit, orgnr))

    @staticmethod
    def _is_luhn_valid(number: str) -> bool:
        """Validate using Luhn algorithm."""
        digits = [int(d) for d in number]
        checksum = digits[-1]

        luhn_sum = 0
        for i, d in enumerate(reversed(digits[:-1])):
            if i % 2 == 0:
                d *= 2
                if d > 9:
                    d -= 9
            luhn_sum += d

        return (luhn_sum + checksum) % 10 == 0

    @staticmethod
    def _has_valid_third_digit(number: str) -> bool:
        """Third digit must be >= 2."""
        try:
            return int(number[2]) >= 2
        except (ValueError, IndexError):
            return False

    # ---------------------------------------------------------
    # Validation
    # ---------------------------------------------------------
    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """Validate Organisationsnummer."""

        num = self._numeric_part(pattern_text)

        if len(num) != 10:
            return False

        # Rule: third digit >= 2
        if not self._has_valid_third_digit(num):
            return False

        # Luhn check
        return self._is_luhn_valid(num)

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate Organisationsnummer.

Source code in presidio_analyzer/predefined_recognizers/country_specific/sweden/se_organisationsnummer_recognizer.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """Validate Organisationsnummer."""

    num = self._numeric_part(pattern_text)

    if len(num) != 10:
        return False

    # Rule: third digit >= 2
    if not self._has_valid_third_digit(num):
        return False

    # Luhn check
    return self._is_luhn_valid(num)

SePersonnummerRecognizer

Bases: PatternRecognizer

Recognizes and validates Swedish Personal Identity Numbers.

The recogniser follows the full guard‑rail logic: * Regex covers YYMMDD[-+]XXXX and YYYYMMDD[-+]XXXX. * Date validation accommodates samordningsnummer (day ≥ 61). * Luhn checksum is applied to the last 10 digits.

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate a candidate Personnummer.

Source code in presidio_analyzer/predefined_recognizers/country_specific/sweden/se_personnummer_recognizer.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
class SePersonnummerRecognizer(PatternRecognizer):
    """Recognizes and validates Swedish Personal Identity Numbers.

    The recogniser follows the full guard‑rail logic:
    * Regex covers ``YYMMDD[-+]XXXX`` and ``YYYYMMDD[-+]XXXX``.
    * Date validation accommodates samordningsnummer (day ≥ 61).
    * Luhn checksum is applied to the last 10 digits.
    """

    COUNTRY_CODE = "se"

    # ---------------------------------------------------------------------
    #  Regular expressions – allow 6‑ or 8‑digit date parts.
    # ---------------------------------------------------------------------
    PATTERNS = [
        Pattern(
            "Swedish Personnummer (Medium)",
            r"\b(\d{6,8})([-+]?)\d{4}\b",
            0.5,
        ),
        Pattern(
            "Swedish Personnummer (Very Weak)",
            r"(\d{6,8})([-+]?)\d{4}",
            0.1,
        ),
    ]
    CONTEXT = [
        "personnummer",
        "svenskt personnummer",
        "svensk id",
        "ssn",
        "personal identity number",
        "samordningsnummer",
    ]

    # ---------------------------------------------------------------------
    #  Initialisation – keep type‑hint safety for ``name``.
    # ---------------------------------------------------------------------
    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "sv",
        supported_entity: str = "SE_PERSONNUMMER",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    # ---------------------------------------------------------------------
    #  Helper utilities (static – no instance state required)
    # ---------------------------------------------------------------------
    @staticmethod
    def _numeric_part(pnr: str) -> str:
        """Return the last 10 numeric characters of a person‑nummer string."""
        return "".join(filter(str.isdigit, pnr))[-10:]

    @staticmethod
    def _is_luhn_valid(pnr: str) -> bool:
        """Luhn checksum validation for the 10‑digit number.

        The algorithm doubles every second digit from the right (excluding the
        checksum digit), subtracts 9 if the result exceeds 9, and finally checks
        that the total modulo 10 equals 0.
        """
        digits = [int(d) for d in pnr]
        checksum = digits[-1]
        luhn_sum = 0
        for i, d in enumerate(reversed(digits[:-1])):
            if i % 2 == 0:
                d *= 2
                if d > 9:
                    d -= 9
            luhn_sum += d
        return (luhn_sum + checksum) % 10 == 0

    @staticmethod
    def _has_valid_date(pnr: str) -> bool:
        """Validate month and day, handling samordningsnummer.

        ``pnr`` must be the 10‑digit numeric representation.
        """
        try:
            month = int(pnr[2:4])
            day = int(pnr[4:6])
            # Samordningsnummer uses day + 60
            if day >= 61:
                day -= 60
            return 1 <= month <= 12 and 1 <= day <= 31
        except (ValueError, IndexError):
            return False

    # ---------------------------------------------------------------------
    #  Core validation invoked by Presidio.
    # ---------------------------------------------------------------------
    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """Validate a candidate Personnummer.

        The validation pipeline:
        1 Normalise to the last 10 digits.
        2 Verify the date component (including samordningsnummer handling).
        3 Apply the Luhn checksum.
        """
        num = self._numeric_part(pattern_text)
        if len(num) != 10:
            return False

        if not self._has_valid_date(num):
            return False

        return self._is_luhn_valid(num)

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate a candidate Personnummer.

The validation pipeline: 1 Normalise to the last 10 digits. 2 Verify the date component (including samordningsnummer handling). 3 Apply the Luhn checksum.

Source code in presidio_analyzer/predefined_recognizers/country_specific/sweden/se_personnummer_recognizer.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """Validate a candidate Personnummer.

    The validation pipeline:
    1 Normalise to the last 10 digits.
    2 Verify the date component (including samordningsnummer handling).
    3 Apply the Luhn checksum.
    """
    num = self._numeric_part(pattern_text)
    if len(num) != 10:
        return False

    if not self._has_valid_date(num):
        return False

    return self._is_luhn_valid(num)

ThTninRecognizer

Bases: PatternRecognizer

Recognize Thai National ID Number (TNIN).

The Thai National ID Number (TNIN) is a unique 13-digit number issued to all Thai residents.

The format is N1N2N3N4N5N6N7N8N9N10N11N12N13 where: - N1-N12 are the main digits - N13 is a check digit calculated using the preceding 12 digits

Validation rules: - Must be exactly 13 digits - First digit (N1) cannot be 0 - Second digit (N2) cannot be 0 - Second and third digits (N2N3) cannot be: 28, 29, 59, 68, 69, 78, 79, 87, 88, 89, 97, 98, 99 These second and third digits in Thai ID number correspond to Thai provinces, so we exclude non-existent or unassigned combinations in Thailand's administrative division system. See ISO 3166-2:TH for reference. - The 13th digit is a checksum computed modulo 11 from the first 12 digits

Checksum algorithm: - Label first 12 digits N1…N12 (left to right) - Compute S = 13·N1 + 12·N2 + … + 2·N12 - Let x = S mod 11 - Then check digit N13 = (11 − x) mod 10 - Equivalently: if x ≤ 1 then N13 = 1 − x; otherwise N13 = 11 − x

Reference: https://th.wikipedia.org/wiki/เลขประจำตัวประชาชนไทย https://th.wikipedia.org/wiki/ISO_3166-2:TH

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'th'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'TH_TNIN'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/thai/th_tnin_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
class ThTninRecognizer(PatternRecognizer):
    """
    Recognize Thai National ID Number (TNIN).

    The Thai National ID Number (TNIN) is a unique 13-digit number
    issued to all Thai residents.

    The format is N1N2N3N4N5N6N7N8N9N10N11N12N13 where:
    - N1-N12 are the main digits
    - N13 is a check digit calculated using the preceding 12 digits

    Validation rules:
    - Must be exactly 13 digits
    - First digit (N1) cannot be 0
    - Second digit (N2) cannot be 0
    - Second and third digits (N2N3) cannot be: 28, 29, 59, 68, 69, 78, 79,
      87, 88, 89, 97, 98, 99
      These second and third digits in Thai ID number correspond to Thai provinces,
      so we exclude non-existent or unassigned combinations in Thailand's
      administrative division system. See ISO 3166-2:TH for reference.
    - The 13th digit is a checksum computed modulo 11 from the first 12 digits

    Checksum algorithm:
    - Label first 12 digits N1…N12 (left to right)
    - Compute S = 13·N1 + 12·N2 + … + 2·N12
    - Let x = S mod 11
    - Then check digit N13 = (11 − x) mod 10
    - Equivalently: if x ≤ 1 then N13 = 1 − x; otherwise N13 = 11 − x

    Reference: https://th.wikipedia.org/wiki/เลขประจำตัวประชาชนไทย
               https://th.wikipedia.org/wiki/ISO_3166-2:TH


    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    """

    COUNTRY_CODE = "th"

    PATTERNS = [
        Pattern(
            "TNIN (Medium)",
            r"\b[1-9](?:[134][0-9]|[25][0134567]|[67][01234567]|[89][0123456])\d{10}\b",
            0.5,
        )
    ]

    CONTEXT = [
        "Thai National ID",
        "Thai ID Number",
        "TNIN",
        "เลขประจำตัวประชาชน",
        "เลขบัตรประชาชน",
        "รหัสปชช",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "th",
        supported_entity: str = "TH_TNIN",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = replacement_pairs if replacement_pairs else []

        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Union[bool, None]:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool or None, indicating whether the validation was successful.
        """
        # Pre-processing before validation checks
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        # Check if the sanitized value has the correct length (13 digits)
        if len(sanitized_value) != 13:
            return False

        # Check if all characters are digits
        if not sanitized_value.isdigit():
            return False

        # Validate TNIN checksum (format validation is handled by regex)
        return self._validate_checksum(sanitized_value)

    def _validate_checksum(self, tnin: str) -> bool:
        """
        Validate the checksum of Thai TNIN.

        Checksum algorithm:
        - Label first 12 digits N1…N12 (left to right)
        - Compute S = 13·N1 + 12·N2 + … + 2·N12
        - Let x = S mod 11
        - Then check digit N13 = (11 − x) mod 10

        :param tnin: The TNIN to validate
        :return: True if checksum is valid, False otherwise
        """
        # Calculate weighted sum: 13*N1 + 12*N2 + ... + 2*N12
        weights = list(range(13, 1, -1))  # [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2]

        total_sum = 0
        for i in range(12):  # First 12 digits
            total_sum += weights[i] * int(tnin[i])

        # Calculate x = S mod 11
        x = total_sum % 11

        # Calculate expected check digit: (11 - x) mod 10
        if x <= 1:
            expected_check_digit = 1 - x
        else:
            expected_check_digit = 11 - x

        # Compare with actual check digit
        actual_check_digit = int(tnin[12])

        return expected_check_digit == actual_check_digit

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Union[bool, None]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Union[bool, None]

A bool or None, indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/thai/th_tnin_recognizer.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def validate_result(self, pattern_text: str) -> Union[bool, None]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool or None, indicating whether the validation was successful.
    """
    # Pre-processing before validation checks
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    # Check if the sanitized value has the correct length (13 digits)
    if len(sanitized_value) != 13:
        return False

    # Check if all characters are digits
    if not sanitized_value.isdigit():
        return False

    # Validate TNIN checksum (format validation is handled by regex)
    return self._validate_checksum(sanitized_value)

TrLicensePlateRecognizer

Bases: PatternRecognizer

Recognize Turkish vehicle license plates (plaka).

Standard civilian format: [province_code 01-81][1-3 letters] [2-4 digits]. Province codes: 01-81 (81 Turkish provinces). Letters: A-Z excluding Q, W, X (not in Turkish alphabet).

Examples: 34 ABC 1234 (Istanbul), 06 A 123 (Ankara), 35 JK 12 (Izmir).

Legal basis: Karayolları Trafik Kanunu (KTK) Madde 23. Data protection: KVKK (Kişisel Verilerin Korunması Kanunu) — license plates constitute personal data when linked to an identifiable vehicle owner.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'tr'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'TR_LICENSE_PLATE'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the matched pattern by checking province code is 01-81.

Source code in presidio_analyzer/predefined_recognizers/country_specific/turkey/tr_license_plate_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
class TrLicensePlateRecognizer(PatternRecognizer):
    """
    Recognize Turkish vehicle license plates (plaka).

    Standard civilian format: [province_code 01-81] [1-3 letters] [2-4 digits].
    Province codes: 01-81 (81 Turkish provinces).
    Letters: A-Z excluding Q, W, X (not in Turkish alphabet).

    Examples: 34 ABC 1234 (Istanbul), 06 A 123 (Ankara), 35 JK 12 (Izmir).

    Legal basis: Karayolları Trafik Kanunu (KTK) Madde 23.
    Data protection: KVKK (Kişisel Verilerin Korunması Kanunu) — license plates
    constitute personal data when linked to an identifiable vehicle owner.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
        for different strings to be used during pattern matching.
    """

    COUNTRY_CODE = "tr"

    PATTERNS = [
        Pattern(
            "TR License Plate (space)",
            r"\b(0[1-9]|[1-7][0-9]|8[0-1])\s?[A-PR-VY-Z]{1,3}\s?\d{2,4}\b",
            0.3,
        ),
        Pattern(
            "TR License Plate (hyphen)",
            r"\b(0[1-9]|[1-7][0-9]|8[0-1])-[A-PR-VY-Z]{1,3}-\d{2,4}\b",
            0.3,
        ),
    ]

    CONTEXT = [
        "plaka",
        "araç plakası",
        "plaka numarası",
        "kayıt plakası",
        "tr plaka",
        "license plate",
        "number plate",
        "plate",
        "taşıt plakası",
        "kayıt",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "tr",
        supported_entity: str = "TR_LICENSE_PLATE",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Union[bool, None]:
        """
        Validate the matched pattern by checking province code is 01-81.

        :param pattern_text: The matched text to validated.
        Only the part in text that was detected by the regex engine
        :return: True if province code valid, False if invalid, None if not a plate
        """
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        if len(sanitized_value) >= 3:
            province_code = sanitized_value[:2]
            if province_code.isdigit():
                code = int(province_code)
                return 1 <= code <= 81

        return None

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Union[bool, None]

Validate the matched pattern by checking province code is 01-81.

PARAMETER DESCRIPTION
pattern_text

The matched text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Union[bool, None]

True if province code valid, False if invalid, None if not a plate

Source code in presidio_analyzer/predefined_recognizers/country_specific/turkey/tr_license_plate_recognizer.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def validate_result(self, pattern_text: str) -> Union[bool, None]:
    """
    Validate the matched pattern by checking province code is 01-81.

    :param pattern_text: The matched text to validated.
    Only the part in text that was detected by the regex engine
    :return: True if province code valid, False if invalid, None if not a plate
    """
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    if len(sanitized_value) >= 3:
        province_code = sanitized_value[:2]
        if province_code.isdigit():
            code = int(province_code)
            return 1 <= code <= 81

    return None

TrNationalIdRecognizer

Bases: PatternRecognizer

Recognize Turkish National Identification Number (TC Kimlik No / TCKN).

The Turkish National ID is an 11-digit number where: - First digit cannot be 0 - 10th digit = (sum_of_odd_positions * 7 - sum_of_even_positions) % 10 - 11th digit = (sum of first 10 digits) % 10

Checksum validation based on the official Nüfus ve Vatandaşlık İşleri Genel Müdürlüğü (NVI) algorithm, referenced in KVKK compliance guidelines. See: https://tckimlik.nvi.gov.tr/ (NVİ official service portal)

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'tr'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'TR_NATIONAL_ID'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/turkey/tr_national_id_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
class TrNationalIdRecognizer(PatternRecognizer):
    """
    Recognize Turkish National Identification Number (TC Kimlik No / TCKN).

    The Turkish National ID is an 11-digit number where:
    - First digit cannot be 0
    - 10th digit = (sum_of_odd_positions * 7 - sum_of_even_positions) % 10
    - 11th digit = (sum of first 10 digits) % 10

    Checksum validation based on the official Nüfus ve Vatandaşlık İşleri
    Genel Müdürlüğü (NVI) algorithm, referenced in KVKK compliance guidelines.
    See: https://tckimlik.nvi.gov.tr/ (NVİ official service portal)

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    """

    COUNTRY_CODE = "tr"

    PATTERNS = [
        Pattern(
            "TR_NATIONAL_ID",
            r"\b[1-9][0-9]{10}\b",
            0.3,
        ),
    ]

    CONTEXT = [
        "tc kimlik",
        "kimlik no",
        "kimlik numarası",
        "tckn",
        "tc no",
        "nüfus cüzdanı",
        "national id",
        "turkish id",
        "türk kimlik",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "tr",
        supported_entity: str = "TR_NATIONAL_ID",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = replacement_pairs if replacement_pairs else []

        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Union[bool, None]:
        """
        Validate the pattern logic by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool or None, indicating whether the validation was successful.
        """
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        if len(sanitized_value) != 11 or not sanitized_value.isdigit():
            return False

        if sanitized_value[0] == "0":
            return False

        return self._validate_checksum(sanitized_value)

    def _validate_checksum(self, tckn: str) -> bool:
        """
        Validate Turkish National ID using the official NVI checksum algorithm.

        :param tckn: The TCKN to validate
        :return: True if checksum is valid, False otherwise
        """
        digits = [int(d) for d in tckn]

        odd_sum = sum(digits[i] for i in range(0, 9, 2))
        even_sum = sum(digits[i] for i in range(1, 8, 2))

        tenth = (odd_sum * 7 - even_sum) % 10
        if tenth != digits[9]:
            return False

        eleventh = sum(digits[:10]) % 10
        if eleventh != digits[10]:
            return False

        return True

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Union[bool, None]

Validate the pattern logic by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Union[bool, None]

A bool or None, indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/turkey/tr_national_id_recognizer.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def validate_result(self, pattern_text: str) -> Union[bool, None]:
    """
    Validate the pattern logic by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool or None, indicating whether the validation was successful.
    """
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    if len(sanitized_value) != 11 or not sanitized_value.isdigit():
        return False

    if sanitized_value[0] == "0":
        return False

    return self._validate_checksum(sanitized_value)

UkDrivingLicenceRecognizer

Bases: PatternRecognizer

Recognizes UK driving licence numbers issued by the DVLA.

The licence number is a 16-character alphanumeric string encoding the holder's surname, date of birth, and initials.

Format: SSSSS NMMDD NIIXA A - Positions 1-5: Surname (A-Z, padded with trailing 9s) - Position 6: Decade digit of birth year - Positions 7-8: Month of birth (01-12, or 51-62 for female) - Positions 9-10: Day of birth (01-31) - Position 11: Last digit of birth year - Positions 12-13: Initials (A-Z or 9 if none) - Position 14: Arbitrary/check digit - Positions 15-16: Computer check digits

Reference: https://en.wikipedia.org/wiki/Driver%27s_license_in_the_United_Kingdom

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'UK_DRIVING_LICENCE'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic for a UK driving licence number.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_driving_licence_recognizer.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
class UkDrivingLicenceRecognizer(PatternRecognizer):
    """
    Recognizes UK driving licence numbers issued by the DVLA.

    The licence number is a 16-character alphanumeric string encoding
    the holder's surname, date of birth, and initials.

    Format: SSSSS NMMDD NIIXA A
      - Positions 1-5: Surname (A-Z, padded with trailing 9s)
      - Position 6: Decade digit of birth year
      - Positions 7-8: Month of birth (01-12, or 51-62 for female)
      - Positions 9-10: Day of birth (01-31)
      - Position 11: Last digit of birth year
      - Positions 12-13: Initials (A-Z or 9 if none)
      - Position 14: Arbitrary/check digit
      - Positions 15-16: Computer check digits

    Reference: https://en.wikipedia.org/wiki/Driver%27s_license_in_the_United_Kingdom

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "uk"

    PATTERNS = [
        Pattern(
            "UK Driving Licence",
            r"\b[A-Z9]{5}[0-9](?:0[1-9]|1[0-2]|5[1-9]|6[0-2])(?:0[1-9]|[12][0-9]|3[01])[0-9][A-Z9]{2}[A-Z0-9][A-Z]{2}\b",  # noqa: E501
            0.5,
        ),
    ]

    CONTEXT = [
        "driving licence",
        "driving license",
        "driver's licence",
        "driver's license",
        "dvla",
        "dl number",
        "licence number",
        "license number",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "UK_DRIVING_LICENCE",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the pattern logic for a UK driving licence number.

        Since the DVLA check digit algorithm is not public, this method
        cannot confirm a valid licence (returns None). It can however
        reject clearly invalid patterns (returns False).

        :param pattern_text: the text to validate.
        :return: None if the pattern is plausible, False if clearly invalid.
        """
        text = pattern_text.upper()

        # Reject if surname portion is all 9s (no valid surname)
        if text[:5] == "99999":
            return False

        # Surname must have letters before any 9-padding (9s only trailing)
        surname = text[:5]
        if not re.match(r"^[A-Z]+9*$", surname):
            return False

        return None

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic for a UK driving licence number.

Since the DVLA check digit algorithm is not public, this method cannot confirm a valid licence (returns None). It can however reject clearly invalid patterns (returns False).

PARAMETER DESCRIPTION
pattern_text

the text to validate.

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

None if the pattern is plausible, False if clearly invalid.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_driving_licence_recognizer.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic for a UK driving licence number.

    Since the DVLA check digit algorithm is not public, this method
    cannot confirm a valid licence (returns None). It can however
    reject clearly invalid patterns (returns False).

    :param pattern_text: the text to validate.
    :return: None if the pattern is plausible, False if clearly invalid.
    """
    text = pattern_text.upper()

    # Reject if surname portion is all 9s (no valid surname)
    if text[:5] == "99999":
        return False

    # Surname must have letters before any 9-padding (9s only trailing)
    surname = text[:5]
    if not re.match(r"^[A-Z]+9*$", surname):
        return False

    return None

NhsRecognizer

Bases: PatternRecognizer

Recognizes NHS number using regex and checksum.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'UK_NHS'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_nhs_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class NhsRecognizer(PatternRecognizer):
    """
    Recognizes NHS number using regex and checksum.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "uk"

    PATTERNS = [
        Pattern(
            "NHS (medium)",
            r"\b([0-9]{3})[- ]?([0-9]{3})[- ]?([0-9]{4})\b",
            0.5,
        ),
    ]

    CONTEXT = [
        "national health service",
        "nhs",
        "health services authority",
        "health authority",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "UK_NHS",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """
        Validate the pattern logic e.g., by running checksum on a detected pattern.

        :param pattern_text: the text to validated.
        Only the part in text that was detected by the regex engine
        :return: A bool indicating whether the validation was successful.
        """
        text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
        total = sum(
            [int(c) * multiplier for c, multiplier in zip(text, reversed(range(11)))]
        )
        remainder = total % 11
        check_remainder = remainder == 0

        return check_remainder

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
bool

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_nhs_recognizer.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def validate_result(self, pattern_text: str) -> bool:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    text = EntityRecognizer.sanitize_value(pattern_text, self.replacement_pairs)
    total = sum(
        [int(c) * multiplier for c, multiplier in zip(text, reversed(range(11)))]
    )
    remainder = total % 11
    check_remainder = remainder == 0

    return check_remainder

UkNinoRecognizer

Bases: PatternRecognizer

Recognizes UK National Insurance Number using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'UK_NINO'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_nino_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class UkNinoRecognizer(PatternRecognizer):
    """
    Recognizes UK National Insurance Number using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "uk"

    PATTERNS = [
        Pattern(
            "NINO (medium)",
            r"\b(?!bg|gb|nk|kn|nt|tn|zz|BG|GB|NK|KN|NT|TN|ZZ) ?([a-ceghj-pr-tw-zA-CEGHJ-PR-TW-Z]{1}[a-ceghj-npr-tw-zA-CEGHJ-NPR-TW-Z]{1}) ?([0-9]{2}) ?([0-9]{2}) ?([0-9]{2}) ?([a-dA-D{1}])\b",  # noqa: E501
            0.5,
        ),
    ]

    CONTEXT = ["national insurance", "ni number", "nino"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "UK_NINO",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UkPassportRecognizer

Bases: PatternRecognizer

Recognizes UK passport numbers using regex.

UK passports issued from 2015 onwards use a 2-letter prefix followed by 7 digits (e.g., AB1234567).

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'UK_PASSPORT'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_passport_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class UkPassportRecognizer(PatternRecognizer):
    """
    Recognizes UK passport numbers using regex.

    UK passports issued from 2015 onwards use a 2-letter prefix
    followed by 7 digits (e.g., AB1234567).

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "uk"

    PATTERNS = [
        Pattern(
            "UK Passport (weak)",
            r"\b[A-Z]{2}\d{7}\b",
            0.1,
        ),
    ]

    CONTEXT = [
        "passport",
        "passport number",
        "travel document",
        "uk passport",
        "british passport",
        "her majesty",
        "his majesty",
        "hm passport",
        "hmpo",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "UK_PASSPORT",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UkPostcodeRecognizer

Bases: PatternRecognizer

Recognizes UK postcodes using regex.

UK postcodes follow strict position-specific letter rules across six formats (A9 9AA, A99 9AA, A9A 9AA, AA9 9AA, AA99 9AA, AA9A 9AA) plus the special GIR 0AA code.

Reference: https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'UK_POSTCODE'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_postcode_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class UkPostcodeRecognizer(PatternRecognizer):
    """
    Recognizes UK postcodes using regex.

    UK postcodes follow strict position-specific letter rules across
    six formats (A9 9AA, A99 9AA, A9A 9AA, AA9 9AA, AA99 9AA, AA9A 9AA)
    plus the special GIR 0AA code.

    Reference: https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "uk"

    PATTERNS = [
        Pattern(
            "UK Postcode",
            r"\b("
            r"GIR\s?0AA"
            r"|[A-PR-UWYZ][0-9][ABCDEFGHJKPSTUW]?\s?[0-9][ABD-HJLNP-UW-Z]{2}"
            r"|[A-PR-UWYZ][0-9]{2}\s?[0-9][ABD-HJLNP-UW-Z]{2}"
            r"|[A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]?\s?[0-9][ABD-HJLNP-UW-Z]{2}"
            r"|[A-PR-UWYZ][A-HK-Y][0-9]{2}\s?[0-9][ABD-HJLNP-UW-Z]{2}"
            r")\b",
            0.1,
        ),
    ]

    CONTEXT = [
        "postcode",
        "post code",
        "postal code",
        "zip",
        "address",
        "delivery",
        "mailing",
        "shipping",
        "correspondence",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "UK_POSTCODE",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UkVehicleRegistrationRecognizer

Bases: PatternRecognizer

Recognizes UK vehicle registration numbers using regex.

Supports three formats still commonly seen on the road:

  • Current (2001+): 2 area letters + 2-digit age id + 3 random letters e.g. AB51 ABC
  • Prefix (1983-2001): year letter + 1-3 digits + 3 letters e.g. A123 BCD
  • Suffix (1963-1983): 3 letters + 1-3 digits + year letter e.g. ABC 123D
PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'UK_VEHICLE_REGISTRATION'

replacement_pairs

List of tuples for replacing characters in the pattern text for validation

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the matched pattern.

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_vehicle_registration_recognizer.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
class UkVehicleRegistrationRecognizer(PatternRecognizer):
    """
    Recognizes UK vehicle registration numbers using regex.

    Supports three formats still commonly seen on the road:

    - Current (2001+): 2 area letters + 2-digit age id + 3 random letters
      e.g. AB51 ABC
    - Prefix (1983-2001): year letter + 1-3 digits + 3 letters
      e.g. A123 BCD
    - Suffix (1963-1983): 3 letters + 1-3 digits + year letter
      e.g. ABC 123D

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples for replacing characters in the
        pattern text for validation
    """

    COUNTRY_CODE = "uk"

    PATTERNS = [
        Pattern(
            "UK Vehicle Registration (current)",
            r"\b[A-HJ-PR-Y][A-HJ-PR-Y](?:0[1-9]|[1-7][0-9])[- ]?[A-HJ-PR-Z]{3}\b",
            0.3,
        ),
        Pattern(
            "UK Vehicle Registration (prefix)",
            r"\b[A-HJ-NPR-TV-Y]\d{1,3}[- ]?[A-HJ-PR-Y][A-HJ-PR-Z]{2}\b",
            0.2,
        ),
        Pattern(
            "UK Vehicle Registration (suffix)",
            r"\b[A-HJ-PR-Z]{3}[- ]?\d{1,3}[- ]?[A-HJ-NPR-TV-Y]\b",
            0.15,
        ),
    ]

    CONTEXT = [
        "vehicle",
        "registration",
        "number plate",
        "licence plate",
        "license plate",
        "reg",
        "vrn",
        "dvla",
        "v5c",
        "logbook",
        "mot",
        "car",
        "insured vehicle",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "UK_VEHICLE_REGISTRATION",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> Optional[bool]:
        """
        Validate the matched pattern.

        Only the current format (2001+) is validated further by checking
        that the two-digit age identifier falls in a valid range:
        02-29 (March registrations) or 51-79 (September registrations).

        Prefix and suffix formats return None (keep base score).

        :param pattern_text: The matched text to validate
        :return: True if valid current format, False if invalid current
            format, None for prefix/suffix formats
        """
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )

        # Current format is exactly 7 chars after sanitization
        if len(sanitized_value) == 7 and sanitized_value[:2].isalpha():
            age_id_str = sanitized_value[2:4]
            if age_id_str.isdigit():
                age_id = int(age_id_str)
                return (2 <= age_id <= 29) or (51 <= age_id <= 79)

        return None

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the matched pattern.

Only the current format (2001+) is validated further by checking that the two-digit age identifier falls in a valid range: 02-29 (March registrations) or 51-79 (September registrations).

Prefix and suffix formats return None (keep base score).

PARAMETER DESCRIPTION
pattern_text

The matched text to validate

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

True if valid current format, False if invalid current format, None for prefix/suffix formats

Source code in presidio_analyzer/predefined_recognizers/country_specific/uk/uk_vehicle_registration_recognizer.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the matched pattern.

    Only the current format (2001+) is validated further by checking
    that the two-digit age identifier falls in a valid range:
    02-29 (March registrations) or 51-79 (September registrations).

    Prefix and suffix formats return None (keep base score).

    :param pattern_text: The matched text to validate
    :return: True if valid current format, False if invalid current
        format, None for prefix/suffix formats
    """
    sanitized_value = EntityRecognizer.sanitize_value(
        pattern_text, self.replacement_pairs
    )

    # Current format is exactly 7 chars after sanitization
    if len(sanitized_value) == 7 and sanitized_value[:2].isalpha():
        age_id_str = sanitized_value[2:4]
        if age_id_str.isdigit():
            age_id = int(age_id_str)
            return (2 <= age_id <= 29) or (51 <= age_id <= 79)

    return None

AbaRoutingRecognizer

Bases: PatternRecognizer

Recognize American Banking Association (ABA) routing number.

Also known as routing transit number (RTN) and used to identify financial institutions and process transactions.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'ABA_ROUTING_NUMBER'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/aba_routing_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class AbaRoutingRecognizer(PatternRecognizer):
    """
    Recognize American Banking Association (ABA) routing number.

    Also known as routing transit number (RTN) and used to identify financial
    institutions and process transactions.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "us"

    PATTERNS = [
        Pattern(
            "ABA routing number (weak)",
            r"\b[0123678]\d{8}\b",
            0.05,
        ),
        Pattern(
            "ABA routing number",
            r"\b[0123678]\d{3}-\d{4}-\d\b",
            0.3,
        ),
    ]

    CONTEXT = [
        "aba",
        "routing",
        "abarouting",
        "association",
        "bankrouting",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "ABA_ROUTING_NUMBER",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = replacement_pairs or [("-", "")]
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:  # noqa: D102
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        return self.__checksum(sanitized_value)

    @staticmethod
    def __checksum(sanitized_value: str) -> bool:
        s = 0
        for idx, m in enumerate([3, 7, 1, 3, 7, 1, 3, 7, 1]):
            s += int(sanitized_value[idx]) * m
        return s % 10 == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

MedicalLicenseRecognizer

Bases: PatternRecognizer

Recognize common Medical license numbers using regex + checksum.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'MEDICAL_LICENSE'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/medical_license_recognizer.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class MedicalLicenseRecognizer(PatternRecognizer):
    """
    Recognize common Medical license numbers using regex + checksum.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    COUNTRY_CODE = "us"

    PATTERNS = [
        Pattern(
            "USA DEA Certificate Number (weak)",
            r"[abcdefghjklmprstuxABCDEFGHJKLMPRSTUX]{1}[a-zA-Z]{1}\d{7}|"
            r"[abcdefghjklmprstuxABCDEFGHJKLMPRSTUX]{1}9\d{7}",
            0.4,
        ),
    ]

    CONTEXT = ["medical", "certificate", "DEA"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "MEDICAL_LICENSE",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:  # noqa: D102
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        checksum = self.__luhn_checksum(sanitized_value)

        return checksum

    @staticmethod
    def __luhn_checksum(sanitized_value: str) -> bool:
        def digits_of(n: str) -> List[int]:
            return [int(dig) for dig in str(n)]

        digits = digits_of(sanitized_value[2:])
        checksum = digits.pop()
        even_digits = digits[-1::-2]
        odd_digits = digits[-2::-2]
        checksum *= -1
        checksum += 2 * sum(even_digits) + sum(odd_digits)
        return checksum % 10 == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UsBankRecognizer

Bases: PatternRecognizer

Recognizes US bank number using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'US_BANK_NUMBER'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_bank_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class UsBankRecognizer(PatternRecognizer):
    """
    Recognizes US bank number using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "us"

    PATTERNS = [
        Pattern(
            "Bank Account (weak)",
            r"\b[0-9]{8,17}\b",
            0.05,
        ),
    ]

    CONTEXT = [
        # Task #603: Support keyphrases: change to "checking account"
        # as part of keyphrase change
        "check",
        "account",
        "account#",
        "acct",
        "bank",
        "save",
        "debit",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "US_BANK_NUMBER",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UsLicenseRecognizer

Bases: PatternRecognizer

Recognizes US driver license using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'US_DRIVER_LICENSE'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_driver_license_recognizer.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class UsLicenseRecognizer(PatternRecognizer):
    """
    Recognizes US driver license using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "us"

    PATTERNS = [
        Pattern(
            "Driver License - Alphanumeric (weak)",
            r"\b([A-Z][0-9]{3,6}|[A-Z][0-9]{5,9}|[A-Z][0-9]{6,8}|[A-Z][0-9]{4,8}|[A-Z][0-9]{9,11}|[A-Z]{1,2}[0-9]{5,6}|H[0-9]{8}|V[0-9]{6}|X[0-9]{8}|A-Z]{2}[0-9]{2,5}|[A-Z]{2}[0-9]{3,7}|[0-9]{2}[A-Z]{3}[0-9]{5,6}|[A-Z][0-9]{13,14}|[A-Z][0-9]{18}|[A-Z][0-9]{6}R|[A-Z][0-9]{9}|[A-Z][0-9]{1,12}|[0-9]{9}[A-Z]|[A-Z]{2}[0-9]{6}[A-Z]|[0-9]{8}[A-Z]{2}|[0-9]{3}[A-Z]{2}[0-9]{4}|[A-Z][0-9][A-Z][0-9][A-Z]|[0-9]{7,8}[A-Z])\b",
            0.3,
        ),
        Pattern(
            "Driver License - Digits (very weak)",
            r"\b([0-9]{6,14}|[0-9]{16})\b",
            0.01,
        ),
    ]

    CONTEXT = [
        "driver",
        "license",
        "permit",
        "lic",
        "identification",
        "dls",
        "cdls",
        "lic#",
        "driving",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "US_DRIVER_LICENSE",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            supported_language=supported_language,
            patterns=patterns,
            context=context,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UsItinRecognizer

Bases: PatternRecognizer

Recognizes US ITIN (Individual Taxpayer Identification Number) using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'US_ITIN'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_itin_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class UsItinRecognizer(PatternRecognizer):
    """
    Recognizes US ITIN (Individual Taxpayer Identification Number) using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "us"

    PATTERNS = [
        Pattern(
            "Itin (very weak)",
            r"\b9\d{2}[- ](5\d|6[0-5]|7\d|8[0-8]|9([0-2]|[4-9]))\d{4}\b|\b9\d{2}(5\d|6[0-5]|7\d|8[0-8]|9([0-2]|[4-9]))[- ]\d{4}\b",  # noqa: E501
            0.05,
        ),
        Pattern(
            "Itin (weak)",
            r"\b9\d{2}(5\d|6[0-5]|7\d|8[0-8]|9([0-2]|[4-9]))\d{4}\b",
            0.3,
        ),
        Pattern(
            "Itin (medium)",
            r"\b9\d{2}[- ](5\d|6[0-5]|7\d|8[0-8]|9([0-2]|[4-9]))[- ]\d{4}\b",
            0.5,
        ),
    ]

    CONTEXT = ["individual", "taxpayer", "itin", "tax", "payer", "taxid", "tin"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "US_ITIN",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UsMbiRecognizer

Bases: PatternRecognizer

Recognize US Medicare Beneficiary Identifier (MBI) using regex.

The MBI is an 11-character identifier used by Medicare. The format follows specific rules defined by CMS (Centers for Medicare & Medicaid Services): https://www.cms.gov/medicare/new-medicare-card/understanding-new-medicare-beneficiary-identifier-mbi

Format: C A AN N A AN N A A N N Where: - C = numeric character (0-9) - A = alphabetic character (excluding S, L, O, I, B, Z) - AN = alphanumeric character (numeric or alphabetic, excluding S, L, O, I, B, Z)

Position rules: - Positions 1, 4, 7, 10, 11: numeric (0-9) - Positions 2, 5, 8, 9: alphabetic - Positions 3, 6: alphanumeric

Example: 1EG4-TE5-MK73 (dashes are for display only)

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'US_MBI'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_mbi_recognizer.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
class UsMbiRecognizer(PatternRecognizer):
    """Recognize US Medicare Beneficiary Identifier (MBI) using regex.

    The MBI is an 11-character identifier used by Medicare. The format follows
    specific rules defined by CMS (Centers for Medicare & Medicaid Services):
    https://www.cms.gov/medicare/new-medicare-card/understanding-new-medicare-beneficiary-identifier-mbi

    Format: C A AN N A AN N A A N N
    Where:
    - C = numeric character (0-9)
    - A = alphabetic character (excluding S, L, O, I, B, Z)
    - AN = alphanumeric character (numeric or alphabetic, excluding S, L, O, I, B, Z)

    Position rules:
    - Positions 1, 4, 7, 10, 11: numeric (0-9)
    - Positions 2, 5, 8, 9: alphabetic
    - Positions 3, 6: alphanumeric

    Example: 1EG4-TE5-MK73 (dashes are for display only)

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "us"

    # Valid letters: A-Z excluding S, L, O, I, B, Z
    # Valid letters are: A, C, D, E, F, G, H, J, K, M, N, P, Q, R, T, U, V, W, X, Y
    VALID_LETTERS = "ACDEFGHJKMNPQRTUVWXY"
    VALID_ALPHANUMERIC = "0-9ACDEFGHJKMNPQRTUVWXY"

    # Regex building blocks
    _NUM = "[0-9]"
    _ALPHA = f"[{VALID_LETTERS}]"
    _ALPHANUM = f"[{VALID_ALPHANUMERIC}]"

    # Full MBI pattern:
    # Pos: 1   2      3        4   5      6        7   8      9      10  11
    #      NUM ALPHA  ALPHANUM NUM ALPHA  ALPHANUM NUM ALPHA  ALPHA  NUM NUM

    # Pattern without dashes (11 consecutive characters)
    _MBI_NO_DASH = (
        f"{_NUM}{_ALPHA}{_ALPHANUM}{_NUM}"
        f"{_ALPHA}{_ALPHANUM}{_NUM}"
        f"{_ALPHA}{_ALPHA}{_NUM}{_NUM}"
    )

    # Pattern with dashes in XXXX-XXX-XXXX format
    _MBI_WITH_DASH = (
        f"{_NUM}{_ALPHA}{_ALPHANUM}{_NUM}-"
        f"{_ALPHA}{_ALPHANUM}{_NUM}-"
        f"{_ALPHA}{_ALPHA}{_NUM}{_NUM}"
    )

    PATTERNS = [
        Pattern(
            "MBI (weak)",
            rf"\b{_MBI_NO_DASH}\b",
            0.3,
        ),
        Pattern(
            "MBI (medium)",
            rf"\b{_MBI_WITH_DASH}\b",
            0.5,
        ),
    ]

    CONTEXT = [
        "medicare",
        "mbi",
        "beneficiary",
        "cms",
        "medicaid",
        "hic",  # Health Insurance Claim number (predecessor)
        "hicn",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "US_MBI",
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UsNpiRecognizer

Bases: PatternRecognizer

Recognize US National Provider Identifier (NPI) using regex + checksum.

The NPI is a unique 10-digit number assigned to healthcare providers in the United States by CMS (Centers for Medicare & Medicaid Services). It is a HIPAA-mandated identifier that appears on insurance claims, prescriptions, and provider directories.

Format: - Exactly 10 digits - Starts with 1 (Type 1, individual) or 2 (Type 2, organization) - Last digit is a Luhn check digit (using "80840" prefix per CMS spec)

Reference: https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'US_NPI'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_npi_recognizer.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
class UsNpiRecognizer(PatternRecognizer):
    """Recognize US National Provider Identifier (NPI) using regex + checksum.

    The NPI is a unique 10-digit number assigned to healthcare providers in the
    United States by CMS (Centers for Medicare & Medicaid Services). It is a
    HIPAA-mandated identifier that appears on insurance claims, prescriptions,
    and provider directories.

    Format:
    - Exactly 10 digits
    - Starts with 1 (Type 1, individual) or 2 (Type 2, organization)
    - Last digit is a Luhn check digit (using "80840" prefix per CMS spec)

    Reference: https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or
    spaces.
    """

    COUNTRY_CODE = "us"

    PATTERNS = [
        Pattern(
            "NPI (weak)",
            r"\b[12]\d{9}\b",
            0.1,
        ),
        Pattern(
            "NPI (medium)",
            r"\b[12]\d{3}[ -]\d{3}[ -]\d{3}\b",
            0.4,
        ),
    ]

    CONTEXT = [
        "npi",
        "national provider",
        "provider",
        "npi number",
        "provider id",
        "provider identifier",
        "taxonomy",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "US_NPI",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:  # noqa: D102
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        return self.__npi_luhn_checksum(sanitized_value)

    def invalidate_result(self, pattern_text: str) -> bool:  # noqa: D102
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        # Reject degenerate patterns where all body digits are identical
        # (e.g., 1111111111 or 1111111112 where the last digit is a check digit).
        if sanitized_value:
            body = sanitized_value[:-1] if len(sanitized_value) > 1 else sanitized_value
            if body and len(set(body)) == 1:
                return True
        return False

    @staticmethod
    def __npi_luhn_checksum(sanitized_value: str) -> bool:
        """Validate NPI using Luhn algorithm with "80840" prefix per CMS spec.

        Steps:
        1. Take the 10-digit NPI
        2. Prepend "80840" to get a 15-digit number
        3. Apply standard Luhn algorithm: result mod 10 should equal 0
        """
        prefixed = "80840" + sanitized_value
        digits = [int(d) for d in prefixed]

        # Standard Luhn: from rightmost digit, double every second digit
        checksum = 0
        for i, digit in enumerate(reversed(digits)):
            if i % 2 == 1:
                doubled = digit * 2
                checksum += doubled - 9 if doubled > 9 else doubled
            else:
                checksum += digit

        return checksum % 10 == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UsPassportRecognizer

Bases: PatternRecognizer

Recognizes US Passport number using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'US_PASSPORT'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_passport_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class UsPassportRecognizer(PatternRecognizer):
    """
    Recognizes US Passport number using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "us"

    # Weak pattern: all passport numbers are a weak match, e.g., 14019033
    PATTERNS = [
        Pattern("Passport (very weak)", r"(\b[0-9]{9}\b)", 0.05),
        Pattern("Passport Next Generation (very weak)", r"(\b[A-Z][0-9]{8}\b)", 0.1),
    ]
    CONTEXT = ["us", "united", "states", "passport", "passport#", "travel", "document"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "US_PASSPORT",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

UsSsnRecognizer

Bases: PatternRecognizer

Recognize US Social Security Number (SSN) using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'US_SSN'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

build_regex_explanation

Construct an explanation for why this entity was detected.

invalidate_result

Check if the pattern text cannot be validated as a US_SSN entity.

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_ssn_recognizer.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class UsSsnRecognizer(PatternRecognizer):
    """Recognize US Social Security Number (SSN) using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    COUNTRY_CODE = "us"

    PATTERNS = [
        Pattern("SSN1 (very weak)", r"\b([0-9]{5})-([0-9]{4})\b", 0.05),
        Pattern("SSN2 (very weak)", r"\b([0-9]{3})-([0-9]{6})\b", 0.05),
        Pattern("SSN3 (very weak)", r"\b(([0-9]{3})-([0-9]{2})-([0-9]{4}))\b", 0.05),
        Pattern("SSN4 (very weak)", r"\b[0-9]{9}\b", 0.05),
        Pattern("SSN5 (medium)", r"\b([0-9]{3})[- .]([0-9]{2})[- .]([0-9]{4})\b", 0.5),
    ]

    CONTEXT = [
        "social",
        "security",
        # "sec", # Task #603: Support keyphrases ("social sec")
        "ssn",
        "ssns",
        # "ssn#",  # iss:1452 - a # does not work with LemmaContextAwareEnhancer
        # "ss#",  # iss:1452 - a # does not work with LemmaContextAwareEnhancer
        "ssid",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "US_SSN",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def invalidate_result(self, pattern_text: str) -> bool:
        """
        Check if the pattern text cannot be validated as a US_SSN entity.

        :param pattern_text: Text detected as pattern by regex
        :return: True if invalidated
        """
        # if there are delimiters, make sure both delimiters are the same
        delimiter_counts = defaultdict(int)
        for c in pattern_text:
            if c in (".", "-", " "):
                delimiter_counts[c] += 1
        if len(delimiter_counts.keys()) > 1:
            # mismatched delimiters
            return True

        only_digits = "".join(c for c in pattern_text if c.isdigit())
        if all(only_digits[0] == c for c in only_digits):
            # cannot be all same digit
            return True

        if only_digits[3:5] == "00" or only_digits[5:] == "0000":
            # groups cannot be all zeros
            return True

        for sample_ssn in ("000", "666", "123456789", "98765432", "078051120"):
            if only_digits.startswith(sample_ssn):
                return True

        return False

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

invalidate_result

invalidate_result(pattern_text: str) -> bool

Check if the pattern text cannot be validated as a US_SSN entity.

PARAMETER DESCRIPTION
pattern_text

Text detected as pattern by regex

TYPE: str

RETURNS DESCRIPTION
bool

True if invalidated

Source code in presidio_analyzer/predefined_recognizers/country_specific/us/us_ssn_recognizer.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def invalidate_result(self, pattern_text: str) -> bool:
    """
    Check if the pattern text cannot be validated as a US_SSN entity.

    :param pattern_text: Text detected as pattern by regex
    :return: True if invalidated
    """
    # if there are delimiters, make sure both delimiters are the same
    delimiter_counts = defaultdict(int)
    for c in pattern_text:
        if c in (".", "-", " "):
            delimiter_counts[c] += 1
    if len(delimiter_counts.keys()) > 1:
        # mismatched delimiters
        return True

    only_digits = "".join(c for c in pattern_text if c.isdigit())
    if all(only_digits[0] == c for c in only_digits):
        # cannot be all same digit
        return True

    if only_digits[3:5] == "00" or only_digits[5:] == "0000":
        # groups cannot be all zeros
        return True

    for sample_ssn in ("000", "666", "123456789", "98765432", "078051120"):
        if only_digits.startswith(sample_ssn):
            return True

    return False

CreditCardRecognizer

Bases: PatternRecognizer

Recognize common credit card numbers using regex + checksum.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'CREDIT_CARD'

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/generic/credit_card_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class CreditCardRecognizer(PatternRecognizer):
    """
    Recognize common credit card numbers using regex + checksum.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    PATTERNS = [
        Pattern(
            "All Credit Cards (weak)",
            r"\b(?!1\d{12}(?!\d))((4\d{3})|(5[0-5]\d{2})|(6\d{3})|(1\d{3})|(3\d{3}))[- ]?(\d{3,4})[- ]?(\d{3,4})[- ]?(\d{3,5})\b",  # noqa: E501
            0.3,
        ),
    ]

    CONTEXT = [
        "credit",
        "card",
        "visa",
        "mastercard",
        "cc ",
        "amex",
        "discover",
        "jcb",
        "diners",
        "maestro",
        "instapayment",
    ]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "CREDIT_CARD",
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = (
            replacement_pairs if replacement_pairs else [("-", ""), (" ", "")]
        )
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:  # noqa: D102
        sanitized_value = EntityRecognizer.sanitize_value(
            pattern_text, self.replacement_pairs
        )
        checksum = self.__luhn_checksum(sanitized_value)

        return checksum

    @staticmethod
    def __luhn_checksum(sanitized_value: str) -> bool:
        def digits_of(n: str) -> List[int]:
            return [int(dig) for dig in str(n)]

        digits = digits_of(sanitized_value)
        odd_digits = digits[-1::-2]
        even_digits = digits[-2::-2]
        checksum = sum(odd_digits)
        for d in even_digits:
            checksum += sum(digits_of(str(d * 2)))
        return checksum % 10 == 0

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

CryptoRecognizer

Bases: PatternRecognizer

Recognize common crypto account numbers using regex + checksum.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'CRYPTO'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

validate_result

Validate the Bitcoin address using checksum.

bech32_polymod

Compute the Bech32 checksum.

bech32_hrp_expand

Expand the HRP into values for checksum computation.

bech32_verify_checksum

Verify a checksum given HRP and converted data characters.

bech32_decode

Validate a Bech32/Bech32m string, and determine HRP and data.

validate_bech32_address

Validate a Bech32 or Bech32m address.

Source code in presidio_analyzer/predefined_recognizers/generic/crypto_recognizer.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
class CryptoRecognizer(PatternRecognizer):
    """Recognize common crypto account numbers using regex + checksum.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    PATTERNS = [
        Pattern("Crypto (Medium)", r"(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,59}", 0.5),
    ]

    CONTEXT = ["wallet", "btc", "bitcoin", "crypto"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "CRYPTO",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str) -> bool:
        """Validate the Bitcoin address using checksum.

        :param pattern_text: The cryptocurrency address to validate.
        :return: True if the address is valid according to its respective
        format, False otherwise.
        """
        if pattern_text.startswith("1") or pattern_text.startswith("3"):
            # P2PKH or P2SH address validation
            try:
                bcbytes = self.__decode_base58(str.encode(pattern_text))
                checksum = sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]
                return bcbytes[-4:] == checksum
            except ValueError:
                return False
        elif pattern_text.startswith("bc1"):
            # Bech32 or Bech32m address validation
            if CryptoRecognizer.validate_bech32_address(pattern_text)[0]:
                return True
        return False

    @staticmethod
    def __decode_base58(bc: bytes) -> bytes:
        digits58 = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
        origlen = len(bc)
        bc = bc.lstrip(digits58[0:1])

        n = 0
        for char in bc:
            n = n * 58 + digits58.index(char)
        return n.to_bytes(origlen - len(bc) + (n.bit_length() + 7) // 8, "big")

    @staticmethod
    def bech32_polymod(values):
        """Compute the Bech32 checksum."""
        generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3]
        chk = 1
        for value in values:
            top = chk >> 25
            chk = (chk & 0x1FFFFFF) << 5 ^ value
            for i in range(5):
                chk ^= generator[i] if ((top >> i) & 1) else 0
        return chk

    @staticmethod
    def bech32_hrp_expand(hrp):
        """Expand the HRP into values for checksum computation."""
        return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]

    @staticmethod
    def bech32_verify_checksum(hrp, data):
        """Verify a checksum given HRP and converted data characters."""
        const = CryptoRecognizer.bech32_polymod(
            CryptoRecognizer.bech32_hrp_expand(hrp) + data
        )
        if const == 1:
            return BECH32
        if const == BECH32M_CONST:
            return BECH32M
        return None

    @staticmethod
    def bech32_decode(bech):
        """Validate a Bech32/Bech32m string, and determine HRP and data."""
        if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or (
            bech.lower() != bech and bech.upper() != bech
        ):
            return (None, None, None)
        bech = bech.lower()
        pos = bech.rfind("1")
        if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
            return (None, None, None)
        if not all(x in CHARSET for x in bech[pos + 1 :]):
            return (None, None, None)
        hrp = bech[:pos]
        data = [CHARSET.find(x) for x in bech[pos + 1 :]]
        spec = CryptoRecognizer.bech32_verify_checksum(hrp, data)
        if spec is None:
            return (None, None, None)
        return (hrp, data[:-6], spec)

    @staticmethod
    def validate_bech32_address(address):
        """Validate a Bech32 or Bech32m address."""
        hrp, data, spec = CryptoRecognizer.bech32_decode(address)
        if hrp is not None and data is not None:
            return True, spec
        return False, None

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

validate_result

validate_result(pattern_text: str) -> bool

Validate the Bitcoin address using checksum.

PARAMETER DESCRIPTION
pattern_text

The cryptocurrency address to validate.

TYPE: str

RETURNS DESCRIPTION
bool

True if the address is valid according to its respective format, False otherwise.

Source code in presidio_analyzer/predefined_recognizers/generic/crypto_recognizer.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def validate_result(self, pattern_text: str) -> bool:
    """Validate the Bitcoin address using checksum.

    :param pattern_text: The cryptocurrency address to validate.
    :return: True if the address is valid according to its respective
    format, False otherwise.
    """
    if pattern_text.startswith("1") or pattern_text.startswith("3"):
        # P2PKH or P2SH address validation
        try:
            bcbytes = self.__decode_base58(str.encode(pattern_text))
            checksum = sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]
            return bcbytes[-4:] == checksum
        except ValueError:
            return False
    elif pattern_text.startswith("bc1"):
        # Bech32 or Bech32m address validation
        if CryptoRecognizer.validate_bech32_address(pattern_text)[0]:
            return True
    return False

bech32_polymod staticmethod

bech32_polymod(values)

Compute the Bech32 checksum.

Source code in presidio_analyzer/predefined_recognizers/generic/crypto_recognizer.py
86
87
88
89
90
91
92
93
94
95
96
@staticmethod
def bech32_polymod(values):
    """Compute the Bech32 checksum."""
    generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3]
    chk = 1
    for value in values:
        top = chk >> 25
        chk = (chk & 0x1FFFFFF) << 5 ^ value
        for i in range(5):
            chk ^= generator[i] if ((top >> i) & 1) else 0
    return chk

bech32_hrp_expand staticmethod

bech32_hrp_expand(hrp)

Expand the HRP into values for checksum computation.

Source code in presidio_analyzer/predefined_recognizers/generic/crypto_recognizer.py
 98
 99
100
101
@staticmethod
def bech32_hrp_expand(hrp):
    """Expand the HRP into values for checksum computation."""
    return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]

bech32_verify_checksum staticmethod

bech32_verify_checksum(hrp, data)

Verify a checksum given HRP and converted data characters.

Source code in presidio_analyzer/predefined_recognizers/generic/crypto_recognizer.py
103
104
105
106
107
108
109
110
111
112
113
@staticmethod
def bech32_verify_checksum(hrp, data):
    """Verify a checksum given HRP and converted data characters."""
    const = CryptoRecognizer.bech32_polymod(
        CryptoRecognizer.bech32_hrp_expand(hrp) + data
    )
    if const == 1:
        return BECH32
    if const == BECH32M_CONST:
        return BECH32M
    return None

bech32_decode staticmethod

bech32_decode(bech)

Validate a Bech32/Bech32m string, and determine HRP and data.

Source code in presidio_analyzer/predefined_recognizers/generic/crypto_recognizer.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@staticmethod
def bech32_decode(bech):
    """Validate a Bech32/Bech32m string, and determine HRP and data."""
    if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or (
        bech.lower() != bech and bech.upper() != bech
    ):
        return (None, None, None)
    bech = bech.lower()
    pos = bech.rfind("1")
    if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
        return (None, None, None)
    if not all(x in CHARSET for x in bech[pos + 1 :]):
        return (None, None, None)
    hrp = bech[:pos]
    data = [CHARSET.find(x) for x in bech[pos + 1 :]]
    spec = CryptoRecognizer.bech32_verify_checksum(hrp, data)
    if spec is None:
        return (None, None, None)
    return (hrp, data[:-6], spec)

validate_bech32_address staticmethod

validate_bech32_address(address)

Validate a Bech32 or Bech32m address.

Source code in presidio_analyzer/predefined_recognizers/generic/crypto_recognizer.py
135
136
137
138
139
140
141
@staticmethod
def validate_bech32_address(address):
    """Validate a Bech32 or Bech32m address."""
    hrp, data, spec = CryptoRecognizer.bech32_decode(address)
    if hrp is not None and data is not None:
        return True, spec
    return False, None

DateRecognizer

Bases: PatternRecognizer

Recognize date using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'DATE_TIME'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/generic/date_recognizer.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
class DateRecognizer(PatternRecognizer):
    """
    Recognize date using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    PATTERNS = [
        Pattern(
            "ISO 8601 datetime",
            r"\b(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))\b",
            0.8,
        ),
        Pattern(
            "mm/dd/yyyy or mm/dd/yy",
            r"\b(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|[1-2][0-9]|3[0-1])/(\d{4}|\d{2}))\b",
            0.6,
        ),
        Pattern(
            "dd/mm/yyyy or dd/mm/yy",
            r"\b(([1-9]|0[1-9]|[1-2][0-9]|3[0-1])/([1-9]|0[1-9]|1[0-2])/(\d{4}|\d{2}))\b",
            0.6,
        ),
        Pattern(
            "yyyy/mm/dd",
            r"\b(\d{4}/([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|[1-2][0-9]|3[0-1]))\b",
            0.6,
        ),
        Pattern(
            "mm-dd-yyyy",
            r"\b(([1-9]|0[1-9]|1[0-2])-([1-9]|0[1-9]|[1-2][0-9]|3[0-1])-\d{4})\b",
            0.6,
        ),
        Pattern(
            "dd-mm-yyyy",
            r"\b(([1-9]|0[1-9]|[1-2][0-9]|3[0-1])-([1-9]|0[1-9]|1[0-2])-\d{4})\b",
            0.6,
        ),
        Pattern(
            "yyyy-mm-dd",
            r"\b(\d{4}-([1-9]|0[1-9]|1[0-2])-([1-9]|0[1-9]|[1-2][0-9]|3[0-1]))\b",
            0.6,
        ),
        Pattern(
            "dd.mm.yyyy or dd.mm.yy",
            r"\b(([1-9]|0[1-9]|[1-2][0-9]|3[0-1])\.([1-9]|0[1-9]|1[0-2])\.(\d{4}|\d{2}))\b",
            0.6,
        ),
        Pattern(
            "dd-MMM-yyyy or dd-MMM-yy",
            r"\b(([1-9]|0[1-9]|[1-2][0-9]|3[0-1])-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)-(\d{4}|\d{2}))\b",
            0.6,
        ),
        Pattern(
            "MMM-yyyy or MMM-yy",
            r"\b((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)-(\d{4}|\d{2}))\b",
            0.6,
        ),
        Pattern(
            "dd-MMM or dd-MMM",
            r"\b(([1-9]|0[1-9]|[1-2][0-9]|3[0-1])-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))\b",
            0.6,
        ),
        Pattern(
            "mm/yyyy or m/yyyy",
            r"\b(([1-9]|0[1-9]|1[0-2])/\d{4})\b",
            0.2,
        ),
        Pattern(
            "mm/yy or m/yy",
            r"\b(([1-9]|0[1-9]|1[0-2])/\d{2})\b",
            0.1,
        ),
    ]

    CONTEXT = ["date", "birthday"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "DATE_TIME",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

EmailRecognizer

Bases: PatternRecognizer

Recognize email addresses using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'EMAIL_ADDRESS'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/generic/email_recognizer.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class EmailRecognizer(PatternRecognizer):
    """
    Recognize email addresses using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    # The domain part allows internal hyphen groups inside each label, including
    # consecutive hyphens, so RFC 3490 punycode labels (e.g. "xn--80ak6aa92e")
    # and raw IDN labels (matched via the Unicode-aware "\w") are detected.
    # Each label still starts and ends with an alphanumeric, and at least one
    # dot is required, so no arbitrary non-email text is matched.
    PATTERNS = [
        Pattern(
            "Email (Medium)",
            r"\b((([!#$%&'*+\-/=?^_`{|}~\w])|([!#$%&'*+\-/=?^_`{|}~\w][!#$%&'*+\-/=?^_`{|}~\.\w]{0,}[!#$%&'*+\-/=?^_`{|}~\w]))[@]\w+(?:-+\w+)*(?:\.\w+(?:-+\w+)*)+)\b",
            0.5,
        ),
    ]

    CONTEXT = ["email"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "EMAIL_ADDRESS",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def validate_result(self, pattern_text: str):  # noqa: D102
        result = tldextract.extract(pattern_text)
        return result.fqdn != ""

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

IbanRecognizer

Bases: PatternRecognizer

Recognize IBAN code using regex and checksum.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: List[str] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: List[str] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IBAN_CODE'

exact_match

Whether patterns should be exactly matched or not

TYPE: bool DEFAULT: False

bos_eos

Tuple of strings for beginning of string (BOS) and end of string (EOS)

TYPE: Tuple[str, str] DEFAULT: (BOS, EOS)

regex_flags

Regex flags options

TYPE: int DEFAULT: DOTALL | MULTILINE

replacement_pairs

List of tuples with potential replacement values for different strings to be used during pattern matching. This can allow a greater variety in input, for example by removing dashes or spaces.

TYPE: Optional[List[Tuple[str, str]]] DEFAULT: None

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

analyze

Analyze IBAN.

Source code in presidio_analyzer/predefined_recognizers/generic/iban_recognizer.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
class IbanRecognizer(PatternRecognizer):
    """
    Recognize IBAN code using regex and checksum.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param exact_match: Whether patterns should be exactly matched or not
    :param bos_eos: Tuple of strings for beginning of string (BOS)
    and end of string (EOS)
    :param regex_flags: Regex flags options
    :param replacement_pairs: List of tuples with potential replacement values
    for different strings to be used during pattern matching.
    This can allow a greater variety in input, for example by removing dashes or spaces.
    """

    # Pattern explanation:
    # - (?<![A-Z0-9]): Negative lookbehind - ensures we don't start mid-IBAN
    # - ([A-Z]{2}[0-9]{2}(?:[ -]?[A-Z0-9]{4}){2,6}): First capture group with
    #   country code, check digits, and 2-6 groups of 4 alphanumerics
    # - ((?:[ -]?[A-Z0-9]{4})?): Second optional capture group for 1 more group of 4
    # - ((?:[ -]?[A-Z0-9]{1,3})?): Third optional capture group for trailing 1-3 chars
    # - (?![A-Z0-9]): Negative lookahead - ensures we don't end mid-IBAN
    #
    # Multiple capture groups enable validation fallback: the __analyze_patterns method
    # iterates through groups in reverse order (3→2→1), trying progressively shorter
    # matches. Example: for "IBAN123456 X", tries "IBAN123456 X" (fails validation),
    # then "IBAN123456" (passes), avoiding false positives from trailing characters.
    PATTERNS = [
        Pattern(
            "IBAN Generic",
            r"(?<![A-Z0-9])([A-Z]{2}[0-9]{2}(?:[ -]?[A-Z0-9]{4}){2,6})"
            r"((?:[ -]?[A-Z0-9]{4})?)((?:[ -]?[A-Z0-9]{1,3})?)(?![A-Z0-9])",
            0.5,
        ),
    ]

    CONTEXT = ["iban", "bank", "transaction"]

    LETTERS: Dict[int, str] = {
        ord(d): str(i) for i, d in enumerate(string.digits + string.ascii_uppercase)
    }

    def __init__(
        self,
        patterns: List[str] = None,
        context: List[str] = None,
        supported_language: str = "en",
        supported_entity: str = "IBAN_CODE",
        exact_match: bool = False,
        bos_eos: Tuple[str, str] = (BOS, EOS),
        regex_flags: int = re.DOTALL | re.MULTILINE,
        replacement_pairs: Optional[List[Tuple[str, str]]] = None,
        name: Optional[str] = None,
    ):
        self.replacement_pairs = replacement_pairs or [("-", ""), (" ", "")]
        self.exact_match = exact_match
        self.BOSEOS = bos_eos if exact_match else ()
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            global_regex_flags=regex_flags,
            name=name,
        )

    def validate_result(self, pattern_text: str):  # noqa: D102
        try:
            pattern_text = EntityRecognizer.sanitize_value(
                pattern_text, self.replacement_pairs
            )
            is_valid_checksum = (
                self.__generate_iban_check_digits(pattern_text, self.LETTERS)
                == pattern_text[2:4]
            )
            # score = EntityRecognizer.MIN_SCORE
            result = False
            if is_valid_checksum:
                if self.__is_valid_format(pattern_text, self.BOSEOS):
                    result = True
                elif self.__is_valid_format(pattern_text.upper(), self.BOSEOS):
                    result = None
            return result
        except ValueError:
            logger.error("Failed to validate text %s", pattern_text)
            return False

    def analyze(
        self,
        text: str,
        entities: List[str],
        nlp_artifacts: NlpArtifacts = None,
        regex_flags: int = None,
    ) -> List[RecognizerResult]:
        """Analyze IBAN."""
        results = []

        if self.patterns:
            pattern_result = self.__analyze_patterns(text)
            results.extend(pattern_result)

        return results

    def __analyze_patterns(self, text: str, flags: int = None):
        """
        Evaluate all patterns in the provided text.

        Logic includes detecting words in the provided deny list.
        In a sentence we could get a false positive at the end of our regex, were we
        want to find the IBAN but not the false positive at the end of the match.

        i.e. "I want my deposit in DE89370400440532013000 2 days from today."

        :param text: text to analyze
        :param flags: regex flags
        :return: A list of RecognizerResult
        """
        flags = flags if flags else self.global_regex_flags
        results = []
        for pattern in self.patterns:
            try:
                matches = re.finditer(
                    pattern.regex, text, flags=flags, timeout=REGEX_TIMEOUT_SECONDS
                )

                for match in matches:
                    for grp_num in reversed(range(1, len(match.groups()) + 1)):
                        start = match.span(0)[0]
                        end = (
                            match.span(grp_num)[1]
                            if match.span(grp_num)[1] > 0
                            else match.span(0)[1]
                        )
                        current_match = text[start:end]

                        # Skip empty results
                        if current_match == "":
                            continue

                        score = pattern.score

                        validation_result = self.validate_result(current_match)
                        description = PatternRecognizer.build_regex_explanation(
                            self.name,
                            pattern.name,
                            pattern.regex,
                            score,
                            validation_result,
                            flags,
                        )
                        pattern_result = RecognizerResult(
                            entity_type=self.supported_entities[0],
                            start=start,
                            end=end,
                            score=score,
                            analysis_explanation=description,
                            recognition_metadata={
                                RecognizerResult.RECOGNIZER_NAME_KEY: self.name,
                                RecognizerResult.RECOGNIZER_IDENTIFIER_KEY: self.id,
                            },
                        )

                        if validation_result is not None:
                            if validation_result:
                                pattern_result.score = EntityRecognizer.MAX_SCORE
                            else:
                                pattern_result.score = EntityRecognizer.MIN_SCORE

                        if pattern_result.score > EntityRecognizer.MIN_SCORE:
                            results.append(pattern_result)
                            break
            except TimeoutError:
                logger.warning(
                    "Regex pattern '%s' timed out after %s seconds, skipping.",
                    pattern.name,
                    REGEX_TIMEOUT_SECONDS,
                    exc_info=True,
                )

        return results

    @staticmethod
    def __number_iban(iban: str, letters: Dict[int, str]) -> str:
        return (iban[4:] + iban[:4]).translate(letters)

    @staticmethod
    def __generate_iban_check_digits(iban: str, letters: Dict[int, str]) -> str:
        transformed_iban = (iban[:2] + "00" + iban[4:]).upper()
        number_iban = IbanRecognizer.__number_iban(transformed_iban, letters)
        return f"{98 - (int(number_iban) % 97):0>2}"

    @staticmethod
    def __is_valid_format(
        iban: str,
        bos_eos: Tuple[str, str] = (BOS, EOS),
        flags: int = re.DOTALL | re.MULTILINE,
    ) -> bool:
        country_code = iban[:2]
        if country_code in regex_per_country:
            country_regex = regex_per_country.get(country_code, "")
            if bos_eos and country_regex:
                country_regex = bos_eos[0] + country_regex + bos_eos[1]
            try:
                return country_regex and re.match(
                    country_regex, iban, flags=flags, timeout=REGEX_TIMEOUT_SECONDS
                )
            except TimeoutError:
                logger.warning(
                    "IBAN format validation regex timed out after %s seconds.",
                    REGEX_TIMEOUT_SECONDS,
                    exc_info=True,
                )
                return False

        return False

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: NlpArtifacts = None,
    regex_flags: int = None,
) -> List[RecognizerResult]

Analyze IBAN.

Source code in presidio_analyzer/predefined_recognizers/generic/iban_recognizer.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: NlpArtifacts = None,
    regex_flags: int = None,
) -> List[RecognizerResult]:
    """Analyze IBAN."""
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text)
        results.extend(pattern_result)

    return results

IpRecognizer

Bases: PatternRecognizer

Recognize IP address using regex.

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'IP_ADDRESS'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

build_regex_explanation

Construct an explanation for why this entity was detected.

invalidate_result

Check if the pattern text cannot be validated as an IP address.

Source code in presidio_analyzer/predefined_recognizers/generic/ip_recognizer.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class IpRecognizer(PatternRecognizer):
    """
    Recognize IP address using regex.

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    PATTERNS = [
        Pattern(
            "IPv4_mapped",
            r"(?<![\w:])::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:/(?:12[0-8]|1[01]\d|[1-9]?\d))?\b",
            0.6,
        ),
        Pattern(
            "IPv4_embedded",
            r"(?<![\w:])(?:(?:[0-9A-Fa-f]{1,4}:){1,5}:(?:[0-9A-Fa-f]{1,4}:){0,4}|(?:[0-9A-Fa-f]{1,4}:){6})(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:/(?:12[0-8]|1[01]\d|[1-9]?\d))?\b",
            0.6,
        ),
        Pattern(
            "IPv4",
            r"\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:/(?:[0-2]?\d|3[0-2]))?\b",
            0.6,
        ),
        Pattern(
            "IPv6",
            r"(?<![\w:])(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|:(?::[0-9A-Fa-f]{1,4}){1,7}|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?::[0-9A-Fa-f]{1,4}){1,6}|:(?::[0-9A-Fa-f]{1,4}){1,6})(?:%[0-9a-zA-Z]+)?(?:/(?:12[0-8]|1[01]\d|[1-9]?\d))?(?![\w:]|\.\d)",
            0.6,
        ),
        Pattern(
            "IPv6_unspecified",
            r"(?<![\w:])::(?:/(?:12[0-8]|1[01]\d|[1-9]?\d))?(?![\w:])",
            0.1,
        ),
    ]

    CONTEXT = ["ip", "ipv4", "ipv6"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "IP_ADDRESS",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

    def invalidate_result(self, pattern_text: str) -> bool:
        """
        Check if the pattern text cannot be validated as an IP address.

        :param pattern_text: Text detected as pattern by regex
        :return: True if invalidated
        """
        try:
            ipaddress.ip_interface(pattern_text)
        except ValueError:
            return True

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

invalidate_result

invalidate_result(pattern_text: str) -> bool

Check if the pattern text cannot be validated as an IP address.

PARAMETER DESCRIPTION
pattern_text

Text detected as pattern by regex

TYPE: str

RETURNS DESCRIPTION
bool

True if invalidated

Source code in presidio_analyzer/predefined_recognizers/generic/ip_recognizer.py
65
66
67
68
69
70
71
72
73
74
75
def invalidate_result(self, pattern_text: str) -> bool:
    """
    Check if the pattern text cannot be validated as an IP address.

    :param pattern_text: Text detected as pattern by regex
    :return: True if invalidated
    """
    try:
        ipaddress.ip_interface(pattern_text)
    except ValueError:
        return True

MacAddressRecognizer

Bases: PatternRecognizer

Recognize MAC (Media Access Control) address using regex.

Supports three common MAC address formats: - Colon-separated: 00:1A:2B:3C:4D:5E - Hyphen-separated: 00-1A-2B-3C-4D-5E - Cisco format (dot-separated groups of 4): 0012.3456.789A

ref : - https://en.wikipedia.org/wiki/MAC_address#Notational_conventions - https://www.ieee802.org/1/files/public/docs2020/yangsters-smansfield-mac-address-format-0420-v01.pdf

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

build_regex_explanation

Construct an explanation for why this entity was detected.

invalidate_result

Check if the pattern text is a valid MAC address format.

Source code in presidio_analyzer/predefined_recognizers/generic/mac_recognizer.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class MacAddressRecognizer(PatternRecognizer):
    """
    Recognize MAC (Media Access Control) address using regex.

    Supports three common MAC address formats:
    - Colon-separated: 00:1A:2B:3C:4D:5E
    - Hyphen-separated: 00-1A-2B-3C-4D-5E
    - Cisco format (dot-separated groups of 4): 0012.3456.789A

    ref :
    - https://en.wikipedia.org/wiki/MAC_address#Notational_conventions
    - https://www.ieee802.org/1/files/public/docs2020/yangsters-smansfield-mac-address-format-0420-v01.pdf
    """

    PATTERNS = [
        Pattern(
            "MAC_COLON_OR_HYPHEN",
            r"\b[0-9A-Fa-f]{2}([:-])(?:[0-9A-Fa-f]{2}\1){4}[0-9A-Fa-f]{2}\b",
            0.6,
        ),
        Pattern(
            "MAC_CISCO_DOT",
            r"\b[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\b",
            0.6,
        ),
    ]

    CONTEXT = ["mac", "mac address", "hardware address", "physical address", "ethernet"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "MAC_ADDRESS",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name
        )

    def invalidate_result(self, pattern_text: str) -> bool:
        """
        Check if the pattern text is a valid MAC address format.

        :param pattern_text: Text detected as pattern by regex
        :return: True if invalidated (invalid MAC address)
        """
        # Remove separators and validate hex characters and length
        cleaned = re.sub(r'[:\-.]', '', pattern_text)

        # All characters must be valid hex
        if re.fullmatch(r"[0-9A-Fa-f]{12}", cleaned) is None:
            return True

        # Optionally reject broadcast/multicast addresses if needed
        # Broadcast: FF:FF:FF:FF:FF:FF
        if cleaned.upper() == 'FFFFFFFFFFFF' or cleaned.upper() == '000000000000':
            return True

        return False

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

invalidate_result

invalidate_result(pattern_text: str) -> bool

Check if the pattern text is a valid MAC address format.

PARAMETER DESCRIPTION
pattern_text

Text detected as pattern by regex

TYPE: str

RETURNS DESCRIPTION
bool

True if invalidated (invalid MAC address)

Source code in presidio_analyzer/predefined_recognizers/generic/mac_recognizer.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def invalidate_result(self, pattern_text: str) -> bool:
    """
    Check if the pattern text is a valid MAC address format.

    :param pattern_text: Text detected as pattern by regex
    :return: True if invalidated (invalid MAC address)
    """
    # Remove separators and validate hex characters and length
    cleaned = re.sub(r'[:\-.]', '', pattern_text)

    # All characters must be valid hex
    if re.fullmatch(r"[0-9A-Fa-f]{12}", cleaned) is None:
        return True

    # Optionally reject broadcast/multicast addresses if needed
    # Broadcast: FF:FF:FF:FF:FF:FF
    if cleaned.upper() == 'FFFFFFFFFFFF' or cleaned.upper() == '000000000000':
        return True

    return False

PhoneRecognizer

Bases: LocalRecognizer

Recognize multi-regional phone numbers.

Using python-phonenumbers, along with fixed and regional context words.

PARAMETER DESCRIPTION
context

Base context words for enhancing the assurance scores.

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'PHONE_NUMBER'

supported_regions

The regions for phone number matching and validation

DEFAULT: DEFAULT_SUPPORTED_REGIONS

leniency

The strictness level of phone number formats. Accepts values from 0 to 3, where 0 is the lenient and 3 is the most strictest.

TYPE: Optional[int] DEFAULT: 1

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

analyze

Analyzes text to detect phone numbers using python-phonenumbers.

Source code in presidio_analyzer/predefined_recognizers/generic/phone_recognizer.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
class PhoneRecognizer(LocalRecognizer):
    """Recognize multi-regional phone numbers.

     Using python-phonenumbers, along with fixed and regional context words.
    :param context: Base context words for enhancing the assurance scores.
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    :param supported_regions: The regions for phone number matching and validation
    :param leniency: The strictness level of phone number formats.
    Accepts values from 0 to 3, where 0 is the lenient and 3 is the most strictest.
    """

    SCORE = 0.4
    CONTEXT = ["phone", "number", "telephone", "cell", "cellphone", "mobile", "call"]
    DEFAULT_SUPPORTED_REGIONS = ("US", "UK", "DE", "FR", "IL", "IN", "CA", "BR")

    def __init__(
        self,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "PHONE_NUMBER",
        # For all regions, use phonenumbers.SUPPORTED_REGIONS
        supported_regions=DEFAULT_SUPPORTED_REGIONS,
        leniency: Optional[int] = 1,
        name: Optional[str] = None,
    ):
        context = context if context else self.CONTEXT
        self.supported_regions = supported_regions
        self.leniency = leniency
        super().__init__(
            supported_entities=[supported_entity],
            supported_language=supported_language,
            context=context,
            name=name,
        )

    def load(self) -> None:  # noqa: D102
        pass

    def analyze(
        self, text: str, entities: List[str], nlp_artifacts: NlpArtifacts = None
    ) -> List[RecognizerResult]:
        """Analyzes text to detect phone numbers using python-phonenumbers.

        Iterates over entities, fetching regions, then matching regional
        phone number patterns against the text.
        :param text: Text to be analyzed
        :param entities: Entities this recognizer can detect
        :param nlp_artifacts: Additional metadata from the NLP engine
        :return: List of phone numbers RecognizerResults
        """
        results = []
        for region in self.supported_regions:
            for match in phonenumbers.PhoneNumberMatcher(
                text, region, leniency=self.leniency
            ):
                try:
                    parsed_number = phonenumbers.parse(text[match.start : match.end])
                    region = phonenumbers.region_code_for_number(parsed_number)
                    results += [
                        self._get_recognizer_result(match, text, region, nlp_artifacts)
                    ]
                except NumberParseException:
                    results += [
                        self._get_recognizer_result(match, text, region, nlp_artifacts)
                    ]

        return EntityRecognizer.remove_duplicates(results)

    def _get_recognizer_result(self, match, text, region, nlp_artifacts):
        result = RecognizerResult(
            entity_type=self.supported_entities[0],
            start=match.start,
            end=match.end,
            score=self.SCORE,
            analysis_explanation=self._get_analysis_explanation(region),
            recognition_metadata={
                RecognizerResult.RECOGNIZER_NAME_KEY: self.name,
                RecognizerResult.RECOGNIZER_IDENTIFIER_KEY: self.id,
            },
        )

        return result

    def _get_analysis_explanation(self, region):
        return AnalysisExplanation(
            recognizer=PhoneRecognizer.__name__,
            original_score=self.SCORE,
            textual_explanation=f"Recognized as {region} region phone number, "
            f"using PhoneRecognizer",
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

analyze

analyze(
    text: str, entities: List[str], nlp_artifacts: NlpArtifacts = None
) -> List[RecognizerResult]

Analyzes text to detect phone numbers using python-phonenumbers.

Iterates over entities, fetching regions, then matching regional phone number patterns against the text.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Additional metadata from the NLP engine

TYPE: NlpArtifacts DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]

List of phone numbers RecognizerResults

Source code in presidio_analyzer/predefined_recognizers/generic/phone_recognizer.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def analyze(
    self, text: str, entities: List[str], nlp_artifacts: NlpArtifacts = None
) -> List[RecognizerResult]:
    """Analyzes text to detect phone numbers using python-phonenumbers.

    Iterates over entities, fetching regions, then matching regional
    phone number patterns against the text.
    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Additional metadata from the NLP engine
    :return: List of phone numbers RecognizerResults
    """
    results = []
    for region in self.supported_regions:
        for match in phonenumbers.PhoneNumberMatcher(
            text, region, leniency=self.leniency
        ):
            try:
                parsed_number = phonenumbers.parse(text[match.start : match.end])
                region = phonenumbers.region_code_for_number(parsed_number)
                results += [
                    self._get_recognizer_result(match, text, region, nlp_artifacts)
                ]
            except NumberParseException:
                results += [
                    self._get_recognizer_result(match, text, region, nlp_artifacts)
                ]

    return EntityRecognizer.remove_duplicates(results)

UrlRecognizer

Bases: PatternRecognizer

Recognize urls using regex.

This application uses Open Source components: Project: CommonRegex https://github.com/madisonmay/CommonRegex Copyright (c) 2014 Madison May License (MIT) https://github.com/madisonmay/CommonRegex/blob/master/LICENSE

PARAMETER DESCRIPTION
patterns

List of patterns to be used by this recognizer

TYPE: Optional[List[Pattern]] DEFAULT: None

context

List of context words to increase confidence in detection

TYPE: Optional[List[str]] DEFAULT: None

supported_language

Language this recognizer supports

TYPE: str DEFAULT: 'en'

supported_entity

The entity this recognizer can detect

TYPE: str DEFAULT: 'URL'

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyzes text to detect PII using regular expressions or deny-lists.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize instance into a dictionary.

from_dict

Create instance from a serialized dict.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

validate_result

Validate the pattern logic e.g., by running checksum on a detected pattern.

invalidate_result

Logic to check for result invalidation by running pruning logic.

build_regex_explanation

Construct an explanation for why this entity was detected.

Source code in presidio_analyzer/predefined_recognizers/generic/url_recognizer.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class UrlRecognizer(PatternRecognizer):
    """
    Recognize urls using regex.

    This application uses Open Source components:
    Project: CommonRegex https://github.com/madisonmay/CommonRegex
    Copyright (c) 2014 Madison May
    License (MIT)  https://github.com/madisonmay/CommonRegex/blob/master/LICENSE

    :param patterns: List of patterns to be used by this recognizer
    :param context: List of context words to increase confidence in detection
    :param supported_language: Language this recognizer supports
    :param supported_entity: The entity this recognizer can detect
    """

    BASE_URL_REGEX = r"((www\d{0,3}[.])?[a-z0-9.\-]{1,253}[.](?:(?:com)|(?:edu)|(?:gov)|(?:int)|(?:mil)|(?:net)|(?:onl)|(?:org)|(?:pro)|(?:red)|(?:tel)|(?:uno)|(?:xxx)|(?:academy)|(?:accountant)|(?:accountants)|(?:actor)|(?:adult)|(?:africa)|(?:agency)|(?:airforce)|(?:apartments)|(?:app)|(?:archi)|(?:army)|(?:art)|(?:asia)|(?:associates)|(?:attorney)|(?:auction)|(?:audio)|(?:auto)|(?:autos)|(?:baby)|(?:band)|(?:bar)|(?:bargains)|(?:beer)|(?:berlin)|(?:best)|(?:bet)|(?:bid)|(?:bike)|(?:bio)|(?:black)|(?:blackfriday)|(?:blog)|(?:blue)|(?:boats)|(?:bond)|(?:boo)|(?:boston)|(?:bot)|(?:boutique)|(?:build)|(?:builders)|(?:business)|(?:buzz)|(?:cab)|(?:cafe)|(?:cam)|(?:camera)|(?:camp)|(?:capital)|(?:car)|(?:cards)|(?:care)|(?:careers)|(?:cars)|(?:casa)|(?:cash)|(?:casino)|(?:catering)|(?:center)|(?:ceo)|(?:cfd)|(?:charity)|(?:chat)|(?:cheap)|(?:christmas)|(?:church)|(?:city)|(?:claims)|(?:cleaning)|(?:click)|(?:clinic)|(?:clothing)|(?:cloud)|(?:club)|(?:codes)|(?:coffee)|(?:college)|(?:com)|(?:community)|(?:company)|(?:computer)|(?:condos)|(?:construction)|(?:consulting)|(?:contact)|(?:contractors)|(?:cooking)|(?:cool)|(?:coupons)|(?:courses)|(?:credit)|(?:creditcard)|(?:cricket)|(?:cruises)|(?:cyou)|(?:dad)|(?:dance)|(?:date)|(?:dating)|(?:day)|(?:degree)|(?:delivery)|(?:democrat)|(?:dental)|(?:dentist)|(?:desi)|(?:design)|(?:dev)|(?:diamonds)|(?:diet)|(?:digital)|(?:direct)|(?:directory)|(?:discount)|(?:doctor)|(?:dog)|(?:domains)|(?:download)|(?:earth)|(?:eco)|(?:education)|(?:email)|(?:energy)|(?:engineer)|(?:engineering)|(?:enterprises)|(?:equipment)|(?:esq)|(?:estate)|(?:events)|(?:exchange)|(?:expert)|(?:exposed)|(?:express)|(?:fail)|(?:faith)|(?:family)|(?:fans)|(?:farm)|(?:fashion)|(?:feedback)|(?:film)|(?:finance)|(?:financial)|(?:fish)|(?:fishing)|(?:fit)|(?:fitness)|(?:flights)|(?:florist)|(?:flowers)|(?:football)|(?:forsale)|(?:foundation)|(?:fun)|(?:fund)|(?:furniture)|(?:futbol)|(?:fyi)|(?:gallery)|(?:game)|(?:games)|(?:garden)|(?:gay)|(?:gdn)|(?:gifts)|(?:gives)|(?:giving)|(?:glass)|(?:global)|(?:gmbh)|(?:gold)|(?:golf)|(?:graphics)|(?:gratis)|(?:green)|(?:gripe)|(?:group)|(?:guide)|(?:guitars)|(?:guru)|(?:hair)|(?:hamburg)|(?:haus)|(?:health)|(?:healthcare)|(?:help)|(?:hiphop)|(?:hockey)|(?:holdings)|(?:holiday)|(?:homes)|(?:horse)|(?:hospital)|(?:host)|(?:hosting)|(?:house)|(?:how)|(?:icu)|(?:info)|(?:ink)|(?:institute)|(?:insure)|(?:international)|(?:investments)|(?:irish)|(?:jewelry)|(?:jetzt)|(?:juegos)|(?:kaufen)|(?:kids)|(?:kitchen)|(?:kiwi)|(?:krd)|(?:kyoto)|(?:land)|(?:lat)|(?:law)|(?:lawyer)|(?:lease)|(?:legal)|(?:lgbt)|(?:life)|(?:lighting)|(?:limited)|(?:limo)|(?:link)|(?:live)|(?:loan)|(?:loans)|(?:lol)|(?:london)|(?:love)|(?:ltd)|(?:ltda)|(?:luxury)|(?:maison)|(?:management)|(?:market)|(?:marketing)|(?:markets)|(?:mba)|(?:media)|(?:melbourne)|(?:meme)|(?:memorial)|(?:men)|(?:miami)|(?:mobi)|(?:moda)|(?:moe)|(?:mom)|(?:money)|(?:monster)|(?:mortgage)|(?:motorcycles)|(?:mov)|(?:movie)|(?:nagoya)|(?:name)|(?:navy)|(?:network)|(?:new)|(?:news)|(?:ngo)|(?:ninja)|(?:now)|(?:nyc)|(?:observer)|(?:okinawa)|(?:one)|(?:ong)|(?:onl)|(?:online)|(?:organic)|(?:osaka)|(?:page)|(?:paris)|(?:partners)|(?:parts)|(?:party)|(?:pet)|(?:phd)|(?:photo)|(?:photography)|(?:photos)|(?:pics)|(?:pictures)|(?:pink)|(?:pizza)|(?:place)|(?:plumbing)|(?:plus)|(?:poker)|(?:porn)|(?:press)|(?:pro)|(?:productions)|(?:prof)|(?:promo)|(?:properties)|(?:property)|(?:protection)|(?:pub)|(?:quest)|(?:racing)|(?:recipes)|(?:red)|(?:rehab)|(?:reise)|(?:reisen)|(?:rent)|(?:rentals)|(?:repair)|(?:report)|(?:republican)|(?:rest)|(?:restaurant)|(?:review)|(?:reviews)|(?:rip)|(?:rocks)|(?:rodeo)|(?:rsvp)|(?:run)|(?:saarland)|(?:sale)|(?:salon)|(?:sarl)|(?:sbs)|(?:school)|(?:schule)|(?:science)|(?:services)|(?:sex)|(?:sexy)|(?:sh)|(?:shoes)|(?:shop)|(?:shopping)|(?:show)|(?:singles)|(?:site)|(?:skin)|(?:soccer)|(?:social)|(?:software)|(?:solar)|(?:solutions)|(?:soy)|(?:space)|(?:spiegel)|(?:study)|(?:style)|(?:sucks)|(?:supply)|(?:support)|(?:surf)|(?:surgery)|(?:systems)|(?:tax)|(?:taxi)|(?:team)|(?:tech)|(?:technology)|(?:tel)|(?:theater)|(?:tips)|(?:tires)|(?:today)|(?:tools)|(?:top)|(?:tours)|(?:town)|(?:toys)|(?:trade)|(?:training)|(?:tube)|(?:uk)|(?:university)|(?:uno)|(?:vacations)|(?:ventures)|(?:vet)|(?:video)|(?:villas)|(?:vin)|(?:vip)|(?:vision)|(?:vlaanderen)|(?:vodka)|(?:vote)|(?:voting)|(?:voyage)|(?:wales)|(?:wang)|(?:watch)|(?:webcam)|(?:website)|(?:wedding)|(?:wiki)|(?:wine)|(?:work)|(?:works)|(?:world)|(?:wtf)|(?:xyz)|(?:yoga)|(?:yokohama)|(?:you)|(?:zone)|(?:ac)|(?:ad)|(?:ae)|(?:af)|(?:ag)|(?:ai)|(?:al)|(?:am)|(?:an)|(?:ao)|(?:aq)|(?:ar)|(?:as)|(?:at)|(?:au)|(?:aw)|(?:ax)|(?:az)|(?:ba)|(?:bb)|(?:bd)|(?:be)|(?:bf)|(?:bg)|(?:bh)|(?:bi)|(?:bj)|(?:bm)|(?:bn)|(?:bo)|(?:br)|(?:bs)|(?:bt)|(?:bv)|(?:bw)|(?:by)|(?:bz)|(?:ca)|(?:cc)|(?:cd)|(?:cf)|(?:cg)|(?:ch)|(?:ci)|(?:ck)|(?:cl)|(?:cm)|(?:cn)|(?:co)|(?:cr)|(?:cu)|(?:cv)|(?:cw)|(?:cx)|(?:cy)|(?:cz)|(?:de)|(?:dj)|(?:dk)|(?:dm)|(?:do)|(?:dz)|(?:ec)|(?:ee)|(?:eg)|(?:er)|(?:es)|(?:et)|(?:eu)|(?:fi)|(?:fj)|(?:fk)|(?:fm)|(?:fo)|(?:fr)|(?:ga)|(?:gb)|(?:gd)|(?:ge)|(?:gf)|(?:gg)|(?:gh)|(?:gi)|(?:gl)|(?:gm)|(?:gn)|(?:gp)|(?:gq)|(?:gr)|(?:gs)|(?:gt)|(?:gu)|(?:gw)|(?:gy)|(?:hk)|(?:hm)|(?:hn)|(?:hr)|(?:ht)|(?:hu)|(?:id)|(?:ie)|(?:il)|(?:im)|(?:in)|(?:io)|(?:iq)|(?:ir)|(?:is)|(?:it)|(?:je)|(?:jm)|(?:jo)|(?:jp)|(?:ke)|(?:kg)|(?:kh)|(?:ki)|(?:km)|(?:kn)|(?:kp)|(?:kr)|(?:kw)|(?:ky)|(?:kz)|(?:la)|(?:lb)|(?:lc)|(?:li)|(?:lk)|(?:lr)|(?:ls)|(?:lt)|(?:lu)|(?:lv)|(?:ly)|(?:ma)|(?:mc)|(?:md)|(?:me)|(?:mg)|(?:mh)|(?:mk)|(?:ml)|(?:mm)|(?:mn)|(?:mo)|(?:mp)|(?:mq)|(?:mr)|(?:ms)|(?:mt)|(?:mu)|(?:mv)|(?:mw)|(?:mx)|(?:my)|(?:mz)|(?:na)|(?:nc)|(?:ne)|(?:nf)|(?:ng)|(?:ni)|(?:nl)|(?:no)|(?:np)|(?:nr)|(?:nu)|(?:nz)|(?:om)|(?:pa)|(?:pe)|(?:pf)|(?:pg)|(?:ph)|(?:pk)|(?:pl)|(?:pm)|(?:pn)|(?:pr)|(?:ps)|(?:pt)|(?:pw)|(?:py)|(?:qa)|(?:re)|(?:ro)|(?:rs)|(?:ru)|(?:rw)|(?:sa)|(?:sb)|(?:sc)|(?:sd)|(?:se)|(?:sg)|(?:sh)|(?:si)|(?:sj)|(?:sk)|(?:sl)|(?:sm)|(?:sn)|(?:so)|(?:sr)|(?:st)|(?:su)|(?:sv)|(?:sx)|(?:sy)|(?:sz)|(?:tc)|(?:td)|(?:tf)|(?:tg)|(?:th)|(?:tj)|(?:tk)|(?:tl)|(?:tm)|(?:tn)|(?:to)|(?:tp)|(?:tr)|(?:tt)|(?:tv)|(?:tw)|(?:tz)|(?:ua)|(?:ug)|(?:uk)|(?:us)|(?:uy)|(?:uz)|(?:va)|(?:vc)|(?:ve)|(?:vg)|(?:vi)|(?:vn)|(?:vu)|(?:wf)|(?:ws)|(?:ye)|(?:yt)|(?:za)|(?:zm)|(?:zw))(?:/[^\s()<>\"']*)?)"  # noqa: E501

    PATTERNS = [
        Pattern("Standard Url", "(?i)(?:https?://)" + BASE_URL_REGEX, 0.6),
        Pattern("Non schema URL", "(?i)" + BASE_URL_REGEX, 0.5),
        Pattern("Quoted URL", r'(?i)["\'](https?://' + BASE_URL_REGEX + r')["\']', 0.6),
        Pattern(
            "Quoted Non-schema URL", r'(?i)["\'](' + BASE_URL_REGEX + r')["\']', 0.5
        ),
    ]

    CONTEXT = ["url", "website", "link"]

    def __init__(
        self,
        patterns: Optional[List[Pattern]] = None,
        context: Optional[List[str]] = None,
        supported_language: str = "en",
        supported_entity: str = "URL",
        name: Optional[str] = None,
    ):
        patterns = patterns if patterns else self.PATTERNS
        context = context if context else self.CONTEXT
        super().__init__(
            supported_entity=supported_entity,
            patterns=patterns,
            context=context,
            supported_language=supported_language,
            name=name,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]

Analyzes text to detect PII using regular expressions or deny-lists.

PARAMETER DESCRIPTION
text

Text to be analyzed

TYPE: str

entities

Entities this recognizer can detect

TYPE: List[str]

nlp_artifacts

Output values from the NLP engine

TYPE: Optional[NlpArtifacts] DEFAULT: None

regex_flags

regex flags to be used in regex matching

TYPE: Optional[int] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]
Source code in presidio_analyzer/pattern_recognizer.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional["NlpArtifacts"] = None,
    regex_flags: Optional[int] = None,
) -> List[RecognizerResult]:
    """
    Analyzes text to detect PII using regular expressions or deny-lists.

    :param text: Text to be analyzed
    :param entities: Entities this recognizer can detect
    :param nlp_artifacts: Output values from the NLP engine
    :param regex_flags: regex flags to be used in regex matching
    :return:
    """
    results = []

    if self.patterns:
        pattern_result = self.__analyze_patterns(text, regex_flags)
        results.extend(pattern_result)

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize instance into a dictionary.

Source code in presidio_analyzer/pattern_recognizer.py
283
284
285
286
287
288
289
290
291
292
293
def to_dict(self) -> Dict:
    """Serialize instance into a dictionary."""
    return_dict = super().to_dict()

    return_dict["patterns"] = [pat.to_dict() for pat in self.patterns]
    return_dict["deny_list"] = self.deny_list
    return_dict["context"] = self.context
    return_dict["supported_entity"] = return_dict["supported_entities"][0]
    del return_dict["supported_entities"]

    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> PatternRecognizer

Create instance from a serialized dict.

Source code in presidio_analyzer/pattern_recognizer.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "PatternRecognizer":
    """Create instance from a serialized dict."""
    # Make a copy to avoid mutating the input
    entity_recognizer_dict = entity_recognizer_dict.copy()

    patterns = entity_recognizer_dict.get("patterns")
    if patterns:
        patterns_list = [Pattern.from_dict(pat) for pat in patterns]
        entity_recognizer_dict["patterns"] = patterns_list

    # Transform supported_entities (plural) to supported_entity (singular)
    # PatternRecognizer only accepts supported_entity (singular)
    if (
        "supported_entity" in entity_recognizer_dict
        and "supported_entities" in entity_recognizer_dict
    ):
        raise ValueError(
            "Both 'supported_entity' and 'supported_entities' "
            "are present in the input dictionary. "
            "Only one should be provided."
        )
    if "supported_entities" in entity_recognizer_dict:
        supported_entities = entity_recognizer_dict.pop("supported_entities")
        if supported_entities and len(supported_entities) > 0:
            # Only set if not already present
            if "supported_entity" not in entity_recognizer_dict:
                entity_recognizer_dict["supported_entity"] = supported_entities[0]

    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

validate_result

validate_result(pattern_text: str) -> Optional[bool]

Validate the pattern logic e.g., by running checksum on a detected pattern.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the validation was successful.

Source code in presidio_analyzer/pattern_recognizer.py
136
137
138
139
140
141
142
143
144
def validate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Validate the pattern logic e.g., by running checksum on a detected pattern.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the validation was successful.
    """
    return None

invalidate_result

invalidate_result(pattern_text: str) -> Optional[bool]

Logic to check for result invalidation by running pruning logic.

For example, each SSN number group should not consist of all the same digits.

PARAMETER DESCRIPTION
pattern_text

the text to validated. Only the part in text that was detected by the regex engine

TYPE: str

RETURNS DESCRIPTION
Optional[bool]

A bool indicating whether the result is invalidated

Source code in presidio_analyzer/pattern_recognizer.py
146
147
148
149
150
151
152
153
154
155
156
def invalidate_result(self, pattern_text: str) -> Optional[bool]:
    """
    Logic to check for result invalidation by running pruning logic.

    For example, each SSN number group should not consist of all the same digits.

    :param pattern_text: the text to validated.
    Only the part in text that was detected by the regex engine
    :return: A bool indicating whether the result is invalidated
    """
    return None

build_regex_explanation staticmethod

build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation

Construct an explanation for why this entity was detected.

PARAMETER DESCRIPTION
recognizer_name

Name of recognizer detecting the entity

TYPE: str

pattern_name

Regex pattern name which detected the entity

TYPE: str

pattern

Regex pattern logic

TYPE: str

original_score

Score given by the recognizer

TYPE: float

validation_result

Whether validation was used and its result

TYPE: bool

regex_flags

Regex flags used in the regex matching

TYPE: int

RETURNS DESCRIPTION
AnalysisExplanation

Analysis explanation

Source code in presidio_analyzer/pattern_recognizer.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
@staticmethod
def build_regex_explanation(
    recognizer_name: str,
    pattern_name: str,
    pattern: str,
    original_score: float,
    validation_result: bool,
    regex_flags: int,
) -> AnalysisExplanation:
    """
    Construct an explanation for why this entity was detected.

    :param recognizer_name: Name of recognizer detecting the entity
    :param pattern_name: Regex pattern name which detected the entity
    :param pattern: Regex pattern logic
    :param original_score: Score given by the recognizer
    :param validation_result: Whether validation was used and its result
    :param regex_flags: Regex flags used in the regex matching
    :return: Analysis explanation
    """
    textual_explanation = (
        f"Detected by `{recognizer_name}` using pattern `{pattern_name}`"
    )

    explanation = AnalysisExplanation(
        recognizer=recognizer_name,
        original_score=original_score,
        pattern_name=pattern_name,
        pattern=pattern,
        validation_result=validation_result,
        regex_flags=regex_flags,
        textual_explanation=textual_explanation,
    )
    return explanation

GLiNERRecognizer

Bases: LocalRecognizer

GLiNER model based entity recognizer.

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

load

Load the GLiNER model.

analyze

Analyze text to identify entities using a GLiNER model.

Source code in presidio_analyzer/predefined_recognizers/ner/gliner_recognizer.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
class GLiNERRecognizer(LocalRecognizer):
    """GLiNER model based entity recognizer."""

    def __init__(
        self,
        supported_entities: Optional[List[str]] = None,
        name: str = "GLiNERRecognizer",
        supported_language: str = "en",
        version: str = "0.0.1",
        context: Optional[List[str]] = None,
        entity_mapping: Optional[Dict[str, str]] = None,
        model_name: str = "urchade/gliner_multi_pii-v1",
        flat_ner: bool = True,
        multi_label: bool = False,
        threshold: float = 0.30,
        map_location: Optional[str] = None,
        text_chunker: Optional[BaseTextChunker] = None,
        load_onnx_model: bool = False,
        onnx_model_file: str = "model.onnx",
        **model_kwargs,
    ):
        """GLiNER model based entity recognizer.

        The model is based on the GLiNER library.

        :param supported_entities: List of supported entities for this recognizer.
        If None, all entities in Presidio's default configuration will be used.
        see `NerModelConfiguration`
        :param name: Name of the recognizer
        :param supported_language: Language code to use for the recognizer
        :param version: Version of the recognizer
        :param context: N/A for this recognizer
        :param model_name: The name of the GLiNER model to load
        :param flat_ner: Whether to use flat NER or not (see GLiNER's documentation)
        :param multi_label: Whether to use multi-label classification or not
        (see GLiNER's documentation)
        :param threshold: The threshold for the model's output
        (see GLiNER's documentation)
        :param map_location: The device to use for the model.
            If None, will auto-detect GPU or use CPU.
        :param text_chunker: Custom text chunking strategy. If None, uses
            CharacterBasedTextChunker with default settings (chunk_size=250,
            chunk_overlap=50)
        :param load_onnx_model: Whether to load the model using ONNX Runtime.
            If True, uses ONNX Runtime backend which supports CPUs without AVX2.
            Requires onnxruntime to be installed. Default is False.
        :param onnx_model_file: The name of the ONNX model file to load.
            Only used when load_onnx_model is True. This is passed directly to
            GLiNER.from_pretrained(). GLiNER looks for this file in the model
            directory (downloaded or cached model path). Default is "model.onnx".
        :param model_kwargs: Additional keyword arguments to pass to
            GLiNER.from_pretrained(). This allows passing future parameters
            to the GLiNER model without explicit support in this recognizer.


        """

        if entity_mapping:
            if supported_entities:
                raise ValueError(
                    "entity_mapping and supported_entities cannot be used together"
                )

            self.model_to_presidio_entity_mapping = entity_mapping
        else:
            if not supported_entities:
                logger.info(
                    "No supported entities provided, "
                    "using default entities from NerModelConfiguration"
                )
                self.model_to_presidio_entity_mapping = (
                    NerModelConfiguration().model_to_presidio_entity_mapping
                )
            else:
                self.model_to_presidio_entity_mapping = {
                    entity: entity for entity in supported_entities
                }

        logger.info(f"Using entity mapping {json.dumps(entity_mapping, indent=2)}")
        supported_entities = list(set(self.model_to_presidio_entity_mapping.values()))
        self.model_name = model_name

        self.map_location = (
            map_location
            if map_location is not None
            else device_detector.get_device()
        )

        self.flat_ner = flat_ner
        self.multi_label = multi_label
        self.threshold = threshold
        self.load_onnx_model = load_onnx_model
        self.onnx_model_file = onnx_model_file
        self.model_kwargs = model_kwargs

        # Use provided chunker or default to in-house character-based chunker
        if text_chunker is not None:
            self.text_chunker = text_chunker
        else:
            from presidio_analyzer.chunkers import CharacterBasedTextChunker

            self.text_chunker = CharacterBasedTextChunker(
                chunk_size=250,
                chunk_overlap=50,
            )

        self.gliner = None

        super().__init__(
            supported_entities=supported_entities,
            name=name,
            supported_language=supported_language,
            version=version,
            context=context,
        )

        self.gliner_labels = list(self.model_to_presidio_entity_mapping.keys())

    def load(self) -> None:
        """Load the GLiNER model."""
        if not GLiNER:
            raise ImportError("GLiNER is not installed. Please install it.")

        self.gliner = GLiNER.from_pretrained(
            self.model_name,
            map_location=self.map_location,
            load_onnx_model=self.load_onnx_model,
            onnx_model_file=self.onnx_model_file,
            **self.model_kwargs,
        )

    def analyze(
        self,
        text: str,
        entities: List[str],
        nlp_artifacts: Optional[NlpArtifacts] = None,
    ) -> List[RecognizerResult]:
        """Analyze text to identify entities using a GLiNER model.

        :param text: The text to be analyzed
        :param entities: The list of entities this recognizer is requested to return
        :param nlp_artifacts: N/A for this recognizer
        """

        # combine the input labels as this model allows for ad-hoc labels
        labels = self.__create_input_labels(entities)

        # Process text with automatic chunking
        def predict_func(text: str) -> List[RecognizerResult]:
            # Get predictions from GLiNER (returns dicts)
            gliner_predictions = self.gliner.predict_entities(
                text=text,
                labels=labels,
                flat_ner=self.flat_ner,
                threshold=self.threshold,
                multi_label=self.multi_label,
            )

            # Convert dicts to RecognizerResult objects
            results = []
            for pred in gliner_predictions:
                presidio_entity = self.model_to_presidio_entity_mapping.get(
                    pred["label"], pred["label"]
                )

                # Filter by requested entities
                if entities and presidio_entity not in entities:
                    continue

                analysis_explanation = AnalysisExplanation(
                    recognizer=self.name,
                    original_score=pred["score"],
                    textual_explanation=f"Identified as {presidio_entity} by GLiNER",
                )

                results.append(
                    RecognizerResult(
                        entity_type=presidio_entity,
                        start=pred["start"],
                        end=pred["end"],
                        score=pred["score"],
                        analysis_explanation=analysis_explanation,
                    )
                )
            return results

        predictions = self.text_chunker.predict_with_chunking(
            text=text,
            predict_func=predict_func,
        )

        return predictions

    def __create_input_labels(self, entities):
        """Append the entities requested by the user to the list of labels if it's not there."""  # noqa: E501
        labels = list(self.gliner_labels)
        for entity in entities:
            if (
                entity not in self.model_to_presidio_entity_mapping.values()
                and entity not in self.gliner_labels
            ):
                labels.append(entity)
        return labels

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

load

load() -> None

Load the GLiNER model.

Source code in presidio_analyzer/predefined_recognizers/ner/gliner_recognizer.py
144
145
146
147
148
149
150
151
152
153
154
155
def load(self) -> None:
    """Load the GLiNER model."""
    if not GLiNER:
        raise ImportError("GLiNER is not installed. Please install it.")

    self.gliner = GLiNER.from_pretrained(
        self.model_name,
        map_location=self.map_location,
        load_onnx_model=self.load_onnx_model,
        onnx_model_file=self.onnx_model_file,
        **self.model_kwargs,
    )

analyze

analyze(
    text: str, entities: List[str], nlp_artifacts: Optional[NlpArtifacts] = None
) -> List[RecognizerResult]

Analyze text to identify entities using a GLiNER model.

PARAMETER DESCRIPTION
text

The text to be analyzed

TYPE: str

entities

The list of entities this recognizer is requested to return

TYPE: List[str]

nlp_artifacts

N/A for this recognizer

TYPE: Optional[NlpArtifacts] DEFAULT: None

Source code in presidio_analyzer/predefined_recognizers/ner/gliner_recognizer.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
) -> List[RecognizerResult]:
    """Analyze text to identify entities using a GLiNER model.

    :param text: The text to be analyzed
    :param entities: The list of entities this recognizer is requested to return
    :param nlp_artifacts: N/A for this recognizer
    """

    # combine the input labels as this model allows for ad-hoc labels
    labels = self.__create_input_labels(entities)

    # Process text with automatic chunking
    def predict_func(text: str) -> List[RecognizerResult]:
        # Get predictions from GLiNER (returns dicts)
        gliner_predictions = self.gliner.predict_entities(
            text=text,
            labels=labels,
            flat_ner=self.flat_ner,
            threshold=self.threshold,
            multi_label=self.multi_label,
        )

        # Convert dicts to RecognizerResult objects
        results = []
        for pred in gliner_predictions:
            presidio_entity = self.model_to_presidio_entity_mapping.get(
                pred["label"], pred["label"]
            )

            # Filter by requested entities
            if entities and presidio_entity not in entities:
                continue

            analysis_explanation = AnalysisExplanation(
                recognizer=self.name,
                original_score=pred["score"],
                textual_explanation=f"Identified as {presidio_entity} by GLiNER",
            )

            results.append(
                RecognizerResult(
                    entity_type=presidio_entity,
                    start=pred["start"],
                    end=pred["end"],
                    score=pred["score"],
                    analysis_explanation=analysis_explanation,
                )
            )
        return results

    predictions = self.text_chunker.predict_with_chunking(
        text=text,
        predict_func=predict_func,
    )

    return predictions

HuggingFaceNerRecognizer

Bases: LocalRecognizer

HuggingFace Transformers based NER Recognizer.

This recognizer uses HuggingFace pipeline directly for NER, bypassing spaCy tokenizer alignment issues. It's particularly useful for agglutinative languages (Korean, Japanese, Turkish, etc.) where particles attach to nouns.

Unlike the standard TransformersNlpEngine approach, this recognizer: - Uses HuggingFace pipeline directly (not through spaCy) - Returns NER results without char_span alignment - Supports any HuggingFace token-classification model - Works with any language that has a HuggingFace NER model

Example: >>> from presidio_analyzer import AnalyzerEngine >>> from presidio_analyzer.predefined_recognizers.ner import ( ... HuggingFaceNerRecognizer ... ) >>> >>> # For Korean >>> recognizer = HuggingFaceNerRecognizer( ... model_name="test-owner/test-model", ... supported_language="ko" ... ) >>> >>> # For English >>> recognizer = HuggingFaceNerRecognizer( ... model_name="dslim/bert-base-NER", ... supported_language="en" ... ) >>> >>> analyzer = AnalyzerEngine() >>> analyzer.registry.add_recognizer(recognizer)

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

load

Load the HuggingFace NER pipeline.

analyze

Analyze text for NER entities using HuggingFace model.

Source code in presidio_analyzer/predefined_recognizers/ner/huggingface_ner_recognizer.py
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
class HuggingFaceNerRecognizer(LocalRecognizer):
    """HuggingFace Transformers based NER Recognizer.

    This recognizer uses HuggingFace pipeline directly for NER,
    bypassing spaCy tokenizer alignment issues. It's particularly
    useful for agglutinative languages (Korean, Japanese, Turkish, etc.)
    where particles attach to nouns.

    Unlike the standard TransformersNlpEngine approach, this recognizer:
    - Uses HuggingFace pipeline directly (not through spaCy)
    - Returns NER results without char_span alignment
    - Supports any HuggingFace token-classification model
    - Works with any language that has a HuggingFace NER model

    Example:
        >>> from presidio_analyzer import AnalyzerEngine
        >>> from presidio_analyzer.predefined_recognizers.ner import (
        ...     HuggingFaceNerRecognizer
        ... )
        >>>
        >>> # For Korean
        >>> recognizer = HuggingFaceNerRecognizer(
        ...     model_name="test-owner/test-model",
        ...     supported_language="ko"
        ... )
        >>>
        >>> # For English
        >>> recognizer = HuggingFaceNerRecognizer(
        ...     model_name="dslim/bert-base-NER",
        ...     supported_language="en"
        ... )
        >>>
        >>> analyzer = AnalyzerEngine()
        >>> analyzer.registry.add_recognizer(recognizer)
    """

    # Default label mapping from common NER models to Presidio entities
    DEFAULT_LABEL_MAPPING = {
        # Standard NER labels (CoNLL format)
        "PER": "PERSON",
        "LOC": "LOCATION",
        "ORG": "ORGANIZATION",
        "MISC": "MISC",
        # Date/Time labels
        "DAT": "DATE_TIME",
        "DATE": "DATE_TIME",
        "TIME": "DATE_TIME",
        # Korean model labels (KoELECTRA, etc.)
        "PS": "PERSON",
        "LC": "LOCATION",
        "OG": "ORGANIZATION",
        "DT": "DATE_TIME",
        "TI": "DATE_TIME",
        # BERT-base-NER style labels (with B-/I- prefix stripped)
        "PERSON": "PERSON",
        "LOCATION": "LOCATION",
        "ORGANIZATION": "ORGANIZATION",
        "DATE_TIME": "DATE_TIME",
    }
    DEFAULT_HF_TASK = "token-classification"

    def __init__(
        self,
        supported_entities: Optional[List[str]] = None,
        name: str = "HuggingFaceNerRecognizer",
        supported_language: str = "en",
        version: str = "0.0.1",
        context: Optional[List[str]] = None,
        model_name: Optional[str] = None,
        label_mapping: Optional[Dict[str, str]] = None,
        threshold: float = 0.3,
        aggregation_strategy: str = "simple",
        chunk_overlap: int = 40,
        chunk_size: int = 400,
        device: Optional[Union[str, int]] = None,
        tokenizer_name: Optional[str] = None,
        text_chunker: Optional[BaseTextChunker] = None,
        label_prefixes: Optional[List[str]] = None,
        **kwargs,
    ):
        """Initialize the HuggingFace NER Recognizer.

        :param supported_entities: List of supported entities.
            If None, uses entities from label_mapping.
        :param name: Name of the recognizer
        :param supported_language: Language code (e.g., "en", "ko", "ja")
        :param version: Version of the recognizer
        :param context: Context words (N/A for this recognizer)
        :param model_name: HuggingFace model name or path.
            If None, must be set before calling load().
        :param label_mapping: Mapping from model labels to Presidio entities.
            If None, uses DEFAULT_LABEL_MAPPING.
        :param threshold: Minimum confidence score threshold (0.0 - 1.0)
        :param aggregation_strategy: Token aggregation strategy
            ("simple", "first", "average", "max").
            Recommendation: Use "simple" or "first" so that entities are pre-aggregated
            by the model, preserving performance and alignment.
        :param device: Device to use. Accepts:
            - "cpu" or -1 for CPU
            - "cuda" or "cuda:N" or int N for GPU
            - None for auto-detection (GPU if available, else CPU)
            Defaults to None.
        :param chunk_overlap: Number of characters to overlap between chunks.
        :param chunk_size: Maximum number of characters per chunk.
        :param tokenizer_name: Name of the tokenizer. Defaults to model_name.
        :param text_chunker: Custom text chunking strategy. If None, uses
            CharacterBasedTextChunker with provided chunk_size and chunk_overlap.
        :param label_prefixes: List of label prefixes to strip (e.g., B-, I-).
        :raises ImportError: If transformers or torch libraries are not installed.
        """
        # Early check for required dependencies
        if hf_pipeline is None:
            raise ImportError(
                "transformers is not installed. Please install it "
                "(pip install transformers torch) to use this recognizer."
            )
        if torch is None:
            raise ImportError(
                "torch is not installed. Please install it "
                "(pip install torch) to use this recognizer."
            )

        self.model_name = model_name
        self.tokenizer_name = tokenizer_name or model_name
        self.label_mapping = (
            label_mapping if label_mapping is not None else self.DEFAULT_LABEL_MAPPING
        )
        self.threshold = threshold
        self.aggregation_strategy = aggregation_strategy
        if self.aggregation_strategy == "none":
            logger.warning(
                "aggregation_strategy='none' may result in fragmented entities "
                "(e.g., 'B-PER', 'I-PER'). Recommended: 'simple' or 'first'."
            )
        self.device = self._parse_device(device)
        self.label_prefixes = label_prefixes or ["B-", "I-", "U-", "L-"]
        self.ner_pipeline = None

        if kwargs:
            logger.warning(
                "Ignoring unsupported kwargs in %s: %s",
                name,
                sorted(kwargs.keys()),
            )

        # Derive supported entities from label mapping
        if supported_entities:
            final_supported_entities = supported_entities
        else:
            # Use sorted set for deterministic order
            final_supported_entities = sorted(list(set(self.label_mapping.values())))

        super().__init__(
            supported_entities=final_supported_entities,
            name=name,
            supported_language=supported_language,
            version=version,
            context=context,
        )

        # Initialize the text chunker
        if text_chunker:
            self.text_chunker = text_chunker
        else:
            self.text_chunker = CharacterBasedTextChunker(
                chunk_size=chunk_size,
                chunk_overlap=chunk_overlap,
            )

        logger.info(
            f"Initialized {self.name} with model={self.model_name}, "
            f"threshold={self.threshold}, device={self.device}"
        )

    def _parse_device(self, device: Optional[Union[str, int]]) -> int:
        """Parse device string or int into a transformer-compatible int.

        Normalize diverse device inputs (None, "cpu", "cuda", "cuda:1", 0)
        to a standard integer format for the Transformers pipeline.
        If None, it uses Presidio's DeviceDetector for auto-detection.
        """
        if device is None:
            detected = device_detector.get_device()
            return 0 if detected == "cuda" else -1

        if isinstance(device, int):
            return device

        device_str = str(device).strip().lower()
        if device_str == "cpu":
            return -1
        if device_str == "cuda":
            return 0
        if device_str.startswith("cuda:"):
            try:
                return int(device_str.split(":", 1)[1])
            except (ValueError, IndexError):
                pass

        raise ValueError(
            f"Invalid device specified: {device}. "
            "Accepts 'cpu', 'cuda', 'cuda:N', or integer index."
        )

    def load(self) -> None:
        """Load the HuggingFace NER pipeline.

        This method handles:
        1. Hardware acceleration setup (CUDA validation and fallback)
        2. Lazy-loading of the heavyweight ML pipeline.

        :raises ValueError: If model_name is not set
        """
        if self.ner_pipeline is not None:
            return

        if not self.model_name:
            raise ValueError(
                "model_name must be set before calling load(). "
                "Pass it to __init__() or set it directly."
            )

        # Device validation and fallback
        device = self.device
        if device >= 0:
            if not torch.cuda.is_available():
                logger.warning("CUDA is not available. Falling back to CPU.")
                device = -1
            elif device >= torch.cuda.device_count():
                logger.warning(
                    "Device index %d out of range (count=%d). Falling back to CPU.",
                    device,
                    torch.cuda.device_count(),
                )
                device = -1

        logger.info(f"Loading HuggingFace model: {self.model_name}, device={device}")

        try:
            self.ner_pipeline = hf_pipeline(
                self.DEFAULT_HF_TASK,
                model=self.model_name,
                tokenizer=self.tokenizer_name,
                aggregation_strategy=self.aggregation_strategy,
                device=device,
            )
            logger.info(f"Successfully loaded {self.model_name}")
        except Exception:
            logger.exception(f"Failed to load model {self.model_name}")
            raise

    def _normalize_label(self, label: str) -> str:
        """Normalize label by removing prefixes like B-/I-/U-/L-.

        This method strips the prefix so that label_mapping can correctly
        map to Presidio entities.

        :param label: The raw label from the model.
        :return: Normalized label.
        """
        if isinstance(label, str):
            for prefix in self.label_prefixes:
                if label.startswith(prefix):
                    return label[len(prefix) :]
        return label

    def _predict_chunk(self, chunk_text: str) -> List[RecognizerResult]:
        """Perform NER prediction on a single text chunk.

        This is a callback method used by the CharacterBasedTextChunker.

        :param chunk_text: The chunk of text to analyze.
        :return: List of RecognizerResult objects.
        """
        chunk_results = []
        # Run inference on the chunk
        try:
            preds = self.ner_pipeline(chunk_text)
        except Exception as e:
            logger.warning(f"NER prediction failed for chunk: {e}", exc_info=True)
            return []

        # Helper to process a single prediction dictionary
        def process_pred(pred: Dict[str, Any]) -> None:
            """Convert a single HuggingFace prediction dict to RecognizerResult."""
            raw_label = pred.get("entity_group") or pred.get("entity")
            if not raw_label:
                return

            model_label = self._normalize_label(raw_label)

            presidio_entity = self.label_mapping.get(model_label)
            if not presidio_entity:
                # If label is not mapped, use the model's label as is. This allows
                # discovering entities not explicitly defined in the mapping.
                presidio_entity = model_label

            raw_score = pred.get("score", 0.0)
            try:
                score = float(raw_score)
            except (TypeError, ValueError):
                logger.warning("Failed to convert score to float: %r", raw_score)
                return

            if score < self.threshold:
                return

            start = pred.get("start")
            end = pred.get("end")
            if start is None or end is None:
                return

            if raw_label == model_label:
                textual_explanation = (
                    f"Identified as {presidio_entity} by {self.model_name} "
                    f"(label: {raw_label})"
                )
            else:
                textual_explanation = (
                    f"Identified as {presidio_entity} by {self.model_name} "
                    f"(original label: {raw_label}, normalized: {model_label})"
                )

            explanation = AnalysisExplanation(
                recognizer=self.name,
                original_score=score,
                textual_explanation=textual_explanation,
            )

            chunk_results.append(
                RecognizerResult(
                    entity_type=presidio_entity,
                    start=start,
                    end=end,
                    score=score,
                    analysis_explanation=explanation,
                )
            )

        # Validate preds is a list before iterating
        if not isinstance(preds, list):
            logger.warning("Unexpected pipeline output type: %s", type(preds))
            return []

        for pred in preds:
            if isinstance(pred, dict):
                process_pred(pred)
            else:
                logger.warning("Unexpected prediction item type: %s", type(pred))

        return chunk_results

    def analyze(
        self,
        text: str,
        entities: List[str],
        nlp_artifacts: Optional[NlpArtifacts] = None,
    ) -> List[RecognizerResult]:
        """Analyze text for NER entities using HuggingFace model.

        This method uses the CharacterBasedTextChunker to handle long texts
        and ignores nlp_artifacts (spaCy results) to bypass tokenizer alignment issues.

        :param text: The text to analyze
        :param entities: List of entity types to detect
        :param nlp_artifacts: Ignored (spaCy artifacts not used)
        :return: List of RecognizerResult with detected entities
        """
        if not text or not text.strip():
            return []

        # Defensive guard for entities input
        entities = entities or []

        if not self.ner_pipeline:
            self.load()

        # Use the standard predict_with_chunking method from BaseTextChunker
        # This handles chunking, processing, and duplicating/merging results
        results = self.text_chunker.predict_with_chunking(
            text=text,
            predict_func=self._predict_chunk,
        )

        # Filter policy:
        # 1. If an entity is requested, it is always kept.
        # 2. If it's a known 'supported' entity but NOT requested, it is filtered out.
        # 3. If it's an unmapped/unexpected entity, it is kept for Discovery.
        if entities:
            requested = set(entities)
            supported = set(self.supported_entities)
            results = [
                r
                for r in results
                if (r.entity_type in requested) or (r.entity_type not in supported)
            ]

        return results

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

load

load() -> None

Load the HuggingFace NER pipeline.

This method handles: 1. Hardware acceleration setup (CUDA validation and fallback) 2. Lazy-loading of the heavyweight ML pipeline.

RAISES DESCRIPTION
ValueError

If model_name is not set

Source code in presidio_analyzer/predefined_recognizers/ner/huggingface_ner_recognizer.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def load(self) -> None:
    """Load the HuggingFace NER pipeline.

    This method handles:
    1. Hardware acceleration setup (CUDA validation and fallback)
    2. Lazy-loading of the heavyweight ML pipeline.

    :raises ValueError: If model_name is not set
    """
    if self.ner_pipeline is not None:
        return

    if not self.model_name:
        raise ValueError(
            "model_name must be set before calling load(). "
            "Pass it to __init__() or set it directly."
        )

    # Device validation and fallback
    device = self.device
    if device >= 0:
        if not torch.cuda.is_available():
            logger.warning("CUDA is not available. Falling back to CPU.")
            device = -1
        elif device >= torch.cuda.device_count():
            logger.warning(
                "Device index %d out of range (count=%d). Falling back to CPU.",
                device,
                torch.cuda.device_count(),
            )
            device = -1

    logger.info(f"Loading HuggingFace model: {self.model_name}, device={device}")

    try:
        self.ner_pipeline = hf_pipeline(
            self.DEFAULT_HF_TASK,
            model=self.model_name,
            tokenizer=self.tokenizer_name,
            aggregation_strategy=self.aggregation_strategy,
            device=device,
        )
        logger.info(f"Successfully loaded {self.model_name}")
    except Exception:
        logger.exception(f"Failed to load model {self.model_name}")
        raise

analyze

analyze(
    text: str, entities: List[str], nlp_artifacts: Optional[NlpArtifacts] = None
) -> List[RecognizerResult]

Analyze text for NER entities using HuggingFace model.

This method uses the CharacterBasedTextChunker to handle long texts and ignores nlp_artifacts (spaCy results) to bypass tokenizer alignment issues.

PARAMETER DESCRIPTION
text

The text to analyze

TYPE: str

entities

List of entity types to detect

TYPE: List[str]

nlp_artifacts

Ignored (spaCy artifacts not used)

TYPE: Optional[NlpArtifacts] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]

List of RecognizerResult with detected entities

Source code in presidio_analyzer/predefined_recognizers/ner/huggingface_ner_recognizer.py
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
) -> List[RecognizerResult]:
    """Analyze text for NER entities using HuggingFace model.

    This method uses the CharacterBasedTextChunker to handle long texts
    and ignores nlp_artifacts (spaCy results) to bypass tokenizer alignment issues.

    :param text: The text to analyze
    :param entities: List of entity types to detect
    :param nlp_artifacts: Ignored (spaCy artifacts not used)
    :return: List of RecognizerResult with detected entities
    """
    if not text or not text.strip():
        return []

    # Defensive guard for entities input
    entities = entities or []

    if not self.ner_pipeline:
        self.load()

    # Use the standard predict_with_chunking method from BaseTextChunker
    # This handles chunking, processing, and duplicating/merging results
    results = self.text_chunker.predict_with_chunking(
        text=text,
        predict_func=self._predict_chunk,
    )

    # Filter policy:
    # 1. If an entity is requested, it is always kept.
    # 2. If it's a known 'supported' entity but NOT requested, it is filtered out.
    # 3. If it's an unmapped/unexpected entity, it is kept for Discovery.
    if entities:
        requested = set(entities)
        supported = set(self.supported_entities)
        results = [
            r
            for r in results
            if (r.entity_type in requested) or (r.entity_type not in supported)
        ]

    return results

MedicalNERRecognizer

Bases: HuggingFaceNerRecognizer

Recognize medical/clinical entities using blaze999/Medical-NER.

Thin subclass of :class:HuggingFaceNerRecognizer that sets medical-specific defaults. Uses HuggingFace transformers.pipeline directly (no spaCy dependency).

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

load

Load the HuggingFace NER pipeline.

analyze

Analyze text for NER entities using HuggingFace model.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

Source code in presidio_analyzer/predefined_recognizers/ner/medical_ner_recognizer.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class MedicalNERRecognizer(HuggingFaceNerRecognizer):
    """Recognize medical/clinical entities using blaze999/Medical-NER.

    Thin subclass of :class:`HuggingFaceNerRecognizer` that sets
    medical-specific defaults. Uses HuggingFace ``transformers.pipeline``
    directly (no spaCy dependency).
    """

    ENTITIES = list(DEFAULT_MEDICAL_ENTITY_MAPPING.values())

    def __init__(
        self,
        model_name: str = "blaze999/Medical-NER",
        label_mapping: Optional[Dict[str, str]] = None,
        supported_entities: Optional[List[str]] = None,
        name: str = "MedicalNERRecognizer",
        supported_language: str = "en",
        aggregation_strategy: str = "simple",
        threshold: float = 0.3,
        device: Optional[Union[str, int]] = None,
        text_chunker: Optional[BaseTextChunker] = None,
    ):
        """Initialize the Medical NER recognizer.

        :param model_name: HuggingFace model name/path.
            Default: ``blaze999/Medical-NER``
        :param label_mapping: Model label -> Presidio entity mapping.
            Default: :data:`DEFAULT_MEDICAL_ENTITY_MAPPING`
        :param supported_entities: Entity types to return (None = all mapped).
        :param name: Recognizer name
        :param supported_language: Language code
        :param aggregation_strategy: Pipeline aggregation strategy
        :param threshold: Minimum confidence score (0.0 - 1.0)
        :param device: Device string/int (None = auto-detect)
        :param text_chunker: Custom text chunker (None = default)
        """
        super().__init__(
            model_name=model_name,
            label_mapping=label_mapping or DEFAULT_MEDICAL_ENTITY_MAPPING,
            supported_entities=supported_entities,
            name=name,
            supported_language=supported_language,
            aggregation_strategy=aggregation_strategy,
            threshold=threshold,
            device=device,
            text_chunker=text_chunker,
        )

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

load

load() -> None

Load the HuggingFace NER pipeline.

This method handles: 1. Hardware acceleration setup (CUDA validation and fallback) 2. Lazy-loading of the heavyweight ML pipeline.

RAISES DESCRIPTION
ValueError

If model_name is not set

Source code in presidio_analyzer/predefined_recognizers/ner/huggingface_ner_recognizer.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def load(self) -> None:
    """Load the HuggingFace NER pipeline.

    This method handles:
    1. Hardware acceleration setup (CUDA validation and fallback)
    2. Lazy-loading of the heavyweight ML pipeline.

    :raises ValueError: If model_name is not set
    """
    if self.ner_pipeline is not None:
        return

    if not self.model_name:
        raise ValueError(
            "model_name must be set before calling load(). "
            "Pass it to __init__() or set it directly."
        )

    # Device validation and fallback
    device = self.device
    if device >= 0:
        if not torch.cuda.is_available():
            logger.warning("CUDA is not available. Falling back to CPU.")
            device = -1
        elif device >= torch.cuda.device_count():
            logger.warning(
                "Device index %d out of range (count=%d). Falling back to CPU.",
                device,
                torch.cuda.device_count(),
            )
            device = -1

    logger.info(f"Loading HuggingFace model: {self.model_name}, device={device}")

    try:
        self.ner_pipeline = hf_pipeline(
            self.DEFAULT_HF_TASK,
            model=self.model_name,
            tokenizer=self.tokenizer_name,
            aggregation_strategy=self.aggregation_strategy,
            device=device,
        )
        logger.info(f"Successfully loaded {self.model_name}")
    except Exception:
        logger.exception(f"Failed to load model {self.model_name}")
        raise

analyze

analyze(
    text: str, entities: List[str], nlp_artifacts: Optional[NlpArtifacts] = None
) -> List[RecognizerResult]

Analyze text for NER entities using HuggingFace model.

This method uses the CharacterBasedTextChunker to handle long texts and ignores nlp_artifacts (spaCy results) to bypass tokenizer alignment issues.

PARAMETER DESCRIPTION
text

The text to analyze

TYPE: str

entities

List of entity types to detect

TYPE: List[str]

nlp_artifacts

Ignored (spaCy artifacts not used)

TYPE: Optional[NlpArtifacts] DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]

List of RecognizerResult with detected entities

Source code in presidio_analyzer/predefined_recognizers/ner/huggingface_ner_recognizer.py
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def analyze(
    self,
    text: str,
    entities: List[str],
    nlp_artifacts: Optional[NlpArtifacts] = None,
) -> List[RecognizerResult]:
    """Analyze text for NER entities using HuggingFace model.

    This method uses the CharacterBasedTextChunker to handle long texts
    and ignores nlp_artifacts (spaCy results) to bypass tokenizer alignment issues.

    :param text: The text to analyze
    :param entities: List of entity types to detect
    :param nlp_artifacts: Ignored (spaCy artifacts not used)
    :return: List of RecognizerResult with detected entities
    """
    if not text or not text.strip():
        return []

    # Defensive guard for entities input
    entities = entities or []

    if not self.ner_pipeline:
        self.load()

    # Use the standard predict_with_chunking method from BaseTextChunker
    # This handles chunking, processing, and duplicating/merging results
    results = self.text_chunker.predict_with_chunking(
        text=text,
        predict_func=self._predict_chunk,
    )

    # Filter policy:
    # 1. If an entity is requested, it is always kept.
    # 2. If it's a known 'supported' entity but NOT requested, it is filtered out.
    # 3. If it's an unmapped/unexpected entity, it is kept for Discovery.
    if entities:
        requested = set(entities)
        supported = set(self.supported_entities)
        results = [
            r
            for r in results
            if (r.entity_type in requested) or (r.entity_type not in supported)
        ]

    return results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

SpacyRecognizer

Bases: LocalRecognizer

Recognize PII entities using a spaCy NLP model.

Since the spaCy pipeline is ran by the AnalyzerEngine/SpacyNlpEngine,
this recognizer only extracts the entities from the NlpArtifacts
and returns them.
METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

build_explanation

Create explanation for why this result was detected.

Source code in presidio_analyzer/predefined_recognizers/nlp_engine_recognizers/spacy_recognizer.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
class SpacyRecognizer(LocalRecognizer):
    """
    Recognize PII entities using a spaCy NLP model.

        Since the spaCy pipeline is ran by the AnalyzerEngine/SpacyNlpEngine,
        this recognizer only extracts the entities from the NlpArtifacts
        and returns them.

    """

    ENTITIES = ["DATE_TIME", "NRP", "LOCATION", "PERSON", "ORGANIZATION"]

    DEFAULT_EXPLANATION = "Identified as {} by Spacy's Named Entity Recognition"

    # deprecated, use MODEL_TO_PRESIDIO_MAPPING in NerModelConfiguration instead
    CHECK_LABEL_GROUPS = [
        ({"LOCATION"}, {"GPE", "LOC"}),
        ({"PERSON", "PER"}, {"PERSON", "PER"}),
        ({"DATE_TIME"}, {"DATE", "TIME"}),
        ({"NRP"}, {"NORP"}),
        ({"ORGANIZATION"}, {"ORG"}),
    ]

    def __init__(
        self,
        supported_language: str = "en",
        supported_entities: Optional[List[str]] = None,
        ner_strength: float = 0.85,
        default_explanation: Optional[str] = None,
        check_label_groups: Optional[List[Tuple[Set, Set]]] = None,
        context: Optional[List[str]] = None,
        name: Optional[str] = None,
    ):
        """Initialize the SpaCy recognizer.

        :param supported_language: Language this recognizer supports
        :param supported_entities: The entities this recognizer can detect
        :param ner_strength: Default confidence for NER prediction
        :param check_label_groups: (DEPRECATED) Tuple containing Presidio entity names
        :param default_explanation: Default explanation for the results when using return_decision_process=True
        """  # noqa: E501

        self.ner_strength = ner_strength
        if check_label_groups:
            warnings.warn(
                "check_label_groups is deprecated and isn't used;"
                "entities are mapped in NerModelConfiguration",
                DeprecationWarning,
                2,
            )

        self.default_explanation = (
            default_explanation if default_explanation else self.DEFAULT_EXPLANATION
        )
        supported_entities = supported_entities if supported_entities else self.ENTITIES
        super().__init__(
            supported_entities=supported_entities,
            supported_language=supported_language,
            context=context,
            name=name,
        )

    def load(self) -> None:  # noqa: D102
        # no need to load anything as the analyze method already receives
        # preprocessed nlp artifacts
        pass

    def build_explanation(
        self, original_score: float, explanation: str
    ) -> AnalysisExplanation:
        """
        Create explanation for why this result was detected.

        :param original_score: Score given by this recognizer
        :param explanation: Explanation string
        :return:
        """
        explanation = AnalysisExplanation(
            recognizer=self.name,
            original_score=original_score,
            textual_explanation=explanation,
        )
        return explanation

    def analyze(self, text: str, entities, nlp_artifacts=None):  # noqa: D102
        results = []
        if not nlp_artifacts:
            logger.warning("Skipping SpaCy, nlp artifacts not provided...")
            return results

        ner_entities = nlp_artifacts.entities
        ner_scores = nlp_artifacts.scores

        for ner_entity, ner_score in zip(ner_entities, ner_scores):
            if (
                ner_entity.label_ not in entities
                or ner_entity.label_ not in self.supported_entities
            ):
                logger.debug(
                    f"Skipping entity {ner_entity.label_} "
                    f"as it is not in the supported entities list"
                )
                continue

            textual_explanation = self.DEFAULT_EXPLANATION.format(ner_entity.label_)
            explanation = self.build_explanation(ner_score, textual_explanation)
            spacy_result = RecognizerResult(
                entity_type=ner_entity.label_,
                start=ner_entity.start_char,
                end=ner_entity.end_char,
                score=ner_score,
                analysis_explanation=explanation,
                recognition_metadata={
                    RecognizerResult.RECOGNIZER_NAME_KEY: self.name,
                    RecognizerResult.RECOGNIZER_IDENTIFIER_KEY: self.id,
                },
            )
            results.append(spacy_result)

        return results

    @staticmethod
    def __check_label(
        entity: str, label: str, check_label_groups: Tuple[Set, Set]
    ) -> bool:
        raise DeprecationWarning("__check_label is deprecated")

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

build_explanation

build_explanation(
    original_score: float, explanation: str
) -> AnalysisExplanation

Create explanation for why this result was detected.

PARAMETER DESCRIPTION
original_score

Score given by this recognizer

TYPE: float

explanation

Explanation string

TYPE: str

RETURNS DESCRIPTION
AnalysisExplanation
Source code in presidio_analyzer/predefined_recognizers/nlp_engine_recognizers/spacy_recognizer.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def build_explanation(
    self, original_score: float, explanation: str
) -> AnalysisExplanation:
    """
    Create explanation for why this result was detected.

    :param original_score: Score given by this recognizer
    :param explanation: Explanation string
    :return:
    """
    explanation = AnalysisExplanation(
        recognizer=self.name,
        original_score=original_score,
        textual_explanation=explanation,
    )
    return explanation

StanzaRecognizer

Bases: SpacyRecognizer

Recognize entities using the Stanza NLP package.

See https://stanfordnlp.github.io/stanza/. Uses the spaCy-Stanza package (https://github.com/explosion/spacy-stanza) to align Stanza's interface with spaCy's

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return the list of entities this recognizer can identify.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

build_explanation

Create explanation for why this result was detected.

Source code in presidio_analyzer/predefined_recognizers/nlp_engine_recognizers/stanza_recognizer.py
 8
 9
10
11
12
13
14
15
16
17
18
19
class StanzaRecognizer(SpacyRecognizer):
    """
    Recognize entities using the Stanza NLP package.

    See https://stanfordnlp.github.io/stanza/.
    Uses the spaCy-Stanza package (https://github.com/explosion/spacy-stanza) to align
    Stanza's interface with spaCy's
    """

    def __init__(self, name: Optional[str] = None, **kwargs):
        self.DEFAULT_EXPLANATION = self.DEFAULT_EXPLANATION.replace("Spacy", "Stanza")
        super().__init__(name=name, **kwargs)

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
209
210
211
212
213
214
215
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

build_explanation

build_explanation(
    original_score: float, explanation: str
) -> AnalysisExplanation

Create explanation for why this result was detected.

PARAMETER DESCRIPTION
original_score

Score given by this recognizer

TYPE: float

explanation

Explanation string

TYPE: str

RETURNS DESCRIPTION
AnalysisExplanation
Source code in presidio_analyzer/predefined_recognizers/nlp_engine_recognizers/spacy_recognizer.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def build_explanation(
    self, original_score: float, explanation: str
) -> AnalysisExplanation:
    """
    Create explanation for why this result was detected.

    :param original_score: Score given by this recognizer
    :param explanation: Explanation string
    :return:
    """
    explanation = AnalysisExplanation(
        recognizer=self.name,
        original_score=original_score,
        textual_explanation=explanation,
    )
    return explanation

AzureHealthDeidRecognizer

Bases: RemoteRecognizer

Wrapper for PHI detection using Azure Health Data Services de-identification.

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

get_supported_entities

Return the list of entities supported by this recognizer.

analyze

Analyze text using Azure Health Data Services Deidentification (TAG operation).

Source code in presidio_analyzer/predefined_recognizers/third_party/ahds_recognizer.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
class AzureHealthDeidRecognizer(RemoteRecognizer):
    """Wrapper for PHI detection using Azure Health Data Services de-identification."""

    def __init__(
        self,
        supported_entities: Optional[List[str]] = None,
        supported_language: str = "en",
        client: Optional[DeidentificationClient] = None,
        name: Optional[str] = None,
    ):
        """
        Wrap PHI detection using Azure Health Data Services de-identification.

        :param supported_entities: List of supported entities for this recognizer.
        :param supported_language: Language code (not used, only 'en' supported).
        :param client: Optional DeidentificationClient instance.
        """
        super().__init__(
            supported_entities=supported_entities,
            supported_language=supported_language,
            name=name if name else "Azure Health Data Services Deidentification",
            version="1.0.0",
        )


        endpoint = os.getenv("AHDS_ENDPOINT", None)

        if client is None:
            if endpoint is None:
                raise ValueError(
                    "AHDS de-identification endpoint is required. "
                    "Please provide an endpoint "
                    "or set the AHDS_ENDPOINT environment variable."
                )

            if not DeidentificationClient:
                raise ImportError(
                    "Azure Health Data Services Deidentification SDK is not available. "
                    "Please install azure-health-deidentification and azure-identity."
                )

            # Use environment-aware credential (DefaultAzureCredential for dev,
            # ChainedTokenCredential for production)
            credential = get_azure_credential()
            client = DeidentificationClient(endpoint, credential)

        self.deid_client = client

        if not supported_entities:
            self.supported_entities = self._get_supported_entities()

    @staticmethod
    def _get_supported_entities() -> List[str]:
        if PhiCategory:
            try:
                # PhiCategory is an enum, try to get the actual enum names
                return [category.name for category in PhiCategory]
            except Exception:
                return ImportError

    def get_supported_entities(self) -> List[str]:
        """
        Return the list of entities supported by this recognizer.

        Returns
            List[str]: A list of supported entity names as strings.
        """
        return self.supported_entities

    def analyze(
        self, text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None
    ) -> List[RecognizerResult]:
        """
        Analyze text using Azure Health Data Services Deidentification (TAG operation).

        :param text: Text to analyze
        :param entities: List of entities to return (optional)
        :param nlp_artifacts: Not used
        :return: List of RecognizerResult for each PHI entity found
        """
        if not entities:
            entities = self.supported_entities

        body = DeidentificationContent(
            input_text=text,
            operation_type=DeidentificationOperationType.TAG
        )
        result = self.deid_client.deidentify_text(body)

        recognizer_results = []
        if result.tagger_result and result.tagger_result.entities:
            for entity in result.tagger_result.entities:
                category = entity.category.upper()
                if category not in [e.upper() for e in entities]:
                    continue
                analysis_explanation = AzureHealthDeidRecognizer._build_explanation(
                    entity_type=category
                )
                recognizer_results.append(
                    RecognizerResult(
                        entity_type=category,
                        start=entity.offset.code_point,
                        end=entity.offset.code_point + entity.length.code_point,
                        score=round(entity.confidence_score, 2),
                        analysis_explanation=analysis_explanation,
                    )
                )
        return recognizer_results

    @staticmethod
    def _build_explanation(entity_type: str) -> AnalysisExplanation:
        explanation = AnalysisExplanation(
            recognizer=AzureHealthDeidRecognizer.__class__.__name__,
            original_score=1.0,
            textual_explanation=(
            f"Identified as {entity_type} by Azure Health Data Services "
            "Deidentification"
            ),
        )
        return explanation

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities supported by this recognizer.

Returns List[str]: A list of supported entity names as strings.

Source code in presidio_analyzer/predefined_recognizers/third_party/ahds_recognizer.py
90
91
92
93
94
95
96
97
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities supported by this recognizer.

    Returns
        List[str]: A list of supported entity names as strings.
    """
    return self.supported_entities

analyze

analyze(
    text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None
) -> List[RecognizerResult]

Analyze text using Azure Health Data Services Deidentification (TAG operation).

PARAMETER DESCRIPTION
text

Text to analyze

TYPE: str

entities

List of entities to return (optional)

TYPE: List[str] DEFAULT: None

nlp_artifacts

Not used

TYPE: NlpArtifacts DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]

List of RecognizerResult for each PHI entity found

Source code in presidio_analyzer/predefined_recognizers/third_party/ahds_recognizer.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def analyze(
    self, text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None
) -> List[RecognizerResult]:
    """
    Analyze text using Azure Health Data Services Deidentification (TAG operation).

    :param text: Text to analyze
    :param entities: List of entities to return (optional)
    :param nlp_artifacts: Not used
    :return: List of RecognizerResult for each PHI entity found
    """
    if not entities:
        entities = self.supported_entities

    body = DeidentificationContent(
        input_text=text,
        operation_type=DeidentificationOperationType.TAG
    )
    result = self.deid_client.deidentify_text(body)

    recognizer_results = []
    if result.tagger_result and result.tagger_result.entities:
        for entity in result.tagger_result.entities:
            category = entity.category.upper()
            if category not in [e.upper() for e in entities]:
                continue
            analysis_explanation = AzureHealthDeidRecognizer._build_explanation(
                entity_type=category
            )
            recognizer_results.append(
                RecognizerResult(
                    entity_type=category,
                    start=entity.offset.code_point,
                    end=entity.offset.code_point + entity.length.code_point,
                    score=round(entity.confidence_score, 2),
                    analysis_explanation=analysis_explanation,
                )
            )
    return recognizer_results

AzureAILanguageRecognizer

Bases: RemoteRecognizer

Wrapper for PII detection using Azure AI Language.

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

get_supported_entities

Return the list of entities this recognizer can identify.

analyze

Analyze text using Azure AI Language.

Source code in presidio_analyzer/predefined_recognizers/third_party/azure_ai_language.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
class AzureAILanguageRecognizer(RemoteRecognizer):
    """Wrapper for PII detection using Azure AI Language."""

    def __init__(
        self,
        supported_entities: Optional[List[str]] = None,
        supported_language: str = "en",
        ta_client: Optional["TextAnalyticsClient"] = None,
        azure_ai_key: Optional[str] = None,
        azure_ai_endpoint: Optional[str] = None,
    ):
        """
        Wrap the PII detection in Azure AI Language.

        :param supported_entities: List of supported entities for this recognizer.
        If None, all supported entities will be used.
        :param supported_language: Language code to use for the recognizer.
        :param ta_client: object of type TextAnalyticsClient. If missing,
        the client will be created using the key and endpoint.
        :param azure_ai_key: Azure AI for language key
        :param azure_ai_endpoint: Azure AI for language endpoint

        For more info, see https://learn.microsoft.com/en-us/azure/ai-services/language-service/personally-identifiable-information/overview
        """

        super().__init__(
            supported_entities=supported_entities,
            supported_language=supported_language,
            name="Azure AI Language PII",
            version="5.2.0",
        )

        is_available = bool(TextAnalyticsClient)
        if not ta_client and not is_available:
            raise ValueError(
                "Azure AI Language is not available. "
                "Please install the required dependencies:"
                "1. azure-ai-textanalytics"
                "2. azure-core"
            )

        if not supported_entities:
            self.supported_entities = self.__get_azure_ai_supported_entities()

        if not ta_client:
            ta_client = self.__authenticate_client(azure_ai_key, azure_ai_endpoint)
        self.ta_client = ta_client

    def get_supported_entities(self) -> List[str]:
        """
        Return the list of entities this recognizer can identify.

        :return: A list of the supported entities by this recognizer
        """
        return self.supported_entities

    @staticmethod
    def __get_azure_ai_supported_entities() -> List[str]:
        """Return the list of all supported entities for Azure AI Language."""
        from azure.ai.textanalytics._models import PiiEntityCategory

        return [r.value.upper() for r in PiiEntityCategory]

    @staticmethod
    def __authenticate_client(key: str, endpoint: str) -> TextAnalyticsClient:
        """Authenticate the client using the key and endpoint.

        :param key: Azure AI Language key
        :param endpoint: Azure AI Language endpoint
        """
        key = key if key else os.getenv("AZURE_AI_KEY", None)
        endpoint = endpoint if endpoint else os.getenv("AZURE_AI_ENDPOINT", None)
        if key is None:
            raise ValueError(
                "Azure AI Language key is required. "
                "Please provide a key or set the AZURE_AI_KEY environment variable."
            )
        if endpoint is None:
            raise ValueError(
                "Azure AI Language endpoint is required. "
                "Please provide an endpoint "
                "or set the AZURE_AI_ENDPOINT environment variable."
            )

        ta_credential = AzureKeyCredential(key)
        text_analytics_client = TextAnalyticsClient(
            endpoint=endpoint, credential=ta_credential
        )
        return text_analytics_client

    def analyze(
        self, text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None
    ) -> List[RecognizerResult]:
        """
        Analyze text using Azure AI Language.

        :param text: Text to analyze
        :param entities: List of entities to return
        :param nlp_artifacts: Object of type NlpArtifacts, not used in this recognizer.
        :return: A list of RecognizerResult, one per each entity found in the text.
        """
        if not entities:
            entities = self.supported_entities
        response = self.ta_client.recognize_pii_entities(
            [text], language=self.supported_language
        )
        results = [doc for doc in response if not doc.is_error]
        recognizer_results = []
        for res in results:
            for entity in res.entities:
                entity.category = entity.category.upper()
                if entity.category.lower() not in [
                    ent.lower() for ent in self.supported_entities
                ]:
                    continue
                if entity.category.lower() not in [ent.lower() for ent in entities]:
                    continue
                analysis_explanation = AzureAILanguageRecognizer._build_explanation(
                    original_score=entity.confidence_score,
                    entity_type=entity.category,
                )
                recognizer_results.append(
                    RecognizerResult(
                        entity_type=entity.category,
                        start=entity.offset,
                        end=entity.offset + entity.length,
                        score=entity.confidence_score,
                        analysis_explanation=analysis_explanation,
                    )
                )

        return recognizer_results

    @staticmethod
    def _build_explanation(
        original_score: float, entity_type: str
    ) -> AnalysisExplanation:
        explanation = AnalysisExplanation(
            recognizer=AzureAILanguageRecognizer.__class__.__name__,
            original_score=original_score,
            textual_explanation=f"Identified as {entity_type} by Azure AI Language",
        )
        return explanation

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

get_supported_entities

get_supported_entities() -> List[str]

Return the list of entities this recognizer can identify.

RETURNS DESCRIPTION
List[str]

A list of the supported entities by this recognizer

Source code in presidio_analyzer/predefined_recognizers/third_party/azure_ai_language.py
66
67
68
69
70
71
72
def get_supported_entities(self) -> List[str]:
    """
    Return the list of entities this recognizer can identify.

    :return: A list of the supported entities by this recognizer
    """
    return self.supported_entities

analyze

analyze(
    text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None
) -> List[RecognizerResult]

Analyze text using Azure AI Language.

PARAMETER DESCRIPTION
text

Text to analyze

TYPE: str

entities

List of entities to return

TYPE: List[str] DEFAULT: None

nlp_artifacts

Object of type NlpArtifacts, not used in this recognizer.

TYPE: NlpArtifacts DEFAULT: None

RETURNS DESCRIPTION
List[RecognizerResult]

A list of RecognizerResult, one per each entity found in the text.

Source code in presidio_analyzer/predefined_recognizers/third_party/azure_ai_language.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def analyze(
    self, text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None
) -> List[RecognizerResult]:
    """
    Analyze text using Azure AI Language.

    :param text: Text to analyze
    :param entities: List of entities to return
    :param nlp_artifacts: Object of type NlpArtifacts, not used in this recognizer.
    :return: A list of RecognizerResult, one per each entity found in the text.
    """
    if not entities:
        entities = self.supported_entities
    response = self.ta_client.recognize_pii_entities(
        [text], language=self.supported_language
    )
    results = [doc for doc in response if not doc.is_error]
    recognizer_results = []
    for res in results:
        for entity in res.entities:
            entity.category = entity.category.upper()
            if entity.category.lower() not in [
                ent.lower() for ent in self.supported_entities
            ]:
                continue
            if entity.category.lower() not in [ent.lower() for ent in entities]:
                continue
            analysis_explanation = AzureAILanguageRecognizer._build_explanation(
                original_score=entity.confidence_score,
                entity_type=entity.category,
            )
            recognizer_results.append(
                RecognizerResult(
                    entity_type=entity.category,
                    start=entity.offset,
                    end=entity.offset + entity.length,
                    score=entity.confidence_score,
                    analysis_explanation=analysis_explanation,
                )
            )

    return recognizer_results

AzureOpenAILangExtractRecognizer

Bases: LangExtractRecognizer

Concrete implementation of LangExtract recognizer using Azure OpenAI backend.

Provides Azure OpenAI-specific functionality including: - Azure OpenAI endpoint and API key configuration - Deployment name management - API version control - Integration with custom Azure OpenAI provider via LangExtract registry

Source code in presidio_analyzer/predefined_recognizers/third_party/azure_openai_langextract_recognizer.py
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
class AzureOpenAILangExtractRecognizer(LangExtractRecognizer):
    """
    Concrete implementation of LangExtract recognizer using Azure OpenAI backend.

    Provides Azure OpenAI-specific functionality including:
    - Azure OpenAI endpoint and API key configuration
    - Deployment name management
    - API version control
    - Integration with custom Azure OpenAI provider via LangExtract registry
    """

    DEFAULT_API_VERSION = "2024-02-15-preview"

    DEFAULT_CONFIG_PATH = (
        Path(__file__).parent.parent.parent
        / "conf"
        / "langextract_config_azureopenai.yaml"
    )

    def __init__(
        self,
        model_id: Optional[str] = None,
        config_path: Optional[str] = None,
        azure_endpoint: Optional[str] = None,
        api_key: Optional[str] = None,
        api_version: Optional[str] = None,
        supported_language: str = "en",
        name: str = "Azure OpenAI LangExtract PII",
    ):
        """
        Initialize Azure OpenAI LangExtract recognizer for PII/PHI detection.

        Note: Azure OpenAI endpoint and API configuration are not validated
        during initialization. Any connectivity or authentication issues will
        be reported when analyze() is first called.

        Configuration priority for authentication (highest to lowest):
        1. Direct parameters (azure_endpoint, api_key, api_version)
        2. Environment variables (AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY,
           AZURE_OPENAI_API_VERSION)

        Configuration priority for model_id (deployment name):
        1. Direct parameter (model_id)
        2. Config file (langextract.model.model_id)

        :param model_id: Azure OpenAI deployment name (e.g., "gpt-4",
            "gpt-4o"). Overrides config file.
        :param config_path: Path to YAML configuration file. If not provided,
            uses default config.
        :param azure_endpoint: Azure OpenAI endpoint URL (overrides env var).
        :param api_key: Azure OpenAI API key (overrides env var). If not
            provided, uses managed identity.
        :param api_version: Azure OpenAI API version (optional, defaults to
            "2024-02-15-preview").
        :param supported_language: Language this recognizer supports
            (optional, default: "en").
        :raises ImportError: If langextract is not installed.
        :raises ValueError: If Azure OpenAI endpoint is not provided via
            parameter or env var.
        """

        # Determine actual config path
        actual_config_path = (
            config_path if config_path else str(self.DEFAULT_CONFIG_PATH)
        )

        # Get Azure-specific settings with priority: parameter > env var
        self.azure_endpoint = azure_endpoint or os.environ.get("AZURE_OPENAI_ENDPOINT")
        self._validate_azure_endpoint(self.azure_endpoint)

        self.api_key = api_key or os.environ.get("AZURE_OPENAI_API_KEY")
        self.api_version = (
            api_version
            or os.environ.get("AZURE_OPENAI_API_VERSION")
            or self.DEFAULT_API_VERSION
        )

        # Initialize parent class (loads config, sets self.model_id from config)
        super().__init__(
            config_path=actual_config_path,
            name=name,
            supported_language=supported_language,
            extract_params={
                "extract": {
                    "fence_output": True,
                    "use_schema_constraints": False,
                },
            },
        )

        # Override model_id if provided as parameter (deployment name)
        if model_id:
            self.model_id = model_id

    def _validate_azure_endpoint(self, azure_endpoint: Optional[str]) -> None:
        """
        Validate that Azure OpenAI endpoint is provided.

        :param azure_endpoint: Azure OpenAI endpoint URL.
        :raises ValueError: If endpoint is not provided.
        """
        if not azure_endpoint:
            raise ValueError(
                "Azure OpenAI endpoint is required. Provide 'azure_endpoint' parameter "
                f"or set AZURE_OPENAI_ENDPOINT environment variable.\n"
                f"See {AZURE_OPENAI_DOCS_URL} for details."
            )

    def _get_provider_params(self):
        """Return Azure OpenAI-specific params."""
        model_id_with_prefix = f"azure:{self.model_id}"

        language_model_params = {
            "azure_endpoint": self.azure_endpoint,
            "api_version": self.api_version,
            "azure_deployment": self.model_id,
        }

        if self.api_key:
            language_model_params["api_key"] = self.api_key

        return {
            "model_id": model_id_with_prefix,
            "language_model_params": language_model_params,
        }

BasicLangExtractRecognizer

Bases: LangExtractRecognizer

Basic LangExtract recognizer using configurable backend.

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyze text for PII/PHI using LLM.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return list of supported PII entity types.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

Source code in presidio_analyzer/predefined_recognizers/third_party/basic_langextract_recognizer.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class BasicLangExtractRecognizer(LangExtractRecognizer):
    """Basic LangExtract recognizer using configurable backend."""

    DEFAULT_CONFIG_PATH = (
        Path(__file__).parent.parent.parent / "conf" / "langextract_config_basic.yaml"
    )

    def __init__(
        self,
        config_path: Optional[str] = None,
        supported_language: str = "en",
        context: Optional[list] = None,
        name="BasicLangExtractRecognizer",
        **kwargs
    ):
        """Initialize Basic LangExtract recognizer.

        :param config_path: Path to configuration file (optional).
        :param supported_language: Language this recognizer supports
            (optional, default: "en").
        :param context: List of context words
            (optional, currently not used by LLM recognizers).
        """
        actual_config_path = (
            config_path if config_path else str(self.DEFAULT_CONFIG_PATH)
        )

        super().__init__(
            config_path=actual_config_path,
            name=name,
            supported_language=supported_language,
            extract_params={
                "extract": DEFAULT_EXTRACT_PARAMS,
                "language_model": DEFAULT_LANGUAGE_MODEL_PARAMS
            }
        )

        model_config: Dict[str, Any] = self.config.get("model", {})
        provider_config = model_config.get("provider", {})

        self.model_id = model_config.get("model_id")
        self.provider = provider_config.get("name")
        self.provider_kwargs = provider_config.get("kwargs", {})

        # Not ideal, but update _extract_params now that self.config is fully loaded.
        self._extract_params.update(provider_config.get("extract_params", {}))
        self._language_model_params.update(
            provider_config.get("language_model_params", {})
        )

        if not self.provider:
            raise ValueError("Configuration must contain "
                             "'langextract.model.provider.name'")

        if ("api_key" not in self.provider_kwargs
                and "LANGEXTRACT_API_KEY" in os.environ):
            self.provider_kwargs["api_key"] = os.environ["LANGEXTRACT_API_KEY"]

        self.lx_model_config = lx_factory.ModelConfig(
            model_id=self.model_id,
            provider=self.provider,
            provider_kwargs=self.provider_kwargs,
        )

    def _get_provider_params(self):
        """Return supplementary params."""
        return {
            "config": self.lx_model_config,
        }

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: Optional[List[str]] = None,
    nlp_artifacts: Optional[NlpArtifacts] = None,
) -> List[RecognizerResult]

Analyze text for PII/PHI using LLM.

Source code in presidio_analyzer/lm_recognizer.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def analyze(
    self,
    text: str,
    entities: Optional[List[str]] = None,
    nlp_artifacts: Optional["NlpArtifacts"] = None
) -> List[RecognizerResult]:
    """Analyze text for PII/PHI using LLM."""
    if not text or not text.strip():
        logger.debug("Empty text provided, returning empty results")
        return []

    if entities is None:
        requested_entities = self.supported_entities
    else:
        requested_entities = [e for e in entities if e in self.supported_entities]

    if not requested_entities:
        logger.debug(
            "No requested entities (%s) match supported entities (%s)",
            entities, self.supported_entities
        )
        return []

    results = self._call_llm(text, requested_entities)

    filtered_results = self._filter_and_process_results(
        results, requested_entities
    )

    if filtered_results:
        logger.debug(
            "LLM recognizer found %d entities",
            len(filtered_results),
        )

    return filtered_results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return list of supported PII entity types.

Source code in presidio_analyzer/lm_recognizer.py
159
160
161
def get_supported_entities(self) -> List[str]:
    """Return list of supported PII entity types."""
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

LangExtractRecognizer

Bases: LMRecognizer, ABC

Base class for LangExtract-based PII recognizers.

Subclasses implement _call_langextract() for specific LLM providers.

METHOD DESCRIPTION
country_code

Return the country tag for this recognizer, or None if generic.

is_country_specific

Return True iff this recognizer is tagged with a country.

analyze

Analyze text for PII/PHI using LLM.

enhance_using_context

Enhance confidence score using context of the entity.

get_supported_entities

Return list of supported PII entity types.

get_supported_language

Return the language this recognizer can support.

get_version

Return the version of this recognizer.

to_dict

Serialize self to dictionary.

from_dict

Create EntityRecognizer from a dict input.

remove_duplicates

Remove duplicate results.

sanitize_value

Cleanse the input string of the replacement pairs specified as argument.

Source code in presidio_analyzer/predefined_recognizers/third_party/langextract_recognizer.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
class LangExtractRecognizer(LMRecognizer, ABC):
    """
    Base class for LangExtract-based PII recognizers.

    Subclasses implement _call_langextract() for specific LLM providers.
    """

    def __init__(
        self,
        config_path: str,
        name: str = "LangExtract LLM PII",
        supported_language: str = "en",
        extract_params: Optional[Dict[str, Any]] = None,
    ):
        """Initialize LangExtract recognizer.

        :param config_path: Path to configuration file.
        :param name: Name of the recognizer (provided by subclass).
        :param supported_language: Language this recognizer supports (default: "en").
        :param extract_params: Dict with 'extract' and/or 'language_model'
            keys containing param defaults.
        """
        check_langextract_available()

        full_config = load_yaml_file(config_path)

        lm_config = extract_lm_config(full_config)
        langextract_config = full_config.get("langextract", {})

        supported_entities = get_supported_entities(lm_config, langextract_config)

        if not supported_entities:
            raise ValueError(
                "Configuration must contain 'supported_entities' in "
                "'lm_recognizer' or 'langextract'"
            )

        validate_config_fields(
            full_config,
            [
                ("langextract",),
                ("langextract", "model"),
                ("langextract", "model", "model_id"),
                ("langextract", "entity_mappings"),
                ("langextract", "prompt_file"),
                ("langextract", "examples_file"),
            ]
        )

        self.config = langextract_config
        model_config = get_model_config(
            full_config, provider_key="langextract"
        )

        super().__init__(
            supported_entities=supported_entities,
            supported_language=supported_language,
            name=name,
            version="1.0.0",
            model_id=model_config["model_id"],
            temperature=model_config.get("temperature"),
            min_score=lm_config.get("min_score"),
            labels_to_ignore=lm_config.get("labels_to_ignore"),
            enable_generic_consolidation=lm_config.get(
                "enable_generic_consolidation"
            ),
        )

        examples_data = load_yaml_examples(
            langextract_config["examples_file"]
        )
        self.examples = convert_to_langextract_format(examples_data)

        prompt_template = load_prompt_file(
            langextract_config["prompt_file"]
        )
        self.prompt_description = render_jinja_template(
            prompt_template,
            supported_entities=self.supported_entities,
            enable_generic_consolidation=self.enable_generic_consolidation,
            labels_to_ignore=self.labels_to_ignore,
        )

        self.entity_mappings = langextract_config["entity_mappings"]
        self.debug = langextract_config.get("debug", False)
        self._model_config = model_config

        # Process extract params with config override
        self._extract_params = {}
        self._language_model_params = {}

        if extract_params:
            if "extract" in extract_params:
                for param_name, default_value in extract_params["extract"].items():
                    self._extract_params[param_name] = self._model_config.get(
                        param_name, default_value
                    )

            if "language_model" in extract_params:
                for param_name, default_value in (
                    extract_params["language_model"].items()
                ):
                    self._language_model_params[param_name] = (
                        self._model_config.get(param_name, default_value)
                    )

    def _call_llm(self, text: str, entities: List[str], **kwargs):
        """Call LangExtract LLM."""
        # Build extract params
        extract_params = {
            "text": text,
            "prompt": self.prompt_description,
            "examples": self.examples,
            "debug": self.debug,
        }

        # Add temperature if configured
        if self.temperature is not None:
            extract_params["temperature"] = self.temperature

        # Add any additional kwargs
        extract_params.update(kwargs)

        langextract_result = self._call_langextract(**extract_params)

        return convert_langextract_to_presidio_results(
            langextract_result=langextract_result,
            entity_mappings=self.entity_mappings,
            supported_entities=self.supported_entities,
            enable_generic_consolidation=self.enable_generic_consolidation,
            recognizer_name=self.__class__.__name__
        )

    def _call_langextract(self, **kwargs):
        """Call LangExtract with configured parameters."""
        try:
            extract_params = {
                "text_or_documents": kwargs.pop("text"),
                "prompt_description": kwargs.pop("prompt"),
                "examples": kwargs.pop("examples"),
            }

            extract_params.update(self._get_provider_params())
            extract_params.update(self._extract_params)
            if self._language_model_params:
                extract_params["language_model_params"] = self._language_model_params
            extract_params.update(kwargs)

            return lx.extract(**extract_params)
        except Exception:
            logger.exception(
                "LangExtract extraction failed (model '%s')",
                self.model_id
            )
            raise

    @abstractmethod
    def _get_provider_params(self) -> Dict[str, Any]:
        """Return provider-specific params.

        Examples: model_id, model_url, azure_endpoint, etc.
        """
        ...

country_code

country_code() -> Optional[str]

Return the country tag for this recognizer, or None if generic.

Resolved at construction time from the class-level :attr:COUNTRY_CODE attribute and the optional country_code constructor kwarg; the two are reconciled by :meth:_resolve_country_code so this method always returns a single, lower-cased value (or None). Note this is an instance method — to introspect a class without instantiating, read cls.COUNTRY_CODE directly.

Source code in presidio_analyzer/entity_recognizer.py
128
129
130
131
132
133
134
135
136
137
138
139
def country_code(self) -> Optional[str]:
    """Return the country tag for this recognizer, or ``None`` if generic.

    Resolved at construction time from the class-level
    :attr:`COUNTRY_CODE` attribute and the optional ``country_code``
    constructor kwarg; the two are reconciled by
    :meth:`_resolve_country_code` so this method always returns a
    single, lower-cased value (or ``None``). Note this is an instance
    method — to introspect a class without instantiating, read
    ``cls.COUNTRY_CODE`` directly.
    """
    return self._country_code

is_country_specific

is_country_specific() -> bool

Return True iff this recognizer is tagged with a country.

Equivalent to self.country_code() is not None. Provided as a named predicate because filter / registry / discoverability code reads more naturally with an explicit "is this country-specific?" question than with a None-check.

Source code in presidio_analyzer/entity_recognizer.py
141
142
143
144
145
146
147
148
149
def is_country_specific(self) -> bool:
    """Return ``True`` iff this recognizer is tagged with a country.

    Equivalent to ``self.country_code() is not None``. Provided as a
    named predicate because filter / registry / discoverability code
    reads more naturally with an explicit "is this country-specific?"
    question than with a None-check.
    """
    return self.country_code() is not None

id property

id

Return a unique identifier of this recognizer.

analyze

analyze(
    text: str,
    entities: Optional[List[str]] = None,
    nlp_artifacts: Optional[NlpArtifacts] = None,
) -> List[RecognizerResult]

Analyze text for PII/PHI using LLM.

Source code in presidio_analyzer/lm_recognizer.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def analyze(
    self,
    text: str,
    entities: Optional[List[str]] = None,
    nlp_artifacts: Optional["NlpArtifacts"] = None
) -> List[RecognizerResult]:
    """Analyze text for PII/PHI using LLM."""
    if not text or not text.strip():
        logger.debug("Empty text provided, returning empty results")
        return []

    if entities is None:
        requested_entities = self.supported_entities
    else:
        requested_entities = [e for e in entities if e in self.supported_entities]

    if not requested_entities:
        logger.debug(
            "No requested entities (%s) match supported entities (%s)",
            entities, self.supported_entities
        )
        return []

    results = self._call_llm(text, requested_entities)

    filtered_results = self._filter_and_process_results(
        results, requested_entities
    )

    if filtered_results:
        logger.debug(
            "LLM recognizer found %d entities",
            len(filtered_results),
        )

    return filtered_results

enhance_using_context

enhance_using_context(
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: NlpArtifacts,
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]

Enhance confidence score using context of the entity.

Override this method in derived class in case a custom logic is needed, otherwise return value will be equal to raw_results.

in case a result score is boosted, derived class need to update result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

PARAMETER DESCRIPTION
text

The actual text that was analyzed

TYPE: str

raw_recognizer_results

This recognizer's results, to be updated based on recognizer specific context.

TYPE: List[RecognizerResult]

other_raw_recognizer_results

Other recognizer results matched in the given text to allow related entity context enhancement

TYPE: List[RecognizerResult]

nlp_artifacts

The nlp artifacts contains elements such as lemmatized tokens for better accuracy of the context enhancement process

TYPE: NlpArtifacts

context

list of context words

TYPE: Optional[List[str]] DEFAULT: None

Source code in presidio_analyzer/entity_recognizer.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enhance_using_context(
    self,
    text: str,
    raw_recognizer_results: List[RecognizerResult],
    other_raw_recognizer_results: List[RecognizerResult],
    nlp_artifacts: "NlpArtifacts",
    context: Optional[List[str]] = None,
) -> List[RecognizerResult]:
    """Enhance confidence score using context of the entity.

    Override this method in derived class in case a custom logic
    is needed, otherwise return value will be equal to
    raw_results.

    in case a result score is boosted, derived class need to update
    result.recognition_metadata[RecognizerResult.IS_SCORE_ENHANCED_BY_CONTEXT_KEY]

    :param text: The actual text that was analyzed
    :param raw_recognizer_results: This recognizer's results, to be updated
    based on recognizer specific context.
    :param other_raw_recognizer_results: Other recognizer results matched in
    the given text to allow related entity context enhancement
    :param nlp_artifacts: The nlp artifacts contains elements
                          such as lemmatized tokens for better
                          accuracy of the context enhancement process
    :param context: list of context words
    """
    return raw_recognizer_results

get_supported_entities

get_supported_entities() -> List[str]

Return list of supported PII entity types.

Source code in presidio_analyzer/lm_recognizer.py
159
160
161
def get_supported_entities(self) -> List[str]:
    """Return list of supported PII entity types."""
    return self.supported_entities

get_supported_language

get_supported_language() -> str

Return the language this recognizer can support.

RETURNS DESCRIPTION
str

A list of the supported language by this recognizer

Source code in presidio_analyzer/entity_recognizer.py
217
218
219
220
221
222
223
def get_supported_language(self) -> str:
    """
    Return the language this recognizer can support.

    :return: A list of the supported language by this recognizer
    """
    return self.supported_language

get_version

get_version() -> str

Return the version of this recognizer.

RETURNS DESCRIPTION
str

The current version of this recognizer

Source code in presidio_analyzer/entity_recognizer.py
225
226
227
228
229
230
231
def get_version(self) -> str:
    """
    Return the version of this recognizer.

    :return: The current version of this recognizer
    """
    return self.version

to_dict

to_dict() -> Dict

Serialize self to dictionary.

RETURNS DESCRIPTION
Dict

a dictionary

Source code in presidio_analyzer/entity_recognizer.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def to_dict(self) -> Dict:
    """
    Serialize self to dictionary.

    :return: a dictionary
    """
    return_dict = {
        "supported_entities": self.supported_entities,
        "supported_language": self.supported_language,
        "name": self.name,
        "version": self.version,
    }
    if self._country_code is not None:
        return_dict["country_code"] = self._country_code
    return return_dict

from_dict classmethod

from_dict(entity_recognizer_dict: Dict) -> EntityRecognizer

Create EntityRecognizer from a dict input.

PARAMETER DESCRIPTION
entity_recognizer_dict

Dict containing keys and values for instantiation

TYPE: Dict

Source code in presidio_analyzer/entity_recognizer.py
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, entity_recognizer_dict: Dict) -> "EntityRecognizer":
    """
    Create EntityRecognizer from a dict input.

    :param entity_recognizer_dict: Dict containing keys and values for instantiation
    """
    return cls(**entity_recognizer_dict)

remove_duplicates staticmethod

remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]

Remove duplicate results.

Remove duplicates in case the two results have identical start and ends and types.

PARAMETER DESCRIPTION
results

List[RecognizerResult]

TYPE: List[RecognizerResult]

RETURNS DESCRIPTION
List[RecognizerResult]

List[RecognizerResult]

Source code in presidio_analyzer/entity_recognizer.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@staticmethod
def remove_duplicates(results: List[RecognizerResult]) -> List[RecognizerResult]:
    """
    Remove duplicate results.

    Remove duplicates in case the two results
    have identical start and ends and types.
    :param results: List[RecognizerResult]
    :return: List[RecognizerResult]
    """
    results = list(set(results))
    results = sorted(results, key=lambda x: (-x.score, x.start, -(x.end - x.start)))
    filtered_results = []

    for result in results:
        if result.score == 0:
            continue

        to_keep = result not in filtered_results  # equals based comparison
        if to_keep:
            for filtered in filtered_results:
                # If result is contained in one of the other results
                if (
                    result.contained_in(filtered)
                    and result.entity_type == filtered.entity_type
                ):
                    to_keep = False
                    break

        if to_keep:
            filtered_results.append(result)

    return filtered_results

sanitize_value staticmethod

sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str

Cleanse the input string of the replacement pairs specified as argument.

PARAMETER DESCRIPTION
text

input string

TYPE: str

replacement_pairs

pairs of what has to be replaced with which value

TYPE: List[Tuple[str, str]]

RETURNS DESCRIPTION
str

cleansed string

Source code in presidio_analyzer/entity_recognizer.py
292
293
294
295
296
297
298
299
300
301
302
303
@staticmethod
def sanitize_value(text: str, replacement_pairs: List[Tuple[str, str]]) -> str:
    """
    Cleanse the input string of the replacement pairs specified as argument.

    :param text: input string
    :param replacement_pairs: pairs of what has to be replaced with which value
    :return: cleansed string
    """
    for search_string, replacement_string in replacement_pairs:
        text = text.replace(search_string, replacement_string)
    return text

Misc

presidio_analyzer.analyzer_request.AnalyzerRequest

Analyzer request data.

PARAMETER DESCRIPTION
req_data

A request dictionary with the following fields: text: the text to analyze language: the language of the text entities: List of PII entities that should be looked for in the text. If entities=None then all entities are looked for. correlation_id: cross call ID for this request score_threshold: A minimum value for which to return an identified entity log_decision_process: Should the decision points within the analysis be logged return_decision_process: Should the decision points within the analysis returned as part of the response

TYPE: Dict

Source code in presidio_analyzer/analyzer_request.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class AnalyzerRequest:
    """
    Analyzer request data.

    :param req_data: A request dictionary with the following fields:
        text: the text to analyze
        language: the language of the text
        entities: List of PII entities that should be looked for in the text.
        If entities=None then all entities are looked for.
        correlation_id: cross call ID for this request
        score_threshold: A minimum value for which to return an identified entity
        log_decision_process: Should the decision points within the analysis
        be logged
        return_decision_process: Should the decision points within the analysis
        returned as part of the response
    """

    def __init__(self, req_data: Dict):
        self.text = req_data.get("text")
        self.language = req_data.get("language")
        self.entities = req_data.get("entities")
        self.correlation_id = req_data.get("correlation_id")
        self.score_threshold = req_data.get("score_threshold")
        self.return_decision_process = req_data.get("return_decision_process")
        ad_hoc_recognizers = req_data.get("ad_hoc_recognizers")
        self.ad_hoc_recognizers = []
        if ad_hoc_recognizers:
            self.ad_hoc_recognizers = [
                PatternRecognizer.from_dict(rec) for rec in ad_hoc_recognizers
            ]
        self.context = req_data.get("context")
        self.allow_list = req_data.get("allow_list")
        self.allow_list_match = req_data.get("allow_list_match", "exact")
        self.regex_flags = req_data.get(
            "regex_flags", re.DOTALL | re.MULTILINE | re.IGNORECASE
        )