Skip to content

Presidio Anonymizer API Reference

presidio_anonymizer

Anonymizer root module.

deanonymize_engine

Deanonymize anonymized text by using deanonymize operators.

DeanonymizeEngine

Bases: EngineBase

Deanonymize text that was previously anonymized.

METHOD DESCRIPTION
deanonymize

Receive the text, entities and operators to perform deanonymization over.

get_deanonymizers

Return a list of supported deanonymizers.

add_deanonymizer

Add a new deanonymizer to the engine.

remove_deanonymizer

Remove a deanonymizer from the engine.

Source code in presidio_anonymizer/deanonymize_engine.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
class DeanonymizeEngine(EngineBase):
    """Deanonymize text that was previously anonymized."""

    def deanonymize(
        self,
        text: str,
        entities: List[OperatorResult],
        operators: Dict[str, OperatorConfig],
    ) -> EngineResult:
        """
        Receive the text, entities and operators to perform deanonymization over.

        :param operators: the operators to apply on the anonymizer result entities
        :param text: the full text with the encrypted entities
        :param entities: list of encrypted entities
        :return: EngineResult - the new text and data about the deanonymized entities.
        """
        return self._operate(text, entities, operators, OperatorType.Deanonymize)

    def get_deanonymizers(self) -> List[str]:
        """Return a list of supported deanonymizers."""
        names = [p for p in self.operators_factory.get_deanonymizers().keys()]
        return names

    def add_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
        """
        Add a new deanonymizer to the engine.

        anonymizer_cls: The deanonymizer class to add to the engine.
        """
        logger.info(f"Added deanonymizer {deanonymizer_cls.__name__}")
        self.operators_factory.add_deanonymize_operator(deanonymizer_cls)

    def remove_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
        """
        Remove a deanonymizer from the engine.

        deanonymizer_cls: The deanonymizer class to remove from the engine.
        """
        logger.info(f"Removed deanonymizer {deanonymizer_cls.__name__}")
        self.operators_factory.remove_deanonymize_operator(deanonymizer_cls)
deanonymize
deanonymize(
    text: str,
    entities: List[OperatorResult],
    operators: Dict[str, OperatorConfig],
) -> EngineResult

Receive the text, entities and operators to perform deanonymization over.

PARAMETER DESCRIPTION
operators

the operators to apply on the anonymizer result entities

TYPE: Dict[str, OperatorConfig]

text

the full text with the encrypted entities

TYPE: str

entities

list of encrypted entities

TYPE: List[OperatorResult]

RETURNS DESCRIPTION
EngineResult

EngineResult - the new text and data about the deanonymized entities.

Source code in presidio_anonymizer/deanonymize_engine.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def deanonymize(
    self,
    text: str,
    entities: List[OperatorResult],
    operators: Dict[str, OperatorConfig],
) -> EngineResult:
    """
    Receive the text, entities and operators to perform deanonymization over.

    :param operators: the operators to apply on the anonymizer result entities
    :param text: the full text with the encrypted entities
    :param entities: list of encrypted entities
    :return: EngineResult - the new text and data about the deanonymized entities.
    """
    return self._operate(text, entities, operators, OperatorType.Deanonymize)
get_deanonymizers
get_deanonymizers() -> List[str]

Return a list of supported deanonymizers.

Source code in presidio_anonymizer/deanonymize_engine.py
32
33
34
35
def get_deanonymizers(self) -> List[str]:
    """Return a list of supported deanonymizers."""
    names = [p for p in self.operators_factory.get_deanonymizers().keys()]
    return names
add_deanonymizer
add_deanonymizer(deanonymizer_cls: Type[Operator]) -> None

Add a new deanonymizer to the engine.

anonymizer_cls: The deanonymizer class to add to the engine.

Source code in presidio_anonymizer/deanonymize_engine.py
37
38
39
40
41
42
43
44
def add_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
    """
    Add a new deanonymizer to the engine.

    anonymizer_cls: The deanonymizer class to add to the engine.
    """
    logger.info(f"Added deanonymizer {deanonymizer_cls.__name__}")
    self.operators_factory.add_deanonymize_operator(deanonymizer_cls)
remove_deanonymizer
remove_deanonymizer(deanonymizer_cls: Type[Operator]) -> None

Remove a deanonymizer from the engine.

deanonymizer_cls: The deanonymizer class to remove from the engine.

Source code in presidio_anonymizer/deanonymize_engine.py
46
47
48
49
50
51
52
53
def remove_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
    """
    Remove a deanonymizer from the engine.

    deanonymizer_cls: The deanonymizer class to remove from the engine.
    """
    logger.info(f"Removed deanonymizer {deanonymizer_cls.__name__}")
    self.operators_factory.remove_deanonymize_operator(deanonymizer_cls)

anonymizer_engine

Handles the entire logic of the Presidio-anonymizer and text anonymizing.

AnonymizerEngine

Bases: EngineBase

AnonymizerEngine class.

Handles the entire logic of the Presidio-anonymizer. Gets the original text and replaces the PII entities with the desired anonymizers.

METHOD DESCRIPTION
anonymize

Anonymize method to anonymize the given text.

add_anonymizer

Add a new anonymizer to the engine.

remove_anonymizer

Remove an anonymizer from the engine.

get_anonymizers

Return a list of supported anonymizers.

Source code in presidio_anonymizer/anonymizer_engine.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
242
243
244
class AnonymizerEngine(EngineBase):
    """
    AnonymizerEngine class.

    Handles the entire logic of the Presidio-anonymizer. Gets the original text
    and replaces the PII entities with the desired anonymizers.
    """

    def anonymize(
        self,
        text: str,
        analyzer_results: List[RecognizerResult],
        operators: Optional[Dict[str, OperatorConfig]] = None,
        conflict_resolution: ConflictResolutionStrategy = (
            ConflictResolutionStrategy.MERGE_SIMILAR_OR_CONTAINED
        ),
    ) -> EngineResult:
        """Anonymize method to anonymize the given text.

        :param text: the text we are anonymizing
        :param analyzer_results: A list of RecognizerResult class -> The results we
        received from the analyzer
        :param operators: The configuration of the anonymizers we would like
        to use for each entity e.g.: {"PHONE_NUMBER":OperatorConfig("redact", {})}
        received from the analyzer
        :param conflict_resolution: The configuration designed to handle conflicts
        among entities
        :return: the anonymized text and a list of information about the
        anonymized entities.

        :example:

        >>> from presidio_anonymizer import AnonymizerEngine
        >>> from presidio_anonymizer.entities import RecognizerResult, OperatorConfig

        >>> # Initialize the engine with logger.
        >>> engine = AnonymizerEngine()

        >>> # Invoke the anonymize function with the text, analyzer results and
        >>> # Operators to define the anonymization type.
        >>> result = engine.anonymize(
        >>>     text="My name is Bond, James Bond",
        >>>     analyzer_results=[RecognizerResult(entity_type="PERSON",
        >>>                                        start=11,
        >>>                                        end=15,
        >>>                                        score=0.8),
        >>>                       RecognizerResult(entity_type="PERSON",
        >>>                                        start=17,
        >>>                                        end=27,
        >>>                                        score=0.8)],
        >>>     operators={"PERSON": OperatorConfig("replace", {"new_value": "BIP"})}
        >>> )

        >>> print(result)
        text: My name is BIP, BIP.
        items:
        [
            {'start': 16, 'end': 19, 'entity_type': 'PERSON',
             'text': 'BIP', 'operator': 'replace'},
            {'start': 11, 'end': 14, 'entity_type': 'PERSON',
             'text': 'BIP', 'operator': 'replace'}
        ]


        """
        analyzer_results = self._remove_conflicts_and_get_text_manipulation_data(
            analyzer_results, conflict_resolution
        )

        merged_results = self._merge_entities_with_whitespace_between(
            text, analyzer_results
        )

        operators = self.__check_or_add_default_operator(operators)

        return self._operate(
            text=text,
            pii_entities=merged_results,
            operators_metadata=operators,
            operator_type=OperatorType.Anonymize,
        )

    def add_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
        """
        Add a new anonymizer to the engine.

        anonymizer_cls: The anonymizer class to add to the engine.
        """
        logger.info(f"Added anonymizer {anonymizer_cls.__name__}")
        self.operators_factory.add_anonymize_operator(anonymizer_cls)

    def remove_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
        """
        Remove an anonymizer from the engine.

        anonymizer_cls: The anonymizer class to remove from the engine.
        """
        logger.info(f"Removed anonymizer {anonymizer_cls.__name__}")
        self.operators_factory.remove_anonymize_operator(anonymizer_cls)

    def _remove_conflicts_and_get_text_manipulation_data(
        self,
        analyzer_results: List[RecognizerResult],
        conflict_resolution: ConflictResolutionStrategy,
    ) -> List[RecognizerResult]:
        """
        Iterate the list and create a sorted unique results list from it.

        Only insert results which are:
        1. Indices are not contained in other result.
        2. Have the same indices as other results but with larger score.
        :return: List
        """
        tmp_analyzer_results = []
        # This list contains all elements which we need to check a single result
        # against. If a result is dropped, it can also be dropped from this list
        # since it is intersecting with another result and we selected the other one.
        other_elements = analyzer_results.copy()
        for result in analyzer_results:
            other_elements.remove(result)

            is_merge_same_entity_type = False
            for other_element in other_elements:
                if other_element.entity_type != result.entity_type:
                    continue
                if result.intersects(other_element) == 0:
                    continue

                other_element.start = min(result.start, other_element.start)
                other_element.end = max(result.end, other_element.end)
                other_element.score = max(result.score, other_element.score)
                is_merge_same_entity_type = True
                break
            if not is_merge_same_entity_type:
                other_elements.append(result)
                tmp_analyzer_results.append(result)
            else:
                self.logger.debug(
                    f"removing element {result} from " f"results list due to merge"
                )

        unique_text_metadata_elements = []
        # This list contains all elements which we need to check a single result
        # against. If a result is dropped, it can also be dropped from this list
        # since it is intersecting with another result and we selected the other one.
        other_elements = tmp_analyzer_results.copy()
        for result in tmp_analyzer_results:
            other_elements.remove(result)
            result_conflicted = self.__is_result_conflicted_with_other_elements(
                other_elements, result
            )
            if not result_conflicted:
                other_elements.append(result)
                unique_text_metadata_elements.append(result)
            else:
                self.logger.debug(
                    f"removing element {result} from results list due to conflict"
                )

        # This further improves the quality of handling the conflict between the
        # various entities overlapping. This will not drop the results insted
        # it adjust the start and end positions of overlapping results and removes
        # All types of conflicts among entities as well as text.
        if conflict_resolution == ConflictResolutionStrategy.REMOVE_INTERSECTIONS:
            unique_text_metadata_elements.sort(key=lambda element: element.start)
            elements_length = len(unique_text_metadata_elements)
            index = 0
            while index < elements_length - 1:
                current_entity = unique_text_metadata_elements[index]
                next_entity = unique_text_metadata_elements[index + 1]
                if current_entity.end <= next_entity.start:
                    index += 1
                else:
                    if current_entity.score >= next_entity.score:
                        next_entity.start = current_entity.end
                    else:
                        current_entity.end = next_entity.start
                    unique_text_metadata_elements.sort(
                        key=lambda element: element.start
                    )
            unique_text_metadata_elements = [
                element
                for element in unique_text_metadata_elements
                if element.start <= element.end
            ]
        return unique_text_metadata_elements

    def _merge_entities_with_whitespace_between(
        self, text: str, analyzer_results: List[RecognizerResult]
    ) -> List[RecognizerResult]:
        """Merge adjacent entities of the same type separated by whitespace."""
        merged_results = []
        prev_result = None
        for result in analyzer_results:
            if prev_result is not None:
                if prev_result.entity_type == result.entity_type:
                    if re.search(r"^( )+$", text[prev_result.end : result.start]):
                        merged_results.remove(prev_result)
                        result.start = prev_result.start
            merged_results.append(result)
            prev_result = result
        return merged_results

    def get_anonymizers(self) -> List[str]:
        """Return a list of supported anonymizers."""
        names = [p for p in self.operators_factory.get_anonymizers().keys()]
        return names

    @staticmethod
    def __is_result_conflicted_with_other_elements(other_elements, result):
        return any(
            [result.has_conflict(other_element) for other_element in other_elements]
        )

    @staticmethod
    def __check_or_add_default_operator(
        operators: Dict[str, OperatorConfig],
    ) -> Dict[str, OperatorConfig]:
        default_operator = OperatorConfig(DEFAULT)
        if not operators:
            return {"DEFAULT": default_operator}
        if not operators.get("DEFAULT"):
            operators["DEFAULT"] = default_operator
        return operators
anonymize
anonymize(
    text: str,
    analyzer_results: List[RecognizerResult],
    operators: Optional[Dict[str, OperatorConfig]] = None,
    conflict_resolution: ConflictResolutionStrategy = ConflictResolutionStrategy.MERGE_SIMILAR_OR_CONTAINED,
) -> EngineResult

Anonymize method to anonymize the given text.

:example:

from presidio_anonymizer import AnonymizerEngine from presidio_anonymizer.entities import RecognizerResult, OperatorConfig

Initialize the engine with logger.

engine = AnonymizerEngine()

Invoke the anonymize function with the text, analyzer results and
Operators to define the anonymization type.

result = engine.anonymize( text="My name is Bond, James Bond", analyzer_results=[RecognizerResult(entity_type="PERSON", start=11, end=15, score=0.8), RecognizerResult(entity_type="PERSON", start=17, end=27, score=0.8)], operators={"PERSON": OperatorConfig("replace", {"new_value": "BIP"})} )

print(result) text: My name is BIP, BIP. items: [ {'start': 16, 'end': 19, 'entity_type': 'PERSON', 'text': 'BIP', 'operator': 'replace'}, {'start': 11, 'end': 14, 'entity_type': 'PERSON', 'text': 'BIP', 'operator': 'replace'} ]

PARAMETER DESCRIPTION
text

the text we are anonymizing

TYPE: str

analyzer_results

A list of RecognizerResult class -> The results we received from the analyzer

TYPE: List[RecognizerResult]

operators

The configuration of the anonymizers we would like to use for each entity e.g.: {"PHONE_NUMBER":OperatorConfig("redact", {})} received from the analyzer

TYPE: Optional[Dict[str, OperatorConfig]] DEFAULT: None

conflict_resolution

The configuration designed to handle conflicts among entities

TYPE: ConflictResolutionStrategy DEFAULT: MERGE_SIMILAR_OR_CONTAINED

RETURNS DESCRIPTION
EngineResult

the anonymized text and a list of information about the anonymized entities.

Source code in presidio_anonymizer/anonymizer_engine.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
def anonymize(
    self,
    text: str,
    analyzer_results: List[RecognizerResult],
    operators: Optional[Dict[str, OperatorConfig]] = None,
    conflict_resolution: ConflictResolutionStrategy = (
        ConflictResolutionStrategy.MERGE_SIMILAR_OR_CONTAINED
    ),
) -> EngineResult:
    """Anonymize method to anonymize the given text.

    :param text: the text we are anonymizing
    :param analyzer_results: A list of RecognizerResult class -> The results we
    received from the analyzer
    :param operators: The configuration of the anonymizers we would like
    to use for each entity e.g.: {"PHONE_NUMBER":OperatorConfig("redact", {})}
    received from the analyzer
    :param conflict_resolution: The configuration designed to handle conflicts
    among entities
    :return: the anonymized text and a list of information about the
    anonymized entities.

    :example:

    >>> from presidio_anonymizer import AnonymizerEngine
    >>> from presidio_anonymizer.entities import RecognizerResult, OperatorConfig

    >>> # Initialize the engine with logger.
    >>> engine = AnonymizerEngine()

    >>> # Invoke the anonymize function with the text, analyzer results and
    >>> # Operators to define the anonymization type.
    >>> result = engine.anonymize(
    >>>     text="My name is Bond, James Bond",
    >>>     analyzer_results=[RecognizerResult(entity_type="PERSON",
    >>>                                        start=11,
    >>>                                        end=15,
    >>>                                        score=0.8),
    >>>                       RecognizerResult(entity_type="PERSON",
    >>>                                        start=17,
    >>>                                        end=27,
    >>>                                        score=0.8)],
    >>>     operators={"PERSON": OperatorConfig("replace", {"new_value": "BIP"})}
    >>> )

    >>> print(result)
    text: My name is BIP, BIP.
    items:
    [
        {'start': 16, 'end': 19, 'entity_type': 'PERSON',
         'text': 'BIP', 'operator': 'replace'},
        {'start': 11, 'end': 14, 'entity_type': 'PERSON',
         'text': 'BIP', 'operator': 'replace'}
    ]


    """
    analyzer_results = self._remove_conflicts_and_get_text_manipulation_data(
        analyzer_results, conflict_resolution
    )

    merged_results = self._merge_entities_with_whitespace_between(
        text, analyzer_results
    )

    operators = self.__check_or_add_default_operator(operators)

    return self._operate(
        text=text,
        pii_entities=merged_results,
        operators_metadata=operators,
        operator_type=OperatorType.Anonymize,
    )
add_anonymizer
add_anonymizer(anonymizer_cls: Type[Operator]) -> None

Add a new anonymizer to the engine.

anonymizer_cls: The anonymizer class to add to the engine.

Source code in presidio_anonymizer/anonymizer_engine.py
103
104
105
106
107
108
109
110
def add_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
    """
    Add a new anonymizer to the engine.

    anonymizer_cls: The anonymizer class to add to the engine.
    """
    logger.info(f"Added anonymizer {anonymizer_cls.__name__}")
    self.operators_factory.add_anonymize_operator(anonymizer_cls)
remove_anonymizer
remove_anonymizer(anonymizer_cls: Type[Operator]) -> None

Remove an anonymizer from the engine.

anonymizer_cls: The anonymizer class to remove from the engine.

Source code in presidio_anonymizer/anonymizer_engine.py
112
113
114
115
116
117
118
119
def remove_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
    """
    Remove an anonymizer from the engine.

    anonymizer_cls: The anonymizer class to remove from the engine.
    """
    logger.info(f"Removed anonymizer {anonymizer_cls.__name__}")
    self.operators_factory.remove_anonymize_operator(anonymizer_cls)
get_anonymizers
get_anonymizers() -> List[str]

Return a list of supported anonymizers.

Source code in presidio_anonymizer/anonymizer_engine.py
224
225
226
227
def get_anonymizers(self) -> List[str]:
    """Return a list of supported anonymizers."""
    names = [p for p in self.operators_factory.get_anonymizers().keys()]
    return names

batch_anonymizer_engine

BatchAnonymizerEngine

BatchAnonymizerEngine class.

A class that provides functionality to anonymize in batches.

PARAMETER DESCRIPTION
anonymizer_engine

An instance of the AnonymizerEngine class.

TYPE: Optional[AnonymizerEngine] DEFAULT: None

METHOD DESCRIPTION
anonymize_list

Anonymize a list of strings.

anonymize_dict

Anonymize values in a dictionary.

Source code in presidio_anonymizer/batch_anonymizer_engine.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
class BatchAnonymizerEngine:
    """
    BatchAnonymizerEngine class.

    A class that provides functionality to anonymize in batches.
    :param anonymizer_engine: An instance of the AnonymizerEngine class.
    """

    def __init__(self, anonymizer_engine: Optional[AnonymizerEngine] = None):
        self.anonymizer_engine = anonymizer_engine or AnonymizerEngine()

    def anonymize_list(
        self,
        texts: List[Optional[Union[str, bool, int, float]]],
        recognizer_results_list: List[List[RecognizerResult]],
        **kwargs,
    ) -> List[Union[str, Any]]:
        """
        Anonymize a list of strings.

        :param texts: List containing the texts to be anonymized (original texts).
            Items with a `type` not in `(str, bool, int, float)` will not be anonymized.
        :param recognizer_results_list: A list of lists of RecognizerResult,
        the output of the AnalyzerEngine on each text in the list.
        :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
        """
        return_list = []
        if not recognizer_results_list:
            recognizer_results_list = [[] for _ in range(len(texts))]
        for text, recognizer_results in zip(texts, recognizer_results_list):
            if type(text) in (str, bool, int, float):
                res = self.anonymizer_engine.anonymize(
                    text=str(text), analyzer_results=recognizer_results, **kwargs
                )
                return_list.append(res.text)
            else:
                return_list.append(text)

        return return_list

    def anonymize_dict(
        self, analyzer_results: Iterable[DictRecognizerResult], **kwargs
    ) -> Dict[str, str]:
        """
        Anonymize values in a dictionary.

        :param analyzer_results: Iterator of `DictRecognizerResult`
        containing the output of the AnalyzerEngine.analyze_dict on the input text.
        :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
        """

        return_dict = {}
        for result in analyzer_results:
            if isinstance(result.value, dict):
                resp = self.anonymize_dict(
                    analyzer_results=result.recognizer_results, **kwargs
                )
                return_dict[result.key] = resp

            elif isinstance(result.value, str):
                resp = self.anonymizer_engine.anonymize(
                    text=result.value,
                    analyzer_results=result.recognizer_results,
                    **kwargs,
                )
                return_dict[result.key] = resp.text

            elif isinstance(result.value, collections.abc.Iterable):
                anonymize_response = self.anonymize_list(
                    texts=result.value,
                    recognizer_results_list=result.recognizer_results,
                    **kwargs,
                )
                return_dict[result.key] = anonymize_response
            else:
                return_dict[result.key] = result.value
        return return_dict
anonymize_list
anonymize_list(
    texts: List[Optional[Union[str, bool, int, float]]],
    recognizer_results_list: List[List[RecognizerResult]],
    **kwargs
) -> List[Union[str, Any]]

Anonymize a list of strings.

PARAMETER DESCRIPTION
texts

List containing the texts to be anonymized (original texts). Items with a type not in (str, bool, int, float) will not be anonymized.

TYPE: List[Optional[Union[str, bool, int, float]]]

recognizer_results_list

A list of lists of RecognizerResult, the output of the AnalyzerEngine on each text in the list.

TYPE: List[List[RecognizerResult]]

kwargs

Additional kwargs for the AnonymizerEngine.anonymize method

DEFAULT: {}

Source code in presidio_anonymizer/batch_anonymizer_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
def anonymize_list(
    self,
    texts: List[Optional[Union[str, bool, int, float]]],
    recognizer_results_list: List[List[RecognizerResult]],
    **kwargs,
) -> List[Union[str, Any]]:
    """
    Anonymize a list of strings.

    :param texts: List containing the texts to be anonymized (original texts).
        Items with a `type` not in `(str, bool, int, float)` will not be anonymized.
    :param recognizer_results_list: A list of lists of RecognizerResult,
    the output of the AnalyzerEngine on each text in the list.
    :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
    """
    return_list = []
    if not recognizer_results_list:
        recognizer_results_list = [[] for _ in range(len(texts))]
    for text, recognizer_results in zip(texts, recognizer_results_list):
        if type(text) in (str, bool, int, float):
            res = self.anonymizer_engine.anonymize(
                text=str(text), analyzer_results=recognizer_results, **kwargs
            )
            return_list.append(res.text)
        else:
            return_list.append(text)

    return return_list
anonymize_dict
anonymize_dict(
    analyzer_results: Iterable[DictRecognizerResult], **kwargs
) -> Dict[str, str]

Anonymize values in a dictionary.

PARAMETER DESCRIPTION
analyzer_results

Iterator of DictRecognizerResult containing the output of the AnalyzerEngine.analyze_dict on the input text.

TYPE: Iterable[DictRecognizerResult]

kwargs

Additional kwargs for the AnonymizerEngine.anonymize method

DEFAULT: {}

Source code in presidio_anonymizer/batch_anonymizer_engine.py
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
def anonymize_dict(
    self, analyzer_results: Iterable[DictRecognizerResult], **kwargs
) -> Dict[str, str]:
    """
    Anonymize values in a dictionary.

    :param analyzer_results: Iterator of `DictRecognizerResult`
    containing the output of the AnalyzerEngine.analyze_dict on the input text.
    :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
    """

    return_dict = {}
    for result in analyzer_results:
        if isinstance(result.value, dict):
            resp = self.anonymize_dict(
                analyzer_results=result.recognizer_results, **kwargs
            )
            return_dict[result.key] = resp

        elif isinstance(result.value, str):
            resp = self.anonymizer_engine.anonymize(
                text=result.value,
                analyzer_results=result.recognizer_results,
                **kwargs,
            )
            return_dict[result.key] = resp.text

        elif isinstance(result.value, collections.abc.Iterable):
            anonymize_response = self.anonymize_list(
                texts=result.value,
                recognizer_results_list=result.recognizer_results,
                **kwargs,
            )
            return_dict[result.key] = anonymize_response
        else:
            return_dict[result.key] = result.value
    return return_dict

services

Services init.

validators

Anomnymizers validations utility methods.

FUNCTION DESCRIPTION
validate_parameter_in_range

Validate an anonymizer parameter.

validate_parameter_not_empty

Validate parameter exists and not only empty.

validate_parameter_exists

Validate parameter is not empty.

validate_parameter

Validate an anonymizer parameter.

validate_type

Validate an anonymizer parameter.

validate_parameter_in_range
validate_parameter_in_range(
    values_range, parameter_value, parameter_name: str, parameter_type: type
) -> None

Validate an anonymizer parameter.

validates the existence of an anonymizer parameter and that it is an instance of the parameter_type and that it is within the range of provided values. Otherwise, raise the appropriate InvalidParamException with the parameter_name as content.

Source code in presidio_anonymizer/services/validators.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def validate_parameter_in_range(
    values_range, parameter_value, parameter_name: str, parameter_type: type
) -> None:
    """Validate an anonymizer parameter.

    validates the existence of an anonymizer parameter and that it is an
    instance of the parameter_type and that it is within the range of provided values.
    Otherwise, raise the appropriate InvalidParamException with the
    parameter_name as content.
    """
    validate_parameter(parameter_value, parameter_name, object)
    if parameter_value not in values_range:
        raise InvalidParamError(
            f"Parameter {parameter_name} value {parameter_value} is not in "
            f"range of values {values_range}"
        )
validate_parameter_not_empty
validate_parameter_not_empty(
    parameter_value, entity: str, parameter_name: str
) -> None

Validate parameter exists and not only empty.

Source code in presidio_anonymizer/services/validators.py
24
25
26
27
28
29
30
31
def validate_parameter_not_empty(
    parameter_value, entity: str, parameter_name: str
) -> None:
    """Validate parameter exists and not only empty."""
    if not parameter_value:
        raise InvalidParamError(
            f"Invalid input, {entity} must contain {parameter_name}"
        )
validate_parameter_exists
validate_parameter_exists(
    parameter_value, entity: str, parameter_name: str
) -> None

Validate parameter is not empty.

Source code in presidio_anonymizer/services/validators.py
34
35
36
37
38
39
40
41
def validate_parameter_exists(
    parameter_value, entity: str, parameter_name: str
) -> None:
    """Validate parameter is not empty."""
    if parameter_value is None:
        raise InvalidParamError(
            f"Invalid input, {entity} must contain {parameter_name}"
        )
validate_parameter
validate_parameter(
    parameter_value, parameter_name: str, parameter_type: type
) -> None

Validate an anonymizer parameter.

Both validate the existence of an anonymizer parameter and that it is an instance of the parameter_type. Otherwise, raise the appropriate InvalidParamException with the parameter_name as content.

Source code in presidio_anonymizer/services/validators.py
44
45
46
47
48
49
50
51
52
53
54
55
def validate_parameter(
    parameter_value, parameter_name: str, parameter_type: type
) -> None:
    """Validate an anonymizer parameter.

    Both validate the existence of an anonymizer parameter and that it is an
    instance of the parameter_type. Otherwise, raise the appropriate
    InvalidParamException with the parameter_name as content.
    """
    if parameter_value is None:
        raise InvalidParamError(f"Expected parameter {parameter_name}")
    validate_type(parameter_value, parameter_name, parameter_type)
validate_type
validate_type(parameter_value, parameter_name, parameter_type)

Validate an anonymizer parameter.

Validate it exists and if so, that it is the instance of the parameter_type. Otherwise, raise the appropriate InvalidParamException with the parameter_name as content.

Source code in presidio_anonymizer/services/validators.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def validate_type(parameter_value, parameter_name, parameter_type):
    """
    Validate an anonymizer parameter.

    Validate it exists and if so, that it is the instance of the parameter_type.
    Otherwise, raise the appropriate InvalidParamException with the parameter_name
    as content.
    """
    if parameter_value and not isinstance(parameter_value, parameter_type):
        message = _get_bad_typed_parameter_error_message(
            parameter_name,
            expected_type=parameter_type,
            actual_type=type(parameter_value),
        )
        raise InvalidParamError(message)

app_entities_convertor

AppEntitiesConvertor

Assisting class to convert API json entities to engine entities.

METHOD DESCRIPTION
analyzer_results_from_json

Go over analyzer results, validate them and convert to List[RecognizerResult].

operators_config_from_json

Go over the operators list and get the relevant create operator config entity.

deanonymize_entities_from_json

Create DecryptEntity list.

check_custom_operator

Check if an operator is of type custom.

Source code in presidio_anonymizer/services/app_entities_convertor.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
class AppEntitiesConvertor:
    """Assisting class to convert API json entities to engine entities."""

    @staticmethod
    def analyzer_results_from_json(data: List[Dict]) -> List["RecognizerResult"]:
        """
        Go over analyzer results, validate them and convert to List[RecognizerResult].

        :param data: contains the anonymizers and analyzer_results_json
        """
        if data is None:
            raise InvalidParamError(
                "Invalid input, " "request must contain analyzer results"
            )
        return [RecognizerResult.from_json(analyzer_result) for analyzer_result in data]

    @staticmethod
    def operators_config_from_json(data: Dict) -> Dict[str, "OperatorConfig"]:
        """
        Go over the operators list and get the relevant create operator config entity.

        :param data: contains the list of configuration
        value - OperatorConfig
        """
        if data is not None:
            return {
                key: OperatorConfig.from_json(operator_json)
                for (key, operator_json) in data.items()
            }
        return {}

    @staticmethod
    def deanonymize_entities_from_json(json: Dict) -> List["OperatorResult"]:
        """
        Create DecryptEntity list.

        :param json:
        {
            "text": text,
            "encrypt_results": [{
                "start": 0,
                "end": 10,
                "key": "1111111111111111",
                "entity_type":"PHONE_NUMBER"
            }],
        }
        :return: List[OperatorResult]
        """
        decrypt_entity = json.get("anonymizer_results")
        return (
            [OperatorResult.from_json(result) for result in decrypt_entity]
            if decrypt_entity
            else []
        )

    @staticmethod
    def check_custom_operator(operators: Dict[str, OperatorConfig]):
        """Check if an operator is of type custom."""
        return any([config.operator_name == "custom" for config in operators.values()])
analyzer_results_from_json staticmethod
analyzer_results_from_json(data: List[Dict]) -> List[RecognizerResult]

Go over analyzer results, validate them and convert to List[RecognizerResult].

PARAMETER DESCRIPTION
data

contains the anonymizers and analyzer_results_json

TYPE: List[Dict]

Source code in presidio_anonymizer/services/app_entities_convertor.py
14
15
16
17
18
19
20
21
22
23
24
25
@staticmethod
def analyzer_results_from_json(data: List[Dict]) -> List["RecognizerResult"]:
    """
    Go over analyzer results, validate them and convert to List[RecognizerResult].

    :param data: contains the anonymizers and analyzer_results_json
    """
    if data is None:
        raise InvalidParamError(
            "Invalid input, " "request must contain analyzer results"
        )
    return [RecognizerResult.from_json(analyzer_result) for analyzer_result in data]
operators_config_from_json staticmethod
operators_config_from_json(data: Dict) -> Dict[str, OperatorConfig]

Go over the operators list and get the relevant create operator config entity.

PARAMETER DESCRIPTION
data

contains the list of configuration value - OperatorConfig

TYPE: Dict

Source code in presidio_anonymizer/services/app_entities_convertor.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@staticmethod
def operators_config_from_json(data: Dict) -> Dict[str, "OperatorConfig"]:
    """
    Go over the operators list and get the relevant create operator config entity.

    :param data: contains the list of configuration
    value - OperatorConfig
    """
    if data is not None:
        return {
            key: OperatorConfig.from_json(operator_json)
            for (key, operator_json) in data.items()
        }
    return {}
deanonymize_entities_from_json staticmethod
deanonymize_entities_from_json(json: Dict) -> List[OperatorResult]

Create DecryptEntity list.

PARAMETER DESCRIPTION
json

{ "text": text, "encrypt_results": [{ "start": 0, "end": 10, "key": "1111111111111111", "entity_type":"PHONE_NUMBER" }], }

TYPE: Dict

RETURNS DESCRIPTION
List[OperatorResult]

List[OperatorResult]

Source code in presidio_anonymizer/services/app_entities_convertor.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@staticmethod
def deanonymize_entities_from_json(json: Dict) -> List["OperatorResult"]:
    """
    Create DecryptEntity list.

    :param json:
    {
        "text": text,
        "encrypt_results": [{
            "start": 0,
            "end": 10,
            "key": "1111111111111111",
            "entity_type":"PHONE_NUMBER"
        }],
    }
    :return: List[OperatorResult]
    """
    decrypt_entity = json.get("anonymizer_results")
    return (
        [OperatorResult.from_json(result) for result in decrypt_entity]
        if decrypt_entity
        else []
    )
check_custom_operator staticmethod
check_custom_operator(operators: Dict[str, OperatorConfig])

Check if an operator is of type custom.

Source code in presidio_anonymizer/services/app_entities_convertor.py
66
67
68
69
@staticmethod
def check_custom_operator(operators: Dict[str, OperatorConfig]):
    """Check if an operator is of type custom."""
    return any([config.operator_name == "custom" for config in operators.values()])

operators

Initializing all the existing anonymizers.

decrypt

Decrypt

Bases: Operator

Decrypt text to from its encrypted form.

METHOD DESCRIPTION
operate

Decrypt the text.

validate

Validate Decrypt parameters.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/decrypt.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
class Decrypt(Operator):
    """Decrypt text to from its encrypted form."""

    NAME = "decrypt"
    KEY = "key"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Decrypt the text.

        :param text: The text for decryption.
        :param params:
            **key* The key supplied by the user for the encryption (bytes or str).
        :return: The encrypted text
        """
        key = params.get(self.KEY)
        if isinstance(key, str):
            key = key.encode("utf8")
        decrypted_text = AESCipher.decrypt(key=key, text=text)
        return decrypted_text

    def validate(self, params: Dict = None) -> None:
        """
        Validate Decrypt parameters.

        :param params:
            * *key* The key supplied by the user for the encryption.
                    Should be a string of 128, 192 or 256 bits length.
        :raises InvalidParamException: in case on an invalid parameter.
        """
        Encrypt().validate(params)

    def operator_name(self) -> str:
        """Return operator name."""
        return self.NAME

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Deanonymize
operate
operate(text: str = None, params: Dict = None) -> str

Decrypt the text.

PARAMETER DESCRIPTION
text

The text for decryption.

TYPE: str DEFAULT: None

params

*key The key supplied by the user for the encryption (bytes or str).

TYPE: Dict DEFAULT: None

RETURNS DESCRIPTION
str

The encrypted text

Source code in presidio_anonymizer/operators/decrypt.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Decrypt the text.

    :param text: The text for decryption.
    :param params:
        **key* The key supplied by the user for the encryption (bytes or str).
    :return: The encrypted text
    """
    key = params.get(self.KEY)
    if isinstance(key, str):
        key = key.encode("utf8")
    decrypted_text = AESCipher.decrypt(key=key, text=text)
    return decrypted_text
validate
validate(params: Dict = None) -> None

Validate Decrypt parameters.

PARAMETER DESCRIPTION
params
  • key The key supplied by the user for the encryption. Should be a string of 128, 192 or 256 bits length.

TYPE: Dict DEFAULT: None

RAISES DESCRIPTION
InvalidParamException

in case on an invalid parameter.

Source code in presidio_anonymizer/operators/decrypt.py
28
29
30
31
32
33
34
35
36
37
def validate(self, params: Dict = None) -> None:
    """
    Validate Decrypt parameters.

    :param params:
        * *key* The key supplied by the user for the encryption.
                Should be a string of 128, 192 or 256 bits length.
    :raises InvalidParamException: in case on an invalid parameter.
    """
    Encrypt().validate(params)
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/decrypt.py
39
40
41
def operator_name(self) -> str:
    """Return operator name."""
    return self.NAME
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/decrypt.py
43
44
45
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Deanonymize

custom

Replaces the PII text with function result.

Custom

Bases: Operator

Replace PII text entity with the results of a function executed on the PII text.

The function retrun type must be a string

METHOD DESCRIPTION
operate

:return: result of function executed on the text.

validate

Validate the provided function is returning a string.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/custom.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
class Custom(Operator):
    """
    Replace PII text entity with the results of a function executed on the PII text.

    The function retrun type must be a string
    """

    LAMBDA = "lambda"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """:return: result of function executed on the text."""
        new_val = params.get(self.LAMBDA)
        return new_val(text)

    def validate(self, params: Dict) -> None:
        """Validate the provided function is returning a string."""
        new_val = params.get(self.LAMBDA)
        if callable(new_val):
            if not isinstance(new_val("PII"), str):
                raise InvalidParamError("Function return type must be a str")

        else:
            raise InvalidParamError("New value must be a callable function")

    def operator_name(self) -> str:
        """Return operator name."""
        return "custom"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

result of function executed on the text.

Source code in presidio_anonymizer/operators/custom.py
18
19
20
21
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: result of function executed on the text."""
    new_val = params.get(self.LAMBDA)
    return new_val(text)
validate
validate(params: Dict) -> None

Validate the provided function is returning a string.

Source code in presidio_anonymizer/operators/custom.py
23
24
25
26
27
28
29
30
31
def validate(self, params: Dict) -> None:
    """Validate the provided function is returning a string."""
    new_val = params.get(self.LAMBDA)
    if callable(new_val):
        if not isinstance(new_val("PII"), str):
            raise InvalidParamError("Function return type must be a str")

    else:
        raise InvalidParamError("New value must be a callable function")
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/custom.py
33
34
35
def operator_name(self) -> str:
    """Return operator name."""
    return "custom"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/custom.py
37
38
39
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

redact

Replaces the PII text entity with empty string.

Redact

Bases: Operator

Redact the string - empty value.

METHOD DESCRIPTION
operate

:return: an empty value.

validate

Redact does not require any paramters so no validation is needed.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/redact.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Redact(Operator):
    """Redact the string - empty value."""

    def operate(self, text: str = None, params: Dict = None) -> str:
        """:return: an empty value."""
        return ""

    def validate(self, params: Dict = None) -> None:
        """Redact does not require any paramters so no validation is needed."""
        pass

    def operator_name(self) -> str:
        """Return operator name."""
        return "redact"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

an empty value.

Source code in presidio_anonymizer/operators/redact.py
11
12
13
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: an empty value."""
    return ""
validate
validate(params: Dict = None) -> None

Redact does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/redact.py
15
16
17
def validate(self, params: Dict = None) -> None:
    """Redact does not require any paramters so no validation is needed."""
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/redact.py
19
20
21
def operator_name(self) -> str:
    """Return operator name."""
    return "redact"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/redact.py
23
24
25
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

deanonymize_keep

DeanonymizeKeep

Bases: BaseKeep

No-op deanonymizer that keeps the PII text unmodified.

This is useful when you don't want to anonymize some types of PII, but wants to keep track of it with the other PIIs.

METHOD DESCRIPTION
operate

:return: original text.

validate

Keep does not require any paramters so no validation is needed.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/deanonymize_keep.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class DeanonymizeKeep(BaseKeep):
    """No-op deanonymizer that keeps the PII text unmodified.

    This is useful when you don't want to anonymize some types of PII,
    but wants to keep track of it with the other PIIs.
    """

    def operator_name(self) -> str:
        """Return operator name."""
        return "deanonymize_keep"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Deanonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

original text.

Source code in presidio_anonymizer/operators/keep.py
16
17
18
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: original text."""
    return text
validate
validate(params: Dict = None) -> None

Keep does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/keep.py
20
21
22
def validate(self, params: Dict = None) -> None:
    """Keep does not require any paramters so no validation is needed."""
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/deanonymize_keep.py
12
13
14
def operator_name(self) -> str:
    """Return operator name."""
    return "deanonymize_keep"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/deanonymize_keep.py
16
17
18
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Deanonymize

hash

Hashes the PII text entity.

Hash

Bases: Operator

Hash given text with sha256/sha512/md5 algorithm.

METHOD DESCRIPTION
operate

Hash given value using sha256.

validate

Validate the hash type is string and in range of allowed hash types.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/hash.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
class Hash(Operator):
    """Hash given text with sha256/sha512/md5 algorithm."""

    HASH_TYPE = "hash_type"
    SHA256 = "sha256"
    SHA512 = "sha512"
    MD5 = "md5"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Hash given value using sha256.

        :return: hashed original text
        """
        hash_type = self._get_hash_type_or_default(params)
        hash_switcher = {
            self.SHA256: lambda s: sha256(s),
            self.SHA512: lambda s: sha512(s),
            self.MD5: lambda s: md5(s),
        }
        return hash_switcher.get(hash_type)(text.encode()).hexdigest()

    def validate(self, params: Dict = None) -> None:
        """Validate the hash type is string and in range of allowed hash types."""
        validate_parameter_in_range(
            [self.SHA256, self.SHA512, self.MD5],
            self._get_hash_type_or_default(params),
            self.HASH_TYPE,
            str,
        )
        pass

    def operator_name(self) -> str:
        """Return operator name."""
        return "hash"

    def _get_hash_type_or_default(self, params: Dict = None):
        return params.get(self.HASH_TYPE, self.SHA256)

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str

Hash given value using sha256.

RETURNS DESCRIPTION
str

hashed original text

Source code in presidio_anonymizer/operators/hash.py
18
19
20
21
22
23
24
25
26
27
28
29
30
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Hash given value using sha256.

    :return: hashed original text
    """
    hash_type = self._get_hash_type_or_default(params)
    hash_switcher = {
        self.SHA256: lambda s: sha256(s),
        self.SHA512: lambda s: sha512(s),
        self.MD5: lambda s: md5(s),
    }
    return hash_switcher.get(hash_type)(text.encode()).hexdigest()
validate
validate(params: Dict = None) -> None

Validate the hash type is string and in range of allowed hash types.

Source code in presidio_anonymizer/operators/hash.py
32
33
34
35
36
37
38
39
40
def validate(self, params: Dict = None) -> None:
    """Validate the hash type is string and in range of allowed hash types."""
    validate_parameter_in_range(
        [self.SHA256, self.SHA512, self.MD5],
        self._get_hash_type_or_default(params),
        self.HASH_TYPE,
        str,
    )
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/hash.py
42
43
44
def operator_name(self) -> str:
    """Return operator name."""
    return "hash"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/hash.py
49
50
51
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

operators_factory

OperatorsFactory

Operators factory to get the correct operator class.

METHOD DESCRIPTION
add_anonymize_operator

Add a new anonymizer to the factory.

add_deanonymize_operator

Add a new deanonymizer to the factory.

remove_anonymize_operator

Remove an anonymizer from the factory.

remove_deanonymize_operator

Remove a deanonymizer from the factory.

create_operator_class

Extract the operator class from the operators list.

get_anonymizers

Return all anonymizers classes currently available.

get_deanonymizers

Return all deanonymizers classes currently available.

Source code in presidio_anonymizer/operators/operators_factory.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
class OperatorsFactory:
    """Operators factory to get the correct operator class."""

    def __init__(
        self,
    ):
        self._anonymizers = self.__load_predefined(OperatorType.Anonymize)
        self._deanonymizers = self.__load_predefined(OperatorType.Deanonymize)

    @staticmethod
    def __load_predefined(operator_type: OperatorType) -> Dict[str, Type[Operator]]:
        """
        Return all predefined operators.

        Return a dict with {name: operator class} for each operator.
        """

        operators = (
            ANONYMIZERS if operator_type == OperatorType.Anonymize else DEANONYMIZERS
        )
        operators_dict = {
            operator().operator_name(): operator for operator in operators
        }

        return operators_dict

    @staticmethod
    def __load_predefined_deanonymizers() -> Dict[str, Type[Operator]]:
        """
        Return all predefined deanonymizers.

        Return a dict with name, deanonymizer class per each deanonymizer.
        """

        deanonymizers = [Decrypt, DeanonymizeKeep]

        deanonymizers_dict = {
            deanonymizer().operator_name(): deanonymizer
            for deanonymizer in deanonymizers
        }

        return deanonymizers_dict

    def add_anonymize_operator(self, operator: Type[Operator]):
        """Add a new anonymizer to the factory.

        :param operator: The operator class to add.
        """
        self._anonymizers[operator().operator_name()] = operator

    def add_deanonymize_operator(self, operator: Type[Operator]):
        """Add a new deanonymizer to the factory.

        :param operator: The operator class to add.
        """
        self._deanonymizers[operator().operator_name()] = operator

    def remove_anonymize_operator(self, operator: Type[Operator]):
        """Remove an anonymizer from the factory.

        :param operator: The operator class to remove.
        """
        if operator().operator_name() not in self._anonymizers:
            logger.error(
                f"Operator {operator().operator_name()} not found in anonymizers list"
            )
            raise InvalidParamError(
                f"Operator {operator().operator_name()} not found in anonymizers list"
            )
        self._anonymizers.pop(operator().operator_name(), None)

    def remove_deanonymize_operator(self, operator: Type[Operator]):
        """Remove a deanonymizer from the factory.

        :param operator: The operator class to remove.
        """
        if operator().operator_name() not in self._deanonymizers:
            logger.error(
                f"Operator {operator().operator_name()} not found in deanonymizers list"
            )
            raise InvalidParamError(
                f"Operator {operator().operator_name()} not found in deanonymizers list"
            )
        self._deanonymizers.pop(operator().operator_name(), None)

    def create_operator_class(
        self, operator_name: str, operator_type: OperatorType
    ) -> Operator:
        """
        Extract the operator class from the operators list.

        :param operator_type: Either Anonymize or Decrypt to defer between operators.
        :type operator_name: operator name.
        :return: operator class entity.
        """

        operators_by_type = self.__get_operators_classes().get(operator_type)
        if not operators_by_type:
            logger.error(f"No such operator type {operator_type}")
            raise InvalidParamError(f"Invalid operator type '{operator_type}'.")

        operator = operators_by_type.get(operator_name)
        if not operator:
            logger.error(f"No such operator {operator_name}")
            raise InvalidParamError(f"Invalid operator class '{operator_name}'.")

        return operator()

    def __get_operators_classes(self) -> Dict[OperatorType, Dict[str, Type[Operator]]]:
        operator_classes = {
            OperatorType.Anonymize: self.get_anonymizers(),
            OperatorType.Deanonymize: self.get_deanonymizers(),
        }
        return operator_classes

    def get_anonymizers(self) -> Dict[str, Type[Operator]]:
        """Return all anonymizers classes currently available."""
        return self._anonymizers

    def get_deanonymizers(self) -> Dict[str, Type[Operator]]:
        """Return all deanonymizers classes currently available."""
        return self._deanonymizers

    def __get_operators_by_type(
        self, operator_type: OperatorType
    ) -> Dict[str, Type[Operator]]:
        return self.__get_operators_classes().get(operator_type)
add_anonymize_operator
add_anonymize_operator(operator: Type[Operator])

Add a new anonymizer to the factory.

PARAMETER DESCRIPTION
operator

The operator class to add.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
69
70
71
72
73
74
def add_anonymize_operator(self, operator: Type[Operator]):
    """Add a new anonymizer to the factory.

    :param operator: The operator class to add.
    """
    self._anonymizers[operator().operator_name()] = operator
add_deanonymize_operator
add_deanonymize_operator(operator: Type[Operator])

Add a new deanonymizer to the factory.

PARAMETER DESCRIPTION
operator

The operator class to add.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
76
77
78
79
80
81
def add_deanonymize_operator(self, operator: Type[Operator]):
    """Add a new deanonymizer to the factory.

    :param operator: The operator class to add.
    """
    self._deanonymizers[operator().operator_name()] = operator
remove_anonymize_operator
remove_anonymize_operator(operator: Type[Operator])

Remove an anonymizer from the factory.

PARAMETER DESCRIPTION
operator

The operator class to remove.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
83
84
85
86
87
88
89
90
91
92
93
94
95
def remove_anonymize_operator(self, operator: Type[Operator]):
    """Remove an anonymizer from the factory.

    :param operator: The operator class to remove.
    """
    if operator().operator_name() not in self._anonymizers:
        logger.error(
            f"Operator {operator().operator_name()} not found in anonymizers list"
        )
        raise InvalidParamError(
            f"Operator {operator().operator_name()} not found in anonymizers list"
        )
    self._anonymizers.pop(operator().operator_name(), None)
remove_deanonymize_operator
remove_deanonymize_operator(operator: Type[Operator])

Remove a deanonymizer from the factory.

PARAMETER DESCRIPTION
operator

The operator class to remove.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def remove_deanonymize_operator(self, operator: Type[Operator]):
    """Remove a deanonymizer from the factory.

    :param operator: The operator class to remove.
    """
    if operator().operator_name() not in self._deanonymizers:
        logger.error(
            f"Operator {operator().operator_name()} not found in deanonymizers list"
        )
        raise InvalidParamError(
            f"Operator {operator().operator_name()} not found in deanonymizers list"
        )
    self._deanonymizers.pop(operator().operator_name(), None)
create_operator_class
create_operator_class(
    operator_name: str, operator_type: OperatorType
) -> Operator

Extract the operator class from the operators list.

PARAMETER DESCRIPTION
operator_type

Either Anonymize or Decrypt to defer between operators.

TYPE: OperatorType

RETURNS DESCRIPTION
Operator

operator class entity.

Source code in presidio_anonymizer/operators/operators_factory.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def create_operator_class(
    self, operator_name: str, operator_type: OperatorType
) -> Operator:
    """
    Extract the operator class from the operators list.

    :param operator_type: Either Anonymize or Decrypt to defer between operators.
    :type operator_name: operator name.
    :return: operator class entity.
    """

    operators_by_type = self.__get_operators_classes().get(operator_type)
    if not operators_by_type:
        logger.error(f"No such operator type {operator_type}")
        raise InvalidParamError(f"Invalid operator type '{operator_type}'.")

    operator = operators_by_type.get(operator_name)
    if not operator:
        logger.error(f"No such operator {operator_name}")
        raise InvalidParamError(f"Invalid operator class '{operator_name}'.")

    return operator()
get_anonymizers
get_anonymizers() -> Dict[str, Type[Operator]]

Return all anonymizers classes currently available.

Source code in presidio_anonymizer/operators/operators_factory.py
141
142
143
def get_anonymizers(self) -> Dict[str, Type[Operator]]:
    """Return all anonymizers classes currently available."""
    return self._anonymizers
get_deanonymizers
get_deanonymizers() -> Dict[str, Type[Operator]]

Return all deanonymizers classes currently available.

Source code in presidio_anonymizer/operators/operators_factory.py
145
146
147
def get_deanonymizers(self) -> Dict[str, Type[Operator]]:
    """Return all deanonymizers classes currently available."""
    return self._deanonymizers

keep

Keeps the PII text unmodified.

BaseKeep

Bases: Operator, ABC

Abstract base class for keep operators.

Implements the common functionality of keep operators, leaving the operator type to be implemented by concrete classes

METHOD DESCRIPTION
operator_name

Return operator name.

operator_type

Return operator type.

operate

:return: original text.

validate

Keep does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/keep.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BaseKeep(Operator, ABC):
    """Abstract base class for keep operators.

    Implements the common functionality of keep operators, leaving the operator
    type to be implemented by concrete classes
    """

    def operate(self, text: str = None, params: Dict = None) -> str:
        """:return: original text."""
        return text

    def validate(self, params: Dict = None) -> None:
        """Keep does not require any paramters so no validation is needed."""
        pass
operator_name abstractmethod
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/operator.py
31
32
33
34
@abstractmethod
def operator_name(self) -> str:
    """Return operator name."""
    pass
operator_type abstractmethod
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/operator.py
36
37
38
39
@abstractmethod
def operator_type(self) -> OperatorType:
    """Return operator type."""
    pass
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

original text.

Source code in presidio_anonymizer/operators/keep.py
16
17
18
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: original text."""
    return text
validate
validate(params: Dict = None) -> None

Keep does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/keep.py
20
21
22
def validate(self, params: Dict = None) -> None:
    """Keep does not require any paramters so no validation is needed."""
    pass
Keep

Bases: BaseKeep

No-op anonymizer that keeps the PII text unmodified.

This is useful when you don't want to anonymize some types of PII, but wants to keep track of it with the other PIIs.

METHOD DESCRIPTION
operate

:return: original text.

validate

Keep does not require any paramters so no validation is needed.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/keep.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Keep(BaseKeep):
    """No-op anonymizer that keeps the PII text unmodified.

    This is useful when you don't want to anonymize some types of PII,
    but wants to keep track of it with the other PIIs.
    """

    def operator_name(self) -> str:
        """Return operator name."""
        return "keep"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

original text.

Source code in presidio_anonymizer/operators/keep.py
16
17
18
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: original text."""
    return text
validate
validate(params: Dict = None) -> None

Keep does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/keep.py
20
21
22
def validate(self, params: Dict = None) -> None:
    """Keep does not require any paramters so no validation is needed."""
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/keep.py
32
33
34
def operator_name(self) -> str:
    """Return operator name."""
    return "keep"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/keep.py
36
37
38
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

replace

Replaces the PII text entity with new string.

Replace

Bases: Operator

Receives new text to replace old PII text entity with.

METHOD DESCRIPTION
operate

:return: new_value.

validate

Validate the new value is string.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/replace.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
class Replace(Operator):
    """Receives new text to replace old PII text entity with."""

    NEW_VALUE = "new_value"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """:return: new_value."""
        new_val = params.get(self.NEW_VALUE)
        if not new_val:
            return f"<{params.get('entity_type')}>"
        return new_val

    def validate(self, params: Dict = None) -> None:
        """Validate the new value is string."""
        validate_type(params.get(self.NEW_VALUE), self.NEW_VALUE, str)
        pass

    def operator_name(self) -> str:
        """Return operator name."""
        return "replace"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

new_value.

Source code in presidio_anonymizer/operators/replace.py
14
15
16
17
18
19
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: new_value."""
    new_val = params.get(self.NEW_VALUE)
    if not new_val:
        return f"<{params.get('entity_type')}>"
    return new_val
validate
validate(params: Dict = None) -> None

Validate the new value is string.

Source code in presidio_anonymizer/operators/replace.py
21
22
23
24
def validate(self, params: Dict = None) -> None:
    """Validate the new value is string."""
    validate_type(params.get(self.NEW_VALUE), self.NEW_VALUE, str)
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/replace.py
26
27
28
def operator_name(self) -> str:
    """Return operator name."""
    return "replace"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/replace.py
30
31
32
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

encrypt

Encrypt

Bases: Operator

Anonymizes text to an encrypted form, or it to be restored using decrypted.

METHOD DESCRIPTION
operate

Anonymize the text with an encrypted text.

validate

Validate Encrypt parameters.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/encrypt.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
class Encrypt(Operator):
    """Anonymizes text to an encrypted form, or it to be restored using decrypted."""

    KEY = "key"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Anonymize the text with an encrypted text.

        :param text: The text for encryption.
        :param params:
            * *key* The key supplied by the user for the encryption (bytes or str).
        :return: The encrypted text
        """
        key = params.get(self.KEY)
        if isinstance(key, str):
            key = key.encode("utf8")
        encrypted_text = AESCipher.encrypt(key, text)
        return encrypted_text

    def validate(self, params: Dict = None) -> None:
        """
        Validate Encrypt parameters.

        :param params:
            * *key* The key supplied by the user for the encryption.
                    Should be a string of 128, 192 or 256 bits length.
        :raises InvalidParamException: in case on an invalid parameter.
        """
        key = params.get(self.KEY)
        if isinstance(key, str):
            validate_parameter(key, self.KEY, str)
            if not AESCipher.is_valid_key_size(key.encode("utf8")):
                raise InvalidParamError(
                    f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
                )
        else:
            validate_parameter(key, self.KEY, bytes)
        if not AESCipher.is_valid_key_size(key):
            raise InvalidParamError(
                f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
            )

    def operator_name(self) -> str:
        """Return operator name."""
        return "encrypt"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str

Anonymize the text with an encrypted text.

PARAMETER DESCRIPTION
text

The text for encryption.

TYPE: str DEFAULT: None

params
  • key The key supplied by the user for the encryption (bytes or str).

TYPE: Dict DEFAULT: None

RETURNS DESCRIPTION
str

The encrypted text

Source code in presidio_anonymizer/operators/encrypt.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Anonymize the text with an encrypted text.

    :param text: The text for encryption.
    :param params:
        * *key* The key supplied by the user for the encryption (bytes or str).
    :return: The encrypted text
    """
    key = params.get(self.KEY)
    if isinstance(key, str):
        key = key.encode("utf8")
    encrypted_text = AESCipher.encrypt(key, text)
    return encrypted_text
validate
validate(params: Dict = None) -> None

Validate Encrypt parameters.

PARAMETER DESCRIPTION
params
  • key The key supplied by the user for the encryption. Should be a string of 128, 192 or 256 bits length.

TYPE: Dict DEFAULT: None

RAISES DESCRIPTION
InvalidParamException

in case on an invalid parameter.

Source code in presidio_anonymizer/operators/encrypt.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def validate(self, params: Dict = None) -> None:
    """
    Validate Encrypt parameters.

    :param params:
        * *key* The key supplied by the user for the encryption.
                Should be a string of 128, 192 or 256 bits length.
    :raises InvalidParamException: in case on an invalid parameter.
    """
    key = params.get(self.KEY)
    if isinstance(key, str):
        validate_parameter(key, self.KEY, str)
        if not AESCipher.is_valid_key_size(key.encode("utf8")):
            raise InvalidParamError(
                f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
            )
    else:
        validate_parameter(key, self.KEY, bytes)
    if not AESCipher.is_valid_key_size(key):
        raise InvalidParamError(
            f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
        )
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/encrypt.py
52
53
54
def operator_name(self) -> str:
    """Return operator name."""
    return "encrypt"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/encrypt.py
56
57
58
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

operator

Operator abstraction - each operator should implement this class.

OperatorType

Bases: Enum

Operator type either anonymize or decrypt to separate the operators.

Source code in presidio_anonymizer/operators/operator.py
 8
 9
10
11
12
class OperatorType(Enum):
    """Operator type either anonymize or decrypt to separate the operators."""

    Anonymize = 1
    Deanonymize = 2
Operator

Bases: ABC

Operator abstract class to be implemented by each operator.

METHOD DESCRIPTION
operate

Operate method to be implemented in each operator.

validate

Validate each operator parameters.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/operator.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Operator(ABC):
    """Operator abstract class to be implemented by each operator."""

    @abstractmethod
    def operate(self, text: str, params: Dict = None) -> str:
        """Operate method to be implemented in each operator."""
        pass

    @abstractmethod
    def validate(self, params: Dict = None) -> None:
        """Validate each operator parameters."""
        pass

    @abstractmethod
    def operator_name(self) -> str:
        """Return operator name."""
        pass

    @abstractmethod
    def operator_type(self) -> OperatorType:
        """Return operator type."""
        pass
operate abstractmethod
operate(text: str, params: Dict = None) -> str

Operate method to be implemented in each operator.

Source code in presidio_anonymizer/operators/operator.py
21
22
23
24
@abstractmethod
def operate(self, text: str, params: Dict = None) -> str:
    """Operate method to be implemented in each operator."""
    pass
validate abstractmethod
validate(params: Dict = None) -> None

Validate each operator parameters.

Source code in presidio_anonymizer/operators/operator.py
26
27
28
29
@abstractmethod
def validate(self, params: Dict = None) -> None:
    """Validate each operator parameters."""
    pass
operator_name abstractmethod
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/operator.py
31
32
33
34
@abstractmethod
def operator_name(self) -> str:
    """Return operator name."""
    pass
operator_type abstractmethod
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/operator.py
36
37
38
39
@abstractmethod
def operator_type(self) -> OperatorType:
    """Return operator type."""
    pass

aes_cipher

AESCipher

Advanced Encryption Standard (aka Rijndael) en/decryption in CBC mode.

METHOD DESCRIPTION
encrypt

Encrypts a text using AES cypher in CBC mode.

decrypt

Decrypts a previously AES-CBC encrypted text.

is_valid_key_size

Validate key size for AES.

Source code in presidio_anonymizer/operators/aes_cipher.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 AESCipher:
    """Advanced Encryption Standard (aka Rijndael) en/decryption in CBC mode."""

    @staticmethod
    def encrypt(key: bytes, text: str) -> str:
        """
        Encrypts a text using AES cypher in CBC mode.

        Uses padding and random IV.
        :param key: AES encryption key in bytes.
        :param text: The text for encryption.
        :returns: The encrypted text.
        """
        encoded_text = text.encode("utf-8")
        padded_text = pad(encoded_text, AES.block_size)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        encrypted_text = base64.b64encode(iv + cipher.encrypt(padded_text))
        return encrypted_text.decode()

    @staticmethod
    def decrypt(key: bytes, text: str) -> str:
        """
        Decrypts a previously AES-CBC encrypted text.

        :param key: AES encryption key in bytes.
        :param text: The text for decryption.
        :returns: The decrypted text.
        """
        decoded_text = base64.b64decode(text)
        iv = decoded_text[: AES.block_size]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        decrypted_text = unpad(
            cipher.decrypt(decoded_text[AES.block_size :]), AES.block_size
        )
        return decrypted_text.decode("utf-8")

    @staticmethod
    def is_valid_key_size(key: bytes) -> bool:
        """
        Validate key size for AES.

        :param key: AES encryption key in bytes.
        :returns: True if the key is of valid size, False otherwise.
        """
        return len(key) in AES.key_size
encrypt staticmethod
encrypt(key: bytes, text: str) -> str

Encrypts a text using AES cypher in CBC mode.

Uses padding and random IV.

PARAMETER DESCRIPTION
key

AES encryption key in bytes.

TYPE: bytes

text

The text for encryption.

TYPE: str

RETURNS DESCRIPTION
str

The encrypted text.

Source code in presidio_anonymizer/operators/aes_cipher.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@staticmethod
def encrypt(key: bytes, text: str) -> str:
    """
    Encrypts a text using AES cypher in CBC mode.

    Uses padding and random IV.
    :param key: AES encryption key in bytes.
    :param text: The text for encryption.
    :returns: The encrypted text.
    """
    encoded_text = text.encode("utf-8")
    padded_text = pad(encoded_text, AES.block_size)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted_text = base64.b64encode(iv + cipher.encrypt(padded_text))
    return encrypted_text.decode()
decrypt staticmethod
decrypt(key: bytes, text: str) -> str

Decrypts a previously AES-CBC encrypted text.

PARAMETER DESCRIPTION
key

AES encryption key in bytes.

TYPE: bytes

text

The text for decryption.

TYPE: str

RETURNS DESCRIPTION
str

The decrypted text.

Source code in presidio_anonymizer/operators/aes_cipher.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@staticmethod
def decrypt(key: bytes, text: str) -> str:
    """
    Decrypts a previously AES-CBC encrypted text.

    :param key: AES encryption key in bytes.
    :param text: The text for decryption.
    :returns: The decrypted text.
    """
    decoded_text = base64.b64decode(text)
    iv = decoded_text[: AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted_text = unpad(
        cipher.decrypt(decoded_text[AES.block_size :]), AES.block_size
    )
    return decrypted_text.decode("utf-8")
is_valid_key_size staticmethod
is_valid_key_size(key: bytes) -> bool

Validate key size for AES.

PARAMETER DESCRIPTION
key

AES encryption key in bytes.

TYPE: bytes

RETURNS DESCRIPTION
bool

True if the key is of valid size, False otherwise.

Source code in presidio_anonymizer/operators/aes_cipher.py
45
46
47
48
49
50
51
52
53
@staticmethod
def is_valid_key_size(key: bytes) -> bool:
    """
    Validate key size for AES.

    :param key: AES encryption key in bytes.
    :returns: True if the key is of valid size, False otherwise.
    """
    return len(key) in AES.key_size

mask

Mask some or all given text entity PII with given character.

Mask

Bases: Operator

Mask the given text with given value.

METHOD DESCRIPTION
operate

Mask a given amount of text with a given character.

validate

Validate the parameters for mask.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/mask.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
class Mask(Operator):
    """Mask the given text with given value."""

    CHARS_TO_MASK = "chars_to_mask"
    FROM_END = "from_end"
    MASKING_CHAR = "masking_char"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Mask a given amount of text with a given character.

        :param text: the text to be masked
        :param params:
            masking_char: The character to be masked with
            chars_to_mask: The amount of characters to mask
            from_end: Whether to mask the text from it's end
        :return: the masked text
        """
        effective_chars_to_mask = self._get_effective_chars_to_mask(
            text, params.get(self.CHARS_TO_MASK)
        )
        from_end = params.get(self.FROM_END)
        masking_char = params.get(self.MASKING_CHAR)
        return self._get_anonymized_text(
            text, effective_chars_to_mask, from_end, masking_char
        )

    def validate(self, params: Dict = None) -> None:
        """
        Validate the parameters for mask.

        :param params:
            masking_char: The character to be masked with
            chars_to_mask: The amount of characters to mask
            from_end: Whether to mask the text from it's end
        """
        masking_char = params.get(self.MASKING_CHAR)
        validate_parameter(masking_char, self.MASKING_CHAR, str)
        if len(masking_char) > 1:
            raise InvalidParamError(
                f"Invalid input, {self.MASKING_CHAR} must be a character"
            )

        validate_parameter(params.get(self.CHARS_TO_MASK), self.CHARS_TO_MASK, int)
        validate_parameter(params.get(self.FROM_END), self.FROM_END, bool)

    def operator_name(self) -> str:
        """Return operator name."""
        return "mask"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize

    @staticmethod
    def _get_effective_chars_to_mask(text, chars_to_mask):
        return min(len(text), chars_to_mask) if chars_to_mask > 0 else 0

    @staticmethod
    def _get_anonymized_text(text, chars_to_mask, from_end, masking_char):
        if not from_end:
            return masking_char * chars_to_mask + text[chars_to_mask:]
        else:
            mask_from_index = len(text) - chars_to_mask
            return text[:mask_from_index] + masking_char * chars_to_mask
operate
operate(text: str = None, params: Dict = None) -> str

Mask a given amount of text with a given character.

PARAMETER DESCRIPTION
text

the text to be masked

TYPE: str DEFAULT: None

params

TYPE: Dict DEFAULT: None

RETURNS DESCRIPTION
str

the masked text

Source code in presidio_anonymizer/operators/mask.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Mask a given amount of text with a given character.

    :param text: the text to be masked
    :param params:
        masking_char: The character to be masked with
        chars_to_mask: The amount of characters to mask
        from_end: Whether to mask the text from it's end
    :return: the masked text
    """
    effective_chars_to_mask = self._get_effective_chars_to_mask(
        text, params.get(self.CHARS_TO_MASK)
    )
    from_end = params.get(self.FROM_END)
    masking_char = params.get(self.MASKING_CHAR)
    return self._get_anonymized_text(
        text, effective_chars_to_mask, from_end, masking_char
    )
validate
validate(params: Dict = None) -> None

Validate the parameters for mask.

PARAMETER DESCRIPTION
params

TYPE: Dict DEFAULT: None

Source code in presidio_anonymizer/operators/mask.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def validate(self, params: Dict = None) -> None:
    """
    Validate the parameters for mask.

    :param params:
        masking_char: The character to be masked with
        chars_to_mask: The amount of characters to mask
        from_end: Whether to mask the text from it's end
    """
    masking_char = params.get(self.MASKING_CHAR)
    validate_parameter(masking_char, self.MASKING_CHAR, str)
    if len(masking_char) > 1:
        raise InvalidParamError(
            f"Invalid input, {self.MASKING_CHAR} must be a character"
        )

    validate_parameter(params.get(self.CHARS_TO_MASK), self.CHARS_TO_MASK, int)
    validate_parameter(params.get(self.FROM_END), self.FROM_END, bool)
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/mask.py
56
57
58
def operator_name(self) -> str:
    """Return operator name."""
    return "mask"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/mask.py
60
61
62
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

OperatorType

Bases: Enum

Operator type either anonymize or decrypt to separate the operators.

Source code in presidio_anonymizer/operators/operator.py
 8
 9
10
11
12
class OperatorType(Enum):
    """Operator type either anonymize or decrypt to separate the operators."""

    Anonymize = 1
    Deanonymize = 2

Operator

Bases: ABC

Operator abstract class to be implemented by each operator.

METHOD DESCRIPTION
operate

Operate method to be implemented in each operator.

validate

Validate each operator parameters.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/operator.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Operator(ABC):
    """Operator abstract class to be implemented by each operator."""

    @abstractmethod
    def operate(self, text: str, params: Dict = None) -> str:
        """Operate method to be implemented in each operator."""
        pass

    @abstractmethod
    def validate(self, params: Dict = None) -> None:
        """Validate each operator parameters."""
        pass

    @abstractmethod
    def operator_name(self) -> str:
        """Return operator name."""
        pass

    @abstractmethod
    def operator_type(self) -> OperatorType:
        """Return operator type."""
        pass
operate abstractmethod
operate(text: str, params: Dict = None) -> str

Operate method to be implemented in each operator.

Source code in presidio_anonymizer/operators/operator.py
21
22
23
24
@abstractmethod
def operate(self, text: str, params: Dict = None) -> str:
    """Operate method to be implemented in each operator."""
    pass
validate abstractmethod
validate(params: Dict = None) -> None

Validate each operator parameters.

Source code in presidio_anonymizer/operators/operator.py
26
27
28
29
@abstractmethod
def validate(self, params: Dict = None) -> None:
    """Validate each operator parameters."""
    pass
operator_name abstractmethod
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/operator.py
31
32
33
34
@abstractmethod
def operator_name(self) -> str:
    """Return operator name."""
    pass
operator_type abstractmethod
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/operator.py
36
37
38
39
@abstractmethod
def operator_type(self) -> OperatorType:
    """Return operator type."""
    pass

AESCipher

Advanced Encryption Standard (aka Rijndael) en/decryption in CBC mode.

METHOD DESCRIPTION
encrypt

Encrypts a text using AES cypher in CBC mode.

decrypt

Decrypts a previously AES-CBC encrypted text.

is_valid_key_size

Validate key size for AES.

Source code in presidio_anonymizer/operators/aes_cipher.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 AESCipher:
    """Advanced Encryption Standard (aka Rijndael) en/decryption in CBC mode."""

    @staticmethod
    def encrypt(key: bytes, text: str) -> str:
        """
        Encrypts a text using AES cypher in CBC mode.

        Uses padding and random IV.
        :param key: AES encryption key in bytes.
        :param text: The text for encryption.
        :returns: The encrypted text.
        """
        encoded_text = text.encode("utf-8")
        padded_text = pad(encoded_text, AES.block_size)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        encrypted_text = base64.b64encode(iv + cipher.encrypt(padded_text))
        return encrypted_text.decode()

    @staticmethod
    def decrypt(key: bytes, text: str) -> str:
        """
        Decrypts a previously AES-CBC encrypted text.

        :param key: AES encryption key in bytes.
        :param text: The text for decryption.
        :returns: The decrypted text.
        """
        decoded_text = base64.b64decode(text)
        iv = decoded_text[: AES.block_size]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        decrypted_text = unpad(
            cipher.decrypt(decoded_text[AES.block_size :]), AES.block_size
        )
        return decrypted_text.decode("utf-8")

    @staticmethod
    def is_valid_key_size(key: bytes) -> bool:
        """
        Validate key size for AES.

        :param key: AES encryption key in bytes.
        :returns: True if the key is of valid size, False otherwise.
        """
        return len(key) in AES.key_size
encrypt staticmethod
encrypt(key: bytes, text: str) -> str

Encrypts a text using AES cypher in CBC mode.

Uses padding and random IV.

PARAMETER DESCRIPTION
key

AES encryption key in bytes.

TYPE: bytes

text

The text for encryption.

TYPE: str

RETURNS DESCRIPTION
str

The encrypted text.

Source code in presidio_anonymizer/operators/aes_cipher.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@staticmethod
def encrypt(key: bytes, text: str) -> str:
    """
    Encrypts a text using AES cypher in CBC mode.

    Uses padding and random IV.
    :param key: AES encryption key in bytes.
    :param text: The text for encryption.
    :returns: The encrypted text.
    """
    encoded_text = text.encode("utf-8")
    padded_text = pad(encoded_text, AES.block_size)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted_text = base64.b64encode(iv + cipher.encrypt(padded_text))
    return encrypted_text.decode()
decrypt staticmethod
decrypt(key: bytes, text: str) -> str

Decrypts a previously AES-CBC encrypted text.

PARAMETER DESCRIPTION
key

AES encryption key in bytes.

TYPE: bytes

text

The text for decryption.

TYPE: str

RETURNS DESCRIPTION
str

The decrypted text.

Source code in presidio_anonymizer/operators/aes_cipher.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@staticmethod
def decrypt(key: bytes, text: str) -> str:
    """
    Decrypts a previously AES-CBC encrypted text.

    :param key: AES encryption key in bytes.
    :param text: The text for decryption.
    :returns: The decrypted text.
    """
    decoded_text = base64.b64decode(text)
    iv = decoded_text[: AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted_text = unpad(
        cipher.decrypt(decoded_text[AES.block_size :]), AES.block_size
    )
    return decrypted_text.decode("utf-8")
is_valid_key_size staticmethod
is_valid_key_size(key: bytes) -> bool

Validate key size for AES.

PARAMETER DESCRIPTION
key

AES encryption key in bytes.

TYPE: bytes

RETURNS DESCRIPTION
bool

True if the key is of valid size, False otherwise.

Source code in presidio_anonymizer/operators/aes_cipher.py
45
46
47
48
49
50
51
52
53
@staticmethod
def is_valid_key_size(key: bytes) -> bool:
    """
    Validate key size for AES.

    :param key: AES encryption key in bytes.
    :returns: True if the key is of valid size, False otherwise.
    """
    return len(key) in AES.key_size

Custom

Bases: Operator

Replace PII text entity with the results of a function executed on the PII text.

The function retrun type must be a string

METHOD DESCRIPTION
operate

:return: result of function executed on the text.

validate

Validate the provided function is returning a string.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/custom.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
class Custom(Operator):
    """
    Replace PII text entity with the results of a function executed on the PII text.

    The function retrun type must be a string
    """

    LAMBDA = "lambda"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """:return: result of function executed on the text."""
        new_val = params.get(self.LAMBDA)
        return new_val(text)

    def validate(self, params: Dict) -> None:
        """Validate the provided function is returning a string."""
        new_val = params.get(self.LAMBDA)
        if callable(new_val):
            if not isinstance(new_val("PII"), str):
                raise InvalidParamError("Function return type must be a str")

        else:
            raise InvalidParamError("New value must be a callable function")

    def operator_name(self) -> str:
        """Return operator name."""
        return "custom"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

result of function executed on the text.

Source code in presidio_anonymizer/operators/custom.py
18
19
20
21
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: result of function executed on the text."""
    new_val = params.get(self.LAMBDA)
    return new_val(text)
validate
validate(params: Dict) -> None

Validate the provided function is returning a string.

Source code in presidio_anonymizer/operators/custom.py
23
24
25
26
27
28
29
30
31
def validate(self, params: Dict) -> None:
    """Validate the provided function is returning a string."""
    new_val = params.get(self.LAMBDA)
    if callable(new_val):
        if not isinstance(new_val("PII"), str):
            raise InvalidParamError("Function return type must be a str")

    else:
        raise InvalidParamError("New value must be a callable function")
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/custom.py
33
34
35
def operator_name(self) -> str:
    """Return operator name."""
    return "custom"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/custom.py
37
38
39
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

DeanonymizeKeep

Bases: BaseKeep

No-op deanonymizer that keeps the PII text unmodified.

This is useful when you don't want to anonymize some types of PII, but wants to keep track of it with the other PIIs.

METHOD DESCRIPTION
operate

:return: original text.

validate

Keep does not require any paramters so no validation is needed.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/deanonymize_keep.py
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class DeanonymizeKeep(BaseKeep):
    """No-op deanonymizer that keeps the PII text unmodified.

    This is useful when you don't want to anonymize some types of PII,
    but wants to keep track of it with the other PIIs.
    """

    def operator_name(self) -> str:
        """Return operator name."""
        return "deanonymize_keep"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Deanonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

original text.

Source code in presidio_anonymizer/operators/keep.py
16
17
18
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: original text."""
    return text
validate
validate(params: Dict = None) -> None

Keep does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/keep.py
20
21
22
def validate(self, params: Dict = None) -> None:
    """Keep does not require any paramters so no validation is needed."""
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/deanonymize_keep.py
12
13
14
def operator_name(self) -> str:
    """Return operator name."""
    return "deanonymize_keep"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/deanonymize_keep.py
16
17
18
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Deanonymize

Encrypt

Bases: Operator

Anonymizes text to an encrypted form, or it to be restored using decrypted.

METHOD DESCRIPTION
operate

Anonymize the text with an encrypted text.

validate

Validate Encrypt parameters.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/encrypt.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
class Encrypt(Operator):
    """Anonymizes text to an encrypted form, or it to be restored using decrypted."""

    KEY = "key"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Anonymize the text with an encrypted text.

        :param text: The text for encryption.
        :param params:
            * *key* The key supplied by the user for the encryption (bytes or str).
        :return: The encrypted text
        """
        key = params.get(self.KEY)
        if isinstance(key, str):
            key = key.encode("utf8")
        encrypted_text = AESCipher.encrypt(key, text)
        return encrypted_text

    def validate(self, params: Dict = None) -> None:
        """
        Validate Encrypt parameters.

        :param params:
            * *key* The key supplied by the user for the encryption.
                    Should be a string of 128, 192 or 256 bits length.
        :raises InvalidParamException: in case on an invalid parameter.
        """
        key = params.get(self.KEY)
        if isinstance(key, str):
            validate_parameter(key, self.KEY, str)
            if not AESCipher.is_valid_key_size(key.encode("utf8")):
                raise InvalidParamError(
                    f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
                )
        else:
            validate_parameter(key, self.KEY, bytes)
        if not AESCipher.is_valid_key_size(key):
            raise InvalidParamError(
                f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
            )

    def operator_name(self) -> str:
        """Return operator name."""
        return "encrypt"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str

Anonymize the text with an encrypted text.

PARAMETER DESCRIPTION
text

The text for encryption.

TYPE: str DEFAULT: None

params
  • key The key supplied by the user for the encryption (bytes or str).

TYPE: Dict DEFAULT: None

RETURNS DESCRIPTION
str

The encrypted text

Source code in presidio_anonymizer/operators/encrypt.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Anonymize the text with an encrypted text.

    :param text: The text for encryption.
    :param params:
        * *key* The key supplied by the user for the encryption (bytes or str).
    :return: The encrypted text
    """
    key = params.get(self.KEY)
    if isinstance(key, str):
        key = key.encode("utf8")
    encrypted_text = AESCipher.encrypt(key, text)
    return encrypted_text
validate
validate(params: Dict = None) -> None

Validate Encrypt parameters.

PARAMETER DESCRIPTION
params
  • key The key supplied by the user for the encryption. Should be a string of 128, 192 or 256 bits length.

TYPE: Dict DEFAULT: None

RAISES DESCRIPTION
InvalidParamException

in case on an invalid parameter.

Source code in presidio_anonymizer/operators/encrypt.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def validate(self, params: Dict = None) -> None:
    """
    Validate Encrypt parameters.

    :param params:
        * *key* The key supplied by the user for the encryption.
                Should be a string of 128, 192 or 256 bits length.
    :raises InvalidParamException: in case on an invalid parameter.
    """
    key = params.get(self.KEY)
    if isinstance(key, str):
        validate_parameter(key, self.KEY, str)
        if not AESCipher.is_valid_key_size(key.encode("utf8")):
            raise InvalidParamError(
                f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
            )
    else:
        validate_parameter(key, self.KEY, bytes)
    if not AESCipher.is_valid_key_size(key):
        raise InvalidParamError(
            f"Invalid input, {self.KEY} must be of length 128, 192 or 256 bits"
        )
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/encrypt.py
52
53
54
def operator_name(self) -> str:
    """Return operator name."""
    return "encrypt"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/encrypt.py
56
57
58
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

Decrypt

Bases: Operator

Decrypt text to from its encrypted form.

METHOD DESCRIPTION
operate

Decrypt the text.

validate

Validate Decrypt parameters.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/decrypt.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
class Decrypt(Operator):
    """Decrypt text to from its encrypted form."""

    NAME = "decrypt"
    KEY = "key"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Decrypt the text.

        :param text: The text for decryption.
        :param params:
            **key* The key supplied by the user for the encryption (bytes or str).
        :return: The encrypted text
        """
        key = params.get(self.KEY)
        if isinstance(key, str):
            key = key.encode("utf8")
        decrypted_text = AESCipher.decrypt(key=key, text=text)
        return decrypted_text

    def validate(self, params: Dict = None) -> None:
        """
        Validate Decrypt parameters.

        :param params:
            * *key* The key supplied by the user for the encryption.
                    Should be a string of 128, 192 or 256 bits length.
        :raises InvalidParamException: in case on an invalid parameter.
        """
        Encrypt().validate(params)

    def operator_name(self) -> str:
        """Return operator name."""
        return self.NAME

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Deanonymize
operate
operate(text: str = None, params: Dict = None) -> str

Decrypt the text.

PARAMETER DESCRIPTION
text

The text for decryption.

TYPE: str DEFAULT: None

params

*key The key supplied by the user for the encryption (bytes or str).

TYPE: Dict DEFAULT: None

RETURNS DESCRIPTION
str

The encrypted text

Source code in presidio_anonymizer/operators/decrypt.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Decrypt the text.

    :param text: The text for decryption.
    :param params:
        **key* The key supplied by the user for the encryption (bytes or str).
    :return: The encrypted text
    """
    key = params.get(self.KEY)
    if isinstance(key, str):
        key = key.encode("utf8")
    decrypted_text = AESCipher.decrypt(key=key, text=text)
    return decrypted_text
validate
validate(params: Dict = None) -> None

Validate Decrypt parameters.

PARAMETER DESCRIPTION
params
  • key The key supplied by the user for the encryption. Should be a string of 128, 192 or 256 bits length.

TYPE: Dict DEFAULT: None

RAISES DESCRIPTION
InvalidParamException

in case on an invalid parameter.

Source code in presidio_anonymizer/operators/decrypt.py
28
29
30
31
32
33
34
35
36
37
def validate(self, params: Dict = None) -> None:
    """
    Validate Decrypt parameters.

    :param params:
        * *key* The key supplied by the user for the encryption.
                Should be a string of 128, 192 or 256 bits length.
    :raises InvalidParamException: in case on an invalid parameter.
    """
    Encrypt().validate(params)
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/decrypt.py
39
40
41
def operator_name(self) -> str:
    """Return operator name."""
    return self.NAME
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/decrypt.py
43
44
45
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Deanonymize

Hash

Bases: Operator

Hash given text with sha256/sha512/md5 algorithm.

METHOD DESCRIPTION
operate

Hash given value using sha256.

validate

Validate the hash type is string and in range of allowed hash types.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/hash.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
class Hash(Operator):
    """Hash given text with sha256/sha512/md5 algorithm."""

    HASH_TYPE = "hash_type"
    SHA256 = "sha256"
    SHA512 = "sha512"
    MD5 = "md5"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Hash given value using sha256.

        :return: hashed original text
        """
        hash_type = self._get_hash_type_or_default(params)
        hash_switcher = {
            self.SHA256: lambda s: sha256(s),
            self.SHA512: lambda s: sha512(s),
            self.MD5: lambda s: md5(s),
        }
        return hash_switcher.get(hash_type)(text.encode()).hexdigest()

    def validate(self, params: Dict = None) -> None:
        """Validate the hash type is string and in range of allowed hash types."""
        validate_parameter_in_range(
            [self.SHA256, self.SHA512, self.MD5],
            self._get_hash_type_or_default(params),
            self.HASH_TYPE,
            str,
        )
        pass

    def operator_name(self) -> str:
        """Return operator name."""
        return "hash"

    def _get_hash_type_or_default(self, params: Dict = None):
        return params.get(self.HASH_TYPE, self.SHA256)

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str

Hash given value using sha256.

RETURNS DESCRIPTION
str

hashed original text

Source code in presidio_anonymizer/operators/hash.py
18
19
20
21
22
23
24
25
26
27
28
29
30
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Hash given value using sha256.

    :return: hashed original text
    """
    hash_type = self._get_hash_type_or_default(params)
    hash_switcher = {
        self.SHA256: lambda s: sha256(s),
        self.SHA512: lambda s: sha512(s),
        self.MD5: lambda s: md5(s),
    }
    return hash_switcher.get(hash_type)(text.encode()).hexdigest()
validate
validate(params: Dict = None) -> None

Validate the hash type is string and in range of allowed hash types.

Source code in presidio_anonymizer/operators/hash.py
32
33
34
35
36
37
38
39
40
def validate(self, params: Dict = None) -> None:
    """Validate the hash type is string and in range of allowed hash types."""
    validate_parameter_in_range(
        [self.SHA256, self.SHA512, self.MD5],
        self._get_hash_type_or_default(params),
        self.HASH_TYPE,
        str,
    )
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/hash.py
42
43
44
def operator_name(self) -> str:
    """Return operator name."""
    return "hash"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/hash.py
49
50
51
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

Keep

Bases: BaseKeep

No-op anonymizer that keeps the PII text unmodified.

This is useful when you don't want to anonymize some types of PII, but wants to keep track of it with the other PIIs.

METHOD DESCRIPTION
operate

:return: original text.

validate

Keep does not require any paramters so no validation is needed.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/keep.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Keep(BaseKeep):
    """No-op anonymizer that keeps the PII text unmodified.

    This is useful when you don't want to anonymize some types of PII,
    but wants to keep track of it with the other PIIs.
    """

    def operator_name(self) -> str:
        """Return operator name."""
        return "keep"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

original text.

Source code in presidio_anonymizer/operators/keep.py
16
17
18
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: original text."""
    return text
validate
validate(params: Dict = None) -> None

Keep does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/keep.py
20
21
22
def validate(self, params: Dict = None) -> None:
    """Keep does not require any paramters so no validation is needed."""
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/keep.py
32
33
34
def operator_name(self) -> str:
    """Return operator name."""
    return "keep"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/keep.py
36
37
38
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

Mask

Bases: Operator

Mask the given text with given value.

METHOD DESCRIPTION
operate

Mask a given amount of text with a given character.

validate

Validate the parameters for mask.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/mask.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
class Mask(Operator):
    """Mask the given text with given value."""

    CHARS_TO_MASK = "chars_to_mask"
    FROM_END = "from_end"
    MASKING_CHAR = "masking_char"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """
        Mask a given amount of text with a given character.

        :param text: the text to be masked
        :param params:
            masking_char: The character to be masked with
            chars_to_mask: The amount of characters to mask
            from_end: Whether to mask the text from it's end
        :return: the masked text
        """
        effective_chars_to_mask = self._get_effective_chars_to_mask(
            text, params.get(self.CHARS_TO_MASK)
        )
        from_end = params.get(self.FROM_END)
        masking_char = params.get(self.MASKING_CHAR)
        return self._get_anonymized_text(
            text, effective_chars_to_mask, from_end, masking_char
        )

    def validate(self, params: Dict = None) -> None:
        """
        Validate the parameters for mask.

        :param params:
            masking_char: The character to be masked with
            chars_to_mask: The amount of characters to mask
            from_end: Whether to mask the text from it's end
        """
        masking_char = params.get(self.MASKING_CHAR)
        validate_parameter(masking_char, self.MASKING_CHAR, str)
        if len(masking_char) > 1:
            raise InvalidParamError(
                f"Invalid input, {self.MASKING_CHAR} must be a character"
            )

        validate_parameter(params.get(self.CHARS_TO_MASK), self.CHARS_TO_MASK, int)
        validate_parameter(params.get(self.FROM_END), self.FROM_END, bool)

    def operator_name(self) -> str:
        """Return operator name."""
        return "mask"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize

    @staticmethod
    def _get_effective_chars_to_mask(text, chars_to_mask):
        return min(len(text), chars_to_mask) if chars_to_mask > 0 else 0

    @staticmethod
    def _get_anonymized_text(text, chars_to_mask, from_end, masking_char):
        if not from_end:
            return masking_char * chars_to_mask + text[chars_to_mask:]
        else:
            mask_from_index = len(text) - chars_to_mask
            return text[:mask_from_index] + masking_char * chars_to_mask
operate
operate(text: str = None, params: Dict = None) -> str

Mask a given amount of text with a given character.

PARAMETER DESCRIPTION
text

the text to be masked

TYPE: str DEFAULT: None

params

TYPE: Dict DEFAULT: None

RETURNS DESCRIPTION
str

the masked text

Source code in presidio_anonymizer/operators/mask.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def operate(self, text: str = None, params: Dict = None) -> str:
    """
    Mask a given amount of text with a given character.

    :param text: the text to be masked
    :param params:
        masking_char: The character to be masked with
        chars_to_mask: The amount of characters to mask
        from_end: Whether to mask the text from it's end
    :return: the masked text
    """
    effective_chars_to_mask = self._get_effective_chars_to_mask(
        text, params.get(self.CHARS_TO_MASK)
    )
    from_end = params.get(self.FROM_END)
    masking_char = params.get(self.MASKING_CHAR)
    return self._get_anonymized_text(
        text, effective_chars_to_mask, from_end, masking_char
    )
validate
validate(params: Dict = None) -> None

Validate the parameters for mask.

PARAMETER DESCRIPTION
params

TYPE: Dict DEFAULT: None

Source code in presidio_anonymizer/operators/mask.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def validate(self, params: Dict = None) -> None:
    """
    Validate the parameters for mask.

    :param params:
        masking_char: The character to be masked with
        chars_to_mask: The amount of characters to mask
        from_end: Whether to mask the text from it's end
    """
    masking_char = params.get(self.MASKING_CHAR)
    validate_parameter(masking_char, self.MASKING_CHAR, str)
    if len(masking_char) > 1:
        raise InvalidParamError(
            f"Invalid input, {self.MASKING_CHAR} must be a character"
        )

    validate_parameter(params.get(self.CHARS_TO_MASK), self.CHARS_TO_MASK, int)
    validate_parameter(params.get(self.FROM_END), self.FROM_END, bool)
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/mask.py
56
57
58
def operator_name(self) -> str:
    """Return operator name."""
    return "mask"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/mask.py
60
61
62
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

Redact

Bases: Operator

Redact the string - empty value.

METHOD DESCRIPTION
operate

:return: an empty value.

validate

Redact does not require any paramters so no validation is needed.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/redact.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Redact(Operator):
    """Redact the string - empty value."""

    def operate(self, text: str = None, params: Dict = None) -> str:
        """:return: an empty value."""
        return ""

    def validate(self, params: Dict = None) -> None:
        """Redact does not require any paramters so no validation is needed."""
        pass

    def operator_name(self) -> str:
        """Return operator name."""
        return "redact"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

an empty value.

Source code in presidio_anonymizer/operators/redact.py
11
12
13
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: an empty value."""
    return ""
validate
validate(params: Dict = None) -> None

Redact does not require any paramters so no validation is needed.

Source code in presidio_anonymizer/operators/redact.py
15
16
17
def validate(self, params: Dict = None) -> None:
    """Redact does not require any paramters so no validation is needed."""
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/redact.py
19
20
21
def operator_name(self) -> str:
    """Return operator name."""
    return "redact"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/redact.py
23
24
25
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

Replace

Bases: Operator

Receives new text to replace old PII text entity with.

METHOD DESCRIPTION
operate

:return: new_value.

validate

Validate the new value is string.

operator_name

Return operator name.

operator_type

Return operator type.

Source code in presidio_anonymizer/operators/replace.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
class Replace(Operator):
    """Receives new text to replace old PII text entity with."""

    NEW_VALUE = "new_value"

    def operate(self, text: str = None, params: Dict = None) -> str:
        """:return: new_value."""
        new_val = params.get(self.NEW_VALUE)
        if not new_val:
            return f"<{params.get('entity_type')}>"
        return new_val

    def validate(self, params: Dict = None) -> None:
        """Validate the new value is string."""
        validate_type(params.get(self.NEW_VALUE), self.NEW_VALUE, str)
        pass

    def operator_name(self) -> str:
        """Return operator name."""
        return "replace"

    def operator_type(self) -> OperatorType:
        """Return operator type."""
        return OperatorType.Anonymize
operate
operate(text: str = None, params: Dict = None) -> str
RETURNS DESCRIPTION
str

new_value.

Source code in presidio_anonymizer/operators/replace.py
14
15
16
17
18
19
def operate(self, text: str = None, params: Dict = None) -> str:
    """:return: new_value."""
    new_val = params.get(self.NEW_VALUE)
    if not new_val:
        return f"<{params.get('entity_type')}>"
    return new_val
validate
validate(params: Dict = None) -> None

Validate the new value is string.

Source code in presidio_anonymizer/operators/replace.py
21
22
23
24
def validate(self, params: Dict = None) -> None:
    """Validate the new value is string."""
    validate_type(params.get(self.NEW_VALUE), self.NEW_VALUE, str)
    pass
operator_name
operator_name() -> str

Return operator name.

Source code in presidio_anonymizer/operators/replace.py
26
27
28
def operator_name(self) -> str:
    """Return operator name."""
    return "replace"
operator_type
operator_type() -> OperatorType

Return operator type.

Source code in presidio_anonymizer/operators/replace.py
30
31
32
def operator_type(self) -> OperatorType:
    """Return operator type."""
    return OperatorType.Anonymize

OperatorsFactory

Operators factory to get the correct operator class.

METHOD DESCRIPTION
add_anonymize_operator

Add a new anonymizer to the factory.

add_deanonymize_operator

Add a new deanonymizer to the factory.

remove_anonymize_operator

Remove an anonymizer from the factory.

remove_deanonymize_operator

Remove a deanonymizer from the factory.

create_operator_class

Extract the operator class from the operators list.

get_anonymizers

Return all anonymizers classes currently available.

get_deanonymizers

Return all deanonymizers classes currently available.

Source code in presidio_anonymizer/operators/operators_factory.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
class OperatorsFactory:
    """Operators factory to get the correct operator class."""

    def __init__(
        self,
    ):
        self._anonymizers = self.__load_predefined(OperatorType.Anonymize)
        self._deanonymizers = self.__load_predefined(OperatorType.Deanonymize)

    @staticmethod
    def __load_predefined(operator_type: OperatorType) -> Dict[str, Type[Operator]]:
        """
        Return all predefined operators.

        Return a dict with {name: operator class} for each operator.
        """

        operators = (
            ANONYMIZERS if operator_type == OperatorType.Anonymize else DEANONYMIZERS
        )
        operators_dict = {
            operator().operator_name(): operator for operator in operators
        }

        return operators_dict

    @staticmethod
    def __load_predefined_deanonymizers() -> Dict[str, Type[Operator]]:
        """
        Return all predefined deanonymizers.

        Return a dict with name, deanonymizer class per each deanonymizer.
        """

        deanonymizers = [Decrypt, DeanonymizeKeep]

        deanonymizers_dict = {
            deanonymizer().operator_name(): deanonymizer
            for deanonymizer in deanonymizers
        }

        return deanonymizers_dict

    def add_anonymize_operator(self, operator: Type[Operator]):
        """Add a new anonymizer to the factory.

        :param operator: The operator class to add.
        """
        self._anonymizers[operator().operator_name()] = operator

    def add_deanonymize_operator(self, operator: Type[Operator]):
        """Add a new deanonymizer to the factory.

        :param operator: The operator class to add.
        """
        self._deanonymizers[operator().operator_name()] = operator

    def remove_anonymize_operator(self, operator: Type[Operator]):
        """Remove an anonymizer from the factory.

        :param operator: The operator class to remove.
        """
        if operator().operator_name() not in self._anonymizers:
            logger.error(
                f"Operator {operator().operator_name()} not found in anonymizers list"
            )
            raise InvalidParamError(
                f"Operator {operator().operator_name()} not found in anonymizers list"
            )
        self._anonymizers.pop(operator().operator_name(), None)

    def remove_deanonymize_operator(self, operator: Type[Operator]):
        """Remove a deanonymizer from the factory.

        :param operator: The operator class to remove.
        """
        if operator().operator_name() not in self._deanonymizers:
            logger.error(
                f"Operator {operator().operator_name()} not found in deanonymizers list"
            )
            raise InvalidParamError(
                f"Operator {operator().operator_name()} not found in deanonymizers list"
            )
        self._deanonymizers.pop(operator().operator_name(), None)

    def create_operator_class(
        self, operator_name: str, operator_type: OperatorType
    ) -> Operator:
        """
        Extract the operator class from the operators list.

        :param operator_type: Either Anonymize or Decrypt to defer between operators.
        :type operator_name: operator name.
        :return: operator class entity.
        """

        operators_by_type = self.__get_operators_classes().get(operator_type)
        if not operators_by_type:
            logger.error(f"No such operator type {operator_type}")
            raise InvalidParamError(f"Invalid operator type '{operator_type}'.")

        operator = operators_by_type.get(operator_name)
        if not operator:
            logger.error(f"No such operator {operator_name}")
            raise InvalidParamError(f"Invalid operator class '{operator_name}'.")

        return operator()

    def __get_operators_classes(self) -> Dict[OperatorType, Dict[str, Type[Operator]]]:
        operator_classes = {
            OperatorType.Anonymize: self.get_anonymizers(),
            OperatorType.Deanonymize: self.get_deanonymizers(),
        }
        return operator_classes

    def get_anonymizers(self) -> Dict[str, Type[Operator]]:
        """Return all anonymizers classes currently available."""
        return self._anonymizers

    def get_deanonymizers(self) -> Dict[str, Type[Operator]]:
        """Return all deanonymizers classes currently available."""
        return self._deanonymizers

    def __get_operators_by_type(
        self, operator_type: OperatorType
    ) -> Dict[str, Type[Operator]]:
        return self.__get_operators_classes().get(operator_type)
add_anonymize_operator
add_anonymize_operator(operator: Type[Operator])

Add a new anonymizer to the factory.

PARAMETER DESCRIPTION
operator

The operator class to add.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
69
70
71
72
73
74
def add_anonymize_operator(self, operator: Type[Operator]):
    """Add a new anonymizer to the factory.

    :param operator: The operator class to add.
    """
    self._anonymizers[operator().operator_name()] = operator
add_deanonymize_operator
add_deanonymize_operator(operator: Type[Operator])

Add a new deanonymizer to the factory.

PARAMETER DESCRIPTION
operator

The operator class to add.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
76
77
78
79
80
81
def add_deanonymize_operator(self, operator: Type[Operator]):
    """Add a new deanonymizer to the factory.

    :param operator: The operator class to add.
    """
    self._deanonymizers[operator().operator_name()] = operator
remove_anonymize_operator
remove_anonymize_operator(operator: Type[Operator])

Remove an anonymizer from the factory.

PARAMETER DESCRIPTION
operator

The operator class to remove.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
83
84
85
86
87
88
89
90
91
92
93
94
95
def remove_anonymize_operator(self, operator: Type[Operator]):
    """Remove an anonymizer from the factory.

    :param operator: The operator class to remove.
    """
    if operator().operator_name() not in self._anonymizers:
        logger.error(
            f"Operator {operator().operator_name()} not found in anonymizers list"
        )
        raise InvalidParamError(
            f"Operator {operator().operator_name()} not found in anonymizers list"
        )
    self._anonymizers.pop(operator().operator_name(), None)
remove_deanonymize_operator
remove_deanonymize_operator(operator: Type[Operator])

Remove a deanonymizer from the factory.

PARAMETER DESCRIPTION
operator

The operator class to remove.

TYPE: Type[Operator]

Source code in presidio_anonymizer/operators/operators_factory.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def remove_deanonymize_operator(self, operator: Type[Operator]):
    """Remove a deanonymizer from the factory.

    :param operator: The operator class to remove.
    """
    if operator().operator_name() not in self._deanonymizers:
        logger.error(
            f"Operator {operator().operator_name()} not found in deanonymizers list"
        )
        raise InvalidParamError(
            f"Operator {operator().operator_name()} not found in deanonymizers list"
        )
    self._deanonymizers.pop(operator().operator_name(), None)
create_operator_class
create_operator_class(
    operator_name: str, operator_type: OperatorType
) -> Operator

Extract the operator class from the operators list.

PARAMETER DESCRIPTION
operator_type

Either Anonymize or Decrypt to defer between operators.

TYPE: OperatorType

RETURNS DESCRIPTION
Operator

operator class entity.

Source code in presidio_anonymizer/operators/operators_factory.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def create_operator_class(
    self, operator_name: str, operator_type: OperatorType
) -> Operator:
    """
    Extract the operator class from the operators list.

    :param operator_type: Either Anonymize or Decrypt to defer between operators.
    :type operator_name: operator name.
    :return: operator class entity.
    """

    operators_by_type = self.__get_operators_classes().get(operator_type)
    if not operators_by_type:
        logger.error(f"No such operator type {operator_type}")
        raise InvalidParamError(f"Invalid operator type '{operator_type}'.")

    operator = operators_by_type.get(operator_name)
    if not operator:
        logger.error(f"No such operator {operator_name}")
        raise InvalidParamError(f"Invalid operator class '{operator_name}'.")

    return operator()
get_anonymizers
get_anonymizers() -> Dict[str, Type[Operator]]

Return all anonymizers classes currently available.

Source code in presidio_anonymizer/operators/operators_factory.py
141
142
143
def get_anonymizers(self) -> Dict[str, Type[Operator]]:
    """Return all anonymizers classes currently available."""
    return self._anonymizers
get_deanonymizers
get_deanonymizers() -> Dict[str, Type[Operator]]

Return all deanonymizers classes currently available.

Source code in presidio_anonymizer/operators/operators_factory.py
145
146
147
def get_deanonymizers(self) -> Dict[str, Type[Operator]]:
    """Return all deanonymizers classes currently available."""
    return self._deanonymizers

core

The core text functionality.

text_replace_builder

Handles the original text and creates a new one according to changes requests.

TextReplaceBuilder

Creates new text according to users request.

METHOD DESCRIPTION
get_text_in_position

Get part of the text inside the original text.

replace_text_get_insertion_index

Replace text in a specific position with the text.

Source code in presidio_anonymizer/core/text_replace_builder.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
class TextReplaceBuilder:
    """Creates new text according to users request."""

    def __init__(self, original_text: str):
        self.logger = logging.getLogger("presidio-anonymizer")
        self.output_text = original_text
        self.original_text = original_text
        self.text_len = len(original_text)
        self.last_replacement_index = self.text_len

    def get_text_in_position(self, start: int, end: int) -> str:
        """
        Get part of the text inside the original text.

        :param start: start position of inner text
        :param end: end position of inner text
        :return: str - part of the original text
        """
        self.__validate_position_in_text(start, end)
        return self.original_text[start:end]

    def replace_text_get_insertion_index(
        self, replacement_text: str, start: int, end: int
    ) -> int:
        """
        Replace text in a specific position with the text.

        :param replacement_text: new text to replace the old text according to indices
        :param start: the startpoint to replace the text
        :param end: the endpoint to replace the text
        :return: The index of inserted text
        """
        end_of_text_index = min(end, self.last_replacement_index)
        self.last_replacement_index = start

        before_text = self.output_text[:start]
        after_text = self.output_text[end_of_text_index:]
        self.output_text = before_text + replacement_text + after_text

        # The replace algorithm is replacing the text from end to start.
        # calculate and return the start point from the end.
        return len(after_text) + len(replacement_text)

    def __validate_position_in_text(self, start: int, end: int):
        """Validate the start and end position match the text length."""
        if self.text_len < start or end > self.text_len:
            err_msg = (
                f"Invalid analyzer result, start: {start} and end: "
                f"{end}, while text length is only {self.text_len}."
            )
            raise InvalidParamError(err_msg)
get_text_in_position
get_text_in_position(start: int, end: int) -> str

Get part of the text inside the original text.

PARAMETER DESCRIPTION
start

start position of inner text

TYPE: int

end

end position of inner text

TYPE: int

RETURNS DESCRIPTION
str

str - part of the original text

Source code in presidio_anonymizer/core/text_replace_builder.py
18
19
20
21
22
23
24
25
26
27
def get_text_in_position(self, start: int, end: int) -> str:
    """
    Get part of the text inside the original text.

    :param start: start position of inner text
    :param end: end position of inner text
    :return: str - part of the original text
    """
    self.__validate_position_in_text(start, end)
    return self.original_text[start:end]
replace_text_get_insertion_index
replace_text_get_insertion_index(
    replacement_text: str, start: int, end: int
) -> int

Replace text in a specific position with the text.

PARAMETER DESCRIPTION
replacement_text

new text to replace the old text according to indices

TYPE: str

start

the startpoint to replace the text

TYPE: int

end

the endpoint to replace the text

TYPE: int

RETURNS DESCRIPTION
int

The index of inserted text

Source code in presidio_anonymizer/core/text_replace_builder.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def replace_text_get_insertion_index(
    self, replacement_text: str, start: int, end: int
) -> int:
    """
    Replace text in a specific position with the text.

    :param replacement_text: new text to replace the old text according to indices
    :param start: the startpoint to replace the text
    :param end: the endpoint to replace the text
    :return: The index of inserted text
    """
    end_of_text_index = min(end, self.last_replacement_index)
    self.last_replacement_index = start

    before_text = self.output_text[:start]
    after_text = self.output_text[end_of_text_index:]
    self.output_text = before_text + replacement_text + after_text

    # The replace algorithm is replacing the text from end to start.
    # calculate and return the start point from the end.
    return len(after_text) + len(replacement_text)

engine_base

Handle the entire text operations using the operators.

EngineBase

Bases: ABC

Handle the logic of operations over the text using the operators.

Source code in presidio_anonymizer/core/engine_base.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
 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 EngineBase(ABC):
    """Handle the logic of operations over the text using the operators."""

    def __init__(self):
        self.logger = logging.getLogger("presidio-anonymizer")
        self.operators_factory = OperatorsFactory()

    def _operate(
        self,
        text: str,
        pii_entities: List[PIIEntity],
        operators_metadata: Dict[str, OperatorConfig],
        operator_type: OperatorType,
        **operator_kwargs: Dict,
    ) -> EngineResult:
        """
        Operate will do the operations required by the user over the text.

        :param text: the text we need to operate on.
        :param pii_entities: data about the text entities we want to operate over.
        :param operators_metadata: dictionary where the key is the entity_type and what
        :type operator_type: either anonymize or deanonymize
        we want to perform over this entity_type.
        :return:
        """
        text_replace_builder = TextReplaceBuilder(original_text=text)
        engine_result = EngineResult()
        sorted_pii_entities = sorted(pii_entities, reverse=True)
        for entity in sorted_pii_entities:
            text_to_operate_on = text_replace_builder.get_text_in_position(
                entity.start, entity.end
            )

            self.logger.debug(f"performing operation {entity}")
            operator_metadata = self.__get_entity_operator_metadata(
                entity.entity_type, operators_metadata
            )
            changed_text = self.__operate_on_text(
                entity, text_to_operate_on, operator_metadata, operator_type
            )
            index_from_end = text_replace_builder.replace_text_get_insertion_index(
                changed_text, entity.start, entity.end
            )

            # The following creates an intermediate list of result entities,
            # ordered from end to start, and the indexes will be normalized
            # from start to end once the loop ends and the text length is deterministic.
            result_item = OperatorResult(
                0,
                index_from_end,
                entity.entity_type,
                changed_text,
                operator_metadata.operator_name,
            )
            engine_result.add_item(result_item)

        engine_result.set_text(text_replace_builder.output_text)
        engine_result.normalize_item_indexes()
        return engine_result

    def __operate_on_text(
        self,
        text_metadata: PIIEntity,
        text_to_operate_on: str,
        operator_metadata: OperatorConfig,
        operator_type: OperatorType,
    ) -> str:
        entity_type = text_metadata.entity_type
        self.logger.debug(f"getting operator for {entity_type}")
        operator = self.operators_factory.create_operator_class(
            operator_metadata.operator_name, operator_type
        )
        self.logger.debug(f"validating operator {operator} for {entity_type}")
        params = operator_metadata.params
        params["entity_type"] = entity_type

        operator.validate(params=params)

        self.logger.debug(f"operating on {entity_type} with {operator}")
        operated_on_text = operator.operate(params=params, text=text_to_operate_on)

        return operated_on_text

    @staticmethod
    def __get_entity_operator_metadata(
        entity_type: str, operators_metadata: Dict = None
    ) -> OperatorConfig:
        # We try to get the operator from the list by entity_type.
        # If it does not exist, we get the default from the list.
        if operators_metadata is None:
            operators_metadata = {}
        operator = operators_metadata.get(entity_type)
        if operator:
            return operator
        else:
            return operators_metadata.get("DEFAULT")

EngineBase

Bases: ABC

Handle the logic of operations over the text using the operators.

Source code in presidio_anonymizer/core/engine_base.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
 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 EngineBase(ABC):
    """Handle the logic of operations over the text using the operators."""

    def __init__(self):
        self.logger = logging.getLogger("presidio-anonymizer")
        self.operators_factory = OperatorsFactory()

    def _operate(
        self,
        text: str,
        pii_entities: List[PIIEntity],
        operators_metadata: Dict[str, OperatorConfig],
        operator_type: OperatorType,
        **operator_kwargs: Dict,
    ) -> EngineResult:
        """
        Operate will do the operations required by the user over the text.

        :param text: the text we need to operate on.
        :param pii_entities: data about the text entities we want to operate over.
        :param operators_metadata: dictionary where the key is the entity_type and what
        :type operator_type: either anonymize or deanonymize
        we want to perform over this entity_type.
        :return:
        """
        text_replace_builder = TextReplaceBuilder(original_text=text)
        engine_result = EngineResult()
        sorted_pii_entities = sorted(pii_entities, reverse=True)
        for entity in sorted_pii_entities:
            text_to_operate_on = text_replace_builder.get_text_in_position(
                entity.start, entity.end
            )

            self.logger.debug(f"performing operation {entity}")
            operator_metadata = self.__get_entity_operator_metadata(
                entity.entity_type, operators_metadata
            )
            changed_text = self.__operate_on_text(
                entity, text_to_operate_on, operator_metadata, operator_type
            )
            index_from_end = text_replace_builder.replace_text_get_insertion_index(
                changed_text, entity.start, entity.end
            )

            # The following creates an intermediate list of result entities,
            # ordered from end to start, and the indexes will be normalized
            # from start to end once the loop ends and the text length is deterministic.
            result_item = OperatorResult(
                0,
                index_from_end,
                entity.entity_type,
                changed_text,
                operator_metadata.operator_name,
            )
            engine_result.add_item(result_item)

        engine_result.set_text(text_replace_builder.output_text)
        engine_result.normalize_item_indexes()
        return engine_result

    def __operate_on_text(
        self,
        text_metadata: PIIEntity,
        text_to_operate_on: str,
        operator_metadata: OperatorConfig,
        operator_type: OperatorType,
    ) -> str:
        entity_type = text_metadata.entity_type
        self.logger.debug(f"getting operator for {entity_type}")
        operator = self.operators_factory.create_operator_class(
            operator_metadata.operator_name, operator_type
        )
        self.logger.debug(f"validating operator {operator} for {entity_type}")
        params = operator_metadata.params
        params["entity_type"] = entity_type

        operator.validate(params=params)

        self.logger.debug(f"operating on {entity_type} with {operator}")
        operated_on_text = operator.operate(params=params, text=text_to_operate_on)

        return operated_on_text

    @staticmethod
    def __get_entity_operator_metadata(
        entity_type: str, operators_metadata: Dict = None
    ) -> OperatorConfig:
        # We try to get the operator from the list by entity_type.
        # If it does not exist, we get the default from the list.
        if operators_metadata is None:
            operators_metadata = {}
        operator = operators_metadata.get(entity_type)
        if operator:
            return operator
        else:
            return operators_metadata.get("DEFAULT")

TextReplaceBuilder

Creates new text according to users request.

METHOD DESCRIPTION
get_text_in_position

Get part of the text inside the original text.

replace_text_get_insertion_index

Replace text in a specific position with the text.

Source code in presidio_anonymizer/core/text_replace_builder.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
class TextReplaceBuilder:
    """Creates new text according to users request."""

    def __init__(self, original_text: str):
        self.logger = logging.getLogger("presidio-anonymizer")
        self.output_text = original_text
        self.original_text = original_text
        self.text_len = len(original_text)
        self.last_replacement_index = self.text_len

    def get_text_in_position(self, start: int, end: int) -> str:
        """
        Get part of the text inside the original text.

        :param start: start position of inner text
        :param end: end position of inner text
        :return: str - part of the original text
        """
        self.__validate_position_in_text(start, end)
        return self.original_text[start:end]

    def replace_text_get_insertion_index(
        self, replacement_text: str, start: int, end: int
    ) -> int:
        """
        Replace text in a specific position with the text.

        :param replacement_text: new text to replace the old text according to indices
        :param start: the startpoint to replace the text
        :param end: the endpoint to replace the text
        :return: The index of inserted text
        """
        end_of_text_index = min(end, self.last_replacement_index)
        self.last_replacement_index = start

        before_text = self.output_text[:start]
        after_text = self.output_text[end_of_text_index:]
        self.output_text = before_text + replacement_text + after_text

        # The replace algorithm is replacing the text from end to start.
        # calculate and return the start point from the end.
        return len(after_text) + len(replacement_text)

    def __validate_position_in_text(self, start: int, end: int):
        """Validate the start and end position match the text length."""
        if self.text_len < start or end > self.text_len:
            err_msg = (
                f"Invalid analyzer result, start: {start} and end: "
                f"{end}, while text length is only {self.text_len}."
            )
            raise InvalidParamError(err_msg)
get_text_in_position
get_text_in_position(start: int, end: int) -> str

Get part of the text inside the original text.

PARAMETER DESCRIPTION
start

start position of inner text

TYPE: int

end

end position of inner text

TYPE: int

RETURNS DESCRIPTION
str

str - part of the original text

Source code in presidio_anonymizer/core/text_replace_builder.py
18
19
20
21
22
23
24
25
26
27
def get_text_in_position(self, start: int, end: int) -> str:
    """
    Get part of the text inside the original text.

    :param start: start position of inner text
    :param end: end position of inner text
    :return: str - part of the original text
    """
    self.__validate_position_in_text(start, end)
    return self.original_text[start:end]
replace_text_get_insertion_index
replace_text_get_insertion_index(
    replacement_text: str, start: int, end: int
) -> int

Replace text in a specific position with the text.

PARAMETER DESCRIPTION
replacement_text

new text to replace the old text according to indices

TYPE: str

start

the startpoint to replace the text

TYPE: int

end

the endpoint to replace the text

TYPE: int

RETURNS DESCRIPTION
int

The index of inserted text

Source code in presidio_anonymizer/core/text_replace_builder.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def replace_text_get_insertion_index(
    self, replacement_text: str, start: int, end: int
) -> int:
    """
    Replace text in a specific position with the text.

    :param replacement_text: new text to replace the old text according to indices
    :param start: the startpoint to replace the text
    :param end: the endpoint to replace the text
    :return: The index of inserted text
    """
    end_of_text_index = min(end, self.last_replacement_index)
    self.last_replacement_index = start

    before_text = self.output_text[:start]
    after_text = self.output_text[end_of_text_index:]
    self.output_text = before_text + replacement_text + after_text

    # The replace algorithm is replacing the text from end to start.
    # calculate and return the start point from the end.
    return len(after_text) + len(replacement_text)

entities

Handles all the entities objects (structs) of the anonymizer.

invalid_exception

Exception to indicate the request we received is invalid.

InvalidParamError

Bases: Exception

Throw exception with error when user input is not valid.

param msg: Message to be added to the exception

Source code in presidio_anonymizer/entities/invalid_exception.py
 4
 5
 6
 7
 8
 9
10
11
12
class InvalidParamError(Exception):
    """Throw exception with error when user input is not valid.

    param msg: Message to be added to the exception
    """

    def __init__(self, msg: str):
        self.err_msg = msg
        super().__init__(self.err_msg)

conflict_resolution_strategy

An Enum class designed to manage all types of conflicts among entities or text.

ConflictResolutionStrategy

Bases: Enum

Conflict resolution strategy.

The strategy to use when there is a conflict between two entities.

MERGE_SIMILAR_OR_CONTAINED: This default strategy resolves conflicts between similar or contained entities. REMOVE_INTERSECTIONS: Effectively resolves both intersection conflicts among entities and default strategy conflicts. NONE: No conflict resolution will be performed.

Source code in presidio_anonymizer/entities/conflict_resolution_strategy.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class ConflictResolutionStrategy(Enum):
    """Conflict resolution strategy.

    The strategy to use when there is a conflict between two entities.

    MERGE_SIMILAR_OR_CONTAINED: This default strategy resolves conflicts
    between similar or contained entities.
    REMOVE_INTERSECTIONS: Effectively resolves both intersection conflicts
    among entities and default strategy conflicts.
    NONE: No conflict resolution will be performed.
    """

    MERGE_SIMILAR_OR_CONTAINED = "merge_similar_or_contained"
    REMOVE_INTERSECTIONS = "remove_intersections"

engine

Engine request entities.

operator_config
OperatorConfig

Hold the data of the required operator.

METHOD DESCRIPTION
from_json

Create OperatorConfig from json.

Source code in presidio_anonymizer/entities/engine/operator_config.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
class OperatorConfig:
    """Hold the data of the required operator."""

    def __init__(self, operator_name: str, params: Dict = None):
        """
        Create an operator config instance.

        :param operator_name: the name of the operator we want to work with
        :param params: the parameters the operator needs in order to work
        """
        self.operator_name = operator_name
        if not params:
            params = {}
        self.params = params
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return f"operator_name: {self.operator_name}, params: {self.params}"

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

        :param params: json e.g.: {
            "type": "mask",
            "masking_char": "*",
            "chars_to_mask": 4,
            "from_end": true
            }
        :return: OperatorConfig
        """
        operator_name = params.get("type")
        if operator_name:
            params.pop("type")
        return cls(operator_name, params)

    def __eq__(self, other: "OperatorConfig"):
        """Verify two OperatorConfigs are equal."""
        operator_name = self.operator_name == other.operator_name
        return self.params == other.params and operator_name

    def __validate_fields(self):
        validate_parameter_not_empty(
            self.operator_name, "operator config", "operator_name"
        )
from_json classmethod
from_json(params: Dict) -> OperatorConfig

Create OperatorConfig from json.

PARAMETER DESCRIPTION
params

json e.g.: { "type": "mask", "masking_char": "*", "chars_to_mask": 4, "from_end": true }

TYPE: Dict

RETURNS DESCRIPTION
OperatorConfig

OperatorConfig

Source code in presidio_anonymizer/entities/engine/operator_config.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@classmethod
def from_json(cls, params: Dict) -> "OperatorConfig":
    """
    Create OperatorConfig from json.

    :param params: json e.g.: {
        "type": "mask",
        "masking_char": "*",
        "chars_to_mask": 4,
        "from_end": true
        }
    :return: OperatorConfig
    """
    operator_name = params.get("type")
    if operator_name:
        params.pop("type")
    return cls(operator_name, params)
dict_recognizer_result
DictRecognizerResult 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 DictRecognizerResult, if the input is a dictionary. In this case the recognizer_results would be the iterator of the DictRecognizerResult next level in the dictionary.

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

Source code in presidio_anonymizer/entities/engine/dict_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
@dataclass
class DictRecognizerResult:
    """
    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 DictRecognizerResult, if the input is a dictionary.
     In this case the recognizer_results would be the iterator
     of the DictRecognizerResult next level in the dictionary.
    """

    key: str
    value: Union[str, List[str], dict]
    recognizer_results: Union[
        List[RecognizerResult],
        List[List[RecognizerResult]],
        Iterator["DictRecognizerResult"],
    ]
pii_entity
PIIEntity

Bases: ABC

Abstract class to hold the text we are going to operate on metadata.

Source code in presidio_anonymizer/entities/engine/pii_entity.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
class PIIEntity(ABC):
    """Abstract class to hold the text we are going to operate on metadata."""

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

    def __init__(self, start: int, end: int, entity_type: str):
        self.start = start
        self.end = end
        self.entity_type = entity_type
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return (
            f"start: {self.start}"
            f"end: {self.end},"
            f"entity_type: {self.entity_type}"
        )

    def __gt__(self, other):
        """Check one entity is greater then other by the text end index."""
        return self.start > other.start

    def __eq__(self, other):
        """Check two text metadata entities are equal."""
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
        )

    def __validate_fields(self):
        validate_parameter_exists(self.start, "result", "start")
        validate_type(self.start, "start", int)
        validate_parameter_exists(self.end, "result", "end")
        validate_type(self.end, "end", int)
        validate_parameter_not_empty(self.entity_type, "result", "entity_type")
        if self.start < 0 or self.end < 0:
            raise InvalidParamError(
                "Invalid input, result start and end must be positive"
            )
        if self.start > self.end:
            raise InvalidParamError(
                f"Invalid input, start index '{self.start}' "
                f"must be smaller than end index '{self.end}'"
            )
recognizer_result

RecognizerResult is an exact copy of the RecognizerResult object from presidio-analyzer.

Represents the findings of detected entity.

RecognizerResult

Bases: PIIEntity

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

METHOD DESCRIPTION
from_json

Create RecognizerResult from json.

has_conflict

Check if two recognizer results are conflicted or not.

contains

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

equal_indices

Check if the indices are equal between two results.

intersects

Check if self intersects with a different RecognizerResult.

Source code in presidio_anonymizer/entities/engine/recognizer_result.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 RecognizerResult(PIIEntity):
    """
    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
    """

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

    def __init__(self, entity_type: str, start: int, end: int, score: float):
        PIIEntity.__init__(self, start, end, entity_type)
        self.score = score
        validate_parameter_exists(score, "analyzer result", "score")

    @classmethod
    def from_json(cls, data: Dict):
        """
        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 __gt__(self, other):
        """
        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):
        """
        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):
        """
        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)

    def contains(self, other):
        """
        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):
        """
        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 intersects(self, other) -> 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)
from_json classmethod
from_json(data: Dict)

Create RecognizerResult from json.

PARAMETER DESCRIPTION
data

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

TYPE: Dict

RETURNS DESCRIPTION

RecognizerResult

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@classmethod
def from_json(cls, data: Dict):
    """
    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)
has_conflict
has_conflict(other)

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

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def has_conflict(self, other):
    """
    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)
contains
contains(other)

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

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION

bool

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
108
109
110
111
112
113
114
115
def contains(self, other):
    """
    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)

Check if the indices are equal between two results.

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
117
118
119
120
121
122
123
124
def equal_indices(self, other):
    """
    Check if the indices are equal between two results.

    :param other: another RecognizerResult
    :return:
    """
    return self.start == other.start and self.end == other.end
intersects
intersects(other) -> 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_anonymizer/entities/engine/recognizer_result.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def intersects(self, other) -> 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)
result

Engine result items either for anonymize or decrypt.

operator_result
OperatorResult

Bases: PIIEntity

A class to hold data for engines results either anonymize or deanonymize.

METHOD DESCRIPTION
to_dict

Return object as Dict.

from_json

Create OperatorResult from user json.

Source code in presidio_anonymizer/entities/engine/result/operator_result.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
class OperatorResult(PIIEntity):
    """A class to hold data for engines results either anonymize or deanonymize."""

    def __init__(
        self,
        start: int,
        end: int,
        entity_type: str,
        text: str = None,
        operator: str = None,
    ):
        PIIEntity.__init__(self, start, end, entity_type)
        self.text = text
        self.operator = operator

    def __repr__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def to_dict(self) -> Dict:
        """Return object as Dict."""
        return self.__dict__

    def __str__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def __eq__(self, other: "OperatorResult") -> bool:
        """
        Verify two OperatorResults are equal.

        :param other: OperatorResult
        :return: bool
        """
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
            and self.operator == other.operator
            and self.text == other.text
        )

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

        :param json: json representation for this operator result. For example:
        {
            "start": 0,
            "end": 10,
            "key": "1111111111111111",
            "entity_type":"PERSON",
            "text":"resulted_text",
            "operator":"encrypt",
        }
        """
        start = json.get("start")
        end = json.get("end")
        entity_type = json.get("entity_type")
        text = json.get("text")
        operator = json.get("operator")
        return cls(
            start=start,
            end=end,
            entity_type=entity_type,
            text=text,
            operator=operator,
        )
to_dict
to_dict() -> Dict

Return object as Dict.

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
25
26
27
def to_dict(self) -> Dict:
    """Return object as Dict."""
    return self.__dict__
from_json classmethod
from_json(json: Dict) -> OperatorResult

Create OperatorResult from user json.

PARAMETER DESCRIPTION
json

json representation for this operator result. For example: { "start": 0, "end": 10, "key": "1111111111111111", "entity_type":"PERSON", "text":"resulted_text", "operator":"encrypt", }

TYPE: Dict

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
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
@classmethod
def from_json(cls, json: Dict) -> "OperatorResult":
    """
    Create OperatorResult from user json.

    :param json: json representation for this operator result. For example:
    {
        "start": 0,
        "end": 10,
        "key": "1111111111111111",
        "entity_type":"PERSON",
        "text":"resulted_text",
        "operator":"encrypt",
    }
    """
    start = json.get("start")
    end = json.get("end")
    entity_type = json.get("entity_type")
    text = json.get("text")
    operator = json.get("operator")
    return cls(
        start=start,
        end=end,
        entity_type=entity_type,
        text=text,
        operator=operator,
    )
engine_result

Handle a serializable anonymizer result.

EngineResult

Engine result.

METHOD DESCRIPTION
set_text

Set a text.

add_item

Add an item.

normalize_item_indexes

Normalize the indexes to be index from start.

to_json

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.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
class EngineResult:
    """Engine result."""

    def __init__(self, text: str = None, items: List[OperatorResult] = None):
        """Create EngineResult entity.

        :param text: The anonymized text.
        :param items: List of PII entities and the indices
         of their replacements in the anonymized text.
        """
        if items is None:
            items = []
        self.text = text
        self.items = items

    def set_text(self, text: str):
        """Set a text."""
        self.text = text

    def add_item(self, item: OperatorResult):
        """Add an item.

        :param item: an item to add to the list.
        """
        self.items.append(item)

    def normalize_item_indexes(self):
        """Normalize the indexes to be index from start."""
        text_len = len(self.text)
        for result_item in self.items:
            result_item.start = text_len - result_item.end
            result_item.end = result_item.start + len(result_item.text)

    def to_json(self) -> str:
        """Return a json string serializing this instance."""
        return json.dumps(self, default=lambda x: x.__dict__)

    def __repr__(self):
        """Return a string representation of the object."""

        items_repr = (
            ",\n    ".join([str(item) for item in self.items]) if self.items else ""
        )
        return f"text: {self.text}\nitems:\n[\n    {items_repr}\n]\n"

    def __eq__(self, other) -> bool:
        """Verify two instances are equal.

        Returns true if the two instances are equal, false otherwise.
        """
        return self.text == other.text and all(
            map(lambda x, y: x == y, self.items, other.items)
        )
set_text
set_text(text: str)

Set a text.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
24
25
26
def set_text(self, text: str):
    """Set a text."""
    self.text = text
add_item
add_item(item: OperatorResult)

Add an item.

PARAMETER DESCRIPTION
item

an item to add to the list.

TYPE: OperatorResult

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
28
29
30
31
32
33
def add_item(self, item: OperatorResult):
    """Add an item.

    :param item: an item to add to the list.
    """
    self.items.append(item)
normalize_item_indexes
normalize_item_indexes()

Normalize the indexes to be index from start.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
35
36
37
38
39
40
def normalize_item_indexes(self):
    """Normalize the indexes to be index from start."""
    text_len = len(self.text)
    for result_item in self.items:
        result_item.start = text_len - result_item.end
        result_item.end = result_item.start + len(result_item.text)
to_json
to_json() -> str

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
42
43
44
def to_json(self) -> str:
    """Return a json string serializing this instance."""
    return json.dumps(self, default=lambda x: x.__dict__)
OperatorResult

Bases: PIIEntity

A class to hold data for engines results either anonymize or deanonymize.

METHOD DESCRIPTION
to_dict

Return object as Dict.

from_json

Create OperatorResult from user json.

Source code in presidio_anonymizer/entities/engine/result/operator_result.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
class OperatorResult(PIIEntity):
    """A class to hold data for engines results either anonymize or deanonymize."""

    def __init__(
        self,
        start: int,
        end: int,
        entity_type: str,
        text: str = None,
        operator: str = None,
    ):
        PIIEntity.__init__(self, start, end, entity_type)
        self.text = text
        self.operator = operator

    def __repr__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def to_dict(self) -> Dict:
        """Return object as Dict."""
        return self.__dict__

    def __str__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def __eq__(self, other: "OperatorResult") -> bool:
        """
        Verify two OperatorResults are equal.

        :param other: OperatorResult
        :return: bool
        """
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
            and self.operator == other.operator
            and self.text == other.text
        )

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

        :param json: json representation for this operator result. For example:
        {
            "start": 0,
            "end": 10,
            "key": "1111111111111111",
            "entity_type":"PERSON",
            "text":"resulted_text",
            "operator":"encrypt",
        }
        """
        start = json.get("start")
        end = json.get("end")
        entity_type = json.get("entity_type")
        text = json.get("text")
        operator = json.get("operator")
        return cls(
            start=start,
            end=end,
            entity_type=entity_type,
            text=text,
            operator=operator,
        )
to_dict
to_dict() -> Dict

Return object as Dict.

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
25
26
27
def to_dict(self) -> Dict:
    """Return object as Dict."""
    return self.__dict__
from_json classmethod
from_json(json: Dict) -> OperatorResult

Create OperatorResult from user json.

PARAMETER DESCRIPTION
json

json representation for this operator result. For example: { "start": 0, "end": 10, "key": "1111111111111111", "entity_type":"PERSON", "text":"resulted_text", "operator":"encrypt", }

TYPE: Dict

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
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
@classmethod
def from_json(cls, json: Dict) -> "OperatorResult":
    """
    Create OperatorResult from user json.

    :param json: json representation for this operator result. For example:
    {
        "start": 0,
        "end": 10,
        "key": "1111111111111111",
        "entity_type":"PERSON",
        "text":"resulted_text",
        "operator":"encrypt",
    }
    """
    start = json.get("start")
    end = json.get("end")
    entity_type = json.get("entity_type")
    text = json.get("text")
    operator = json.get("operator")
    return cls(
        start=start,
        end=end,
        entity_type=entity_type,
        text=text,
        operator=operator,
    )
EngineResult

Engine result.

METHOD DESCRIPTION
set_text

Set a text.

add_item

Add an item.

normalize_item_indexes

Normalize the indexes to be index from start.

to_json

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.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
class EngineResult:
    """Engine result."""

    def __init__(self, text: str = None, items: List[OperatorResult] = None):
        """Create EngineResult entity.

        :param text: The anonymized text.
        :param items: List of PII entities and the indices
         of their replacements in the anonymized text.
        """
        if items is None:
            items = []
        self.text = text
        self.items = items

    def set_text(self, text: str):
        """Set a text."""
        self.text = text

    def add_item(self, item: OperatorResult):
        """Add an item.

        :param item: an item to add to the list.
        """
        self.items.append(item)

    def normalize_item_indexes(self):
        """Normalize the indexes to be index from start."""
        text_len = len(self.text)
        for result_item in self.items:
            result_item.start = text_len - result_item.end
            result_item.end = result_item.start + len(result_item.text)

    def to_json(self) -> str:
        """Return a json string serializing this instance."""
        return json.dumps(self, default=lambda x: x.__dict__)

    def __repr__(self):
        """Return a string representation of the object."""

        items_repr = (
            ",\n    ".join([str(item) for item in self.items]) if self.items else ""
        )
        return f"text: {self.text}\nitems:\n[\n    {items_repr}\n]\n"

    def __eq__(self, other) -> bool:
        """Verify two instances are equal.

        Returns true if the two instances are equal, false otherwise.
        """
        return self.text == other.text and all(
            map(lambda x, y: x == y, self.items, other.items)
        )
set_text
set_text(text: str)

Set a text.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
24
25
26
def set_text(self, text: str):
    """Set a text."""
    self.text = text
add_item
add_item(item: OperatorResult)

Add an item.

PARAMETER DESCRIPTION
item

an item to add to the list.

TYPE: OperatorResult

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
28
29
30
31
32
33
def add_item(self, item: OperatorResult):
    """Add an item.

    :param item: an item to add to the list.
    """
    self.items.append(item)
normalize_item_indexes
normalize_item_indexes()

Normalize the indexes to be index from start.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
35
36
37
38
39
40
def normalize_item_indexes(self):
    """Normalize the indexes to be index from start."""
    text_len = len(self.text)
    for result_item in self.items:
        result_item.start = text_len - result_item.end
        result_item.end = result_item.start + len(result_item.text)
to_json
to_json() -> str

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
42
43
44
def to_json(self) -> str:
    """Return a json string serializing this instance."""
    return json.dumps(self, default=lambda x: x.__dict__)
OperatorConfig

Hold the data of the required operator.

METHOD DESCRIPTION
from_json

Create OperatorConfig from json.

Source code in presidio_anonymizer/entities/engine/operator_config.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
class OperatorConfig:
    """Hold the data of the required operator."""

    def __init__(self, operator_name: str, params: Dict = None):
        """
        Create an operator config instance.

        :param operator_name: the name of the operator we want to work with
        :param params: the parameters the operator needs in order to work
        """
        self.operator_name = operator_name
        if not params:
            params = {}
        self.params = params
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return f"operator_name: {self.operator_name}, params: {self.params}"

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

        :param params: json e.g.: {
            "type": "mask",
            "masking_char": "*",
            "chars_to_mask": 4,
            "from_end": true
            }
        :return: OperatorConfig
        """
        operator_name = params.get("type")
        if operator_name:
            params.pop("type")
        return cls(operator_name, params)

    def __eq__(self, other: "OperatorConfig"):
        """Verify two OperatorConfigs are equal."""
        operator_name = self.operator_name == other.operator_name
        return self.params == other.params and operator_name

    def __validate_fields(self):
        validate_parameter_not_empty(
            self.operator_name, "operator config", "operator_name"
        )
from_json classmethod
from_json(params: Dict) -> OperatorConfig

Create OperatorConfig from json.

PARAMETER DESCRIPTION
params

json e.g.: { "type": "mask", "masking_char": "*", "chars_to_mask": 4, "from_end": true }

TYPE: Dict

RETURNS DESCRIPTION
OperatorConfig

OperatorConfig

Source code in presidio_anonymizer/entities/engine/operator_config.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@classmethod
def from_json(cls, params: Dict) -> "OperatorConfig":
    """
    Create OperatorConfig from json.

    :param params: json e.g.: {
        "type": "mask",
        "masking_char": "*",
        "chars_to_mask": 4,
        "from_end": true
        }
    :return: OperatorConfig
    """
    operator_name = params.get("type")
    if operator_name:
        params.pop("type")
    return cls(operator_name, params)
PIIEntity

Bases: ABC

Abstract class to hold the text we are going to operate on metadata.

Source code in presidio_anonymizer/entities/engine/pii_entity.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
class PIIEntity(ABC):
    """Abstract class to hold the text we are going to operate on metadata."""

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

    def __init__(self, start: int, end: int, entity_type: str):
        self.start = start
        self.end = end
        self.entity_type = entity_type
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return (
            f"start: {self.start}"
            f"end: {self.end},"
            f"entity_type: {self.entity_type}"
        )

    def __gt__(self, other):
        """Check one entity is greater then other by the text end index."""
        return self.start > other.start

    def __eq__(self, other):
        """Check two text metadata entities are equal."""
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
        )

    def __validate_fields(self):
        validate_parameter_exists(self.start, "result", "start")
        validate_type(self.start, "start", int)
        validate_parameter_exists(self.end, "result", "end")
        validate_type(self.end, "end", int)
        validate_parameter_not_empty(self.entity_type, "result", "entity_type")
        if self.start < 0 or self.end < 0:
            raise InvalidParamError(
                "Invalid input, result start and end must be positive"
            )
        if self.start > self.end:
            raise InvalidParamError(
                f"Invalid input, start index '{self.start}' "
                f"must be smaller than end index '{self.end}'"
            )
RecognizerResult

Bases: PIIEntity

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

METHOD DESCRIPTION
from_json

Create RecognizerResult from json.

has_conflict

Check if two recognizer results are conflicted or not.

contains

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

equal_indices

Check if the indices are equal between two results.

intersects

Check if self intersects with a different RecognizerResult.

Source code in presidio_anonymizer/entities/engine/recognizer_result.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 RecognizerResult(PIIEntity):
    """
    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
    """

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

    def __init__(self, entity_type: str, start: int, end: int, score: float):
        PIIEntity.__init__(self, start, end, entity_type)
        self.score = score
        validate_parameter_exists(score, "analyzer result", "score")

    @classmethod
    def from_json(cls, data: Dict):
        """
        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 __gt__(self, other):
        """
        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):
        """
        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):
        """
        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)

    def contains(self, other):
        """
        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):
        """
        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 intersects(self, other) -> 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)
from_json classmethod
from_json(data: Dict)

Create RecognizerResult from json.

PARAMETER DESCRIPTION
data

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

TYPE: Dict

RETURNS DESCRIPTION

RecognizerResult

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@classmethod
def from_json(cls, data: Dict):
    """
    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)
has_conflict
has_conflict(other)

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

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def has_conflict(self, other):
    """
    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)
contains
contains(other)

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

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION

bool

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
108
109
110
111
112
113
114
115
def contains(self, other):
    """
    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)

Check if the indices are equal between two results.

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
117
118
119
120
121
122
123
124
def equal_indices(self, other):
    """
    Check if the indices are equal between two results.

    :param other: another RecognizerResult
    :return:
    """
    return self.start == other.start and self.end == other.end
intersects
intersects(other) -> 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_anonymizer/entities/engine/recognizer_result.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def intersects(self, other) -> 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)

InvalidParamError

Bases: Exception

Throw exception with error when user input is not valid.

param msg: Message to be added to the exception

Source code in presidio_anonymizer/entities/invalid_exception.py
 4
 5
 6
 7
 8
 9
10
11
12
class InvalidParamError(Exception):
    """Throw exception with error when user input is not valid.

    param msg: Message to be added to the exception
    """

    def __init__(self, msg: str):
        self.err_msg = msg
        super().__init__(self.err_msg)

ConflictResolutionStrategy

Bases: Enum

Conflict resolution strategy.

The strategy to use when there is a conflict between two entities.

MERGE_SIMILAR_OR_CONTAINED: This default strategy resolves conflicts between similar or contained entities. REMOVE_INTERSECTIONS: Effectively resolves both intersection conflicts among entities and default strategy conflicts. NONE: No conflict resolution will be performed.

Source code in presidio_anonymizer/entities/conflict_resolution_strategy.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class ConflictResolutionStrategy(Enum):
    """Conflict resolution strategy.

    The strategy to use when there is a conflict between two entities.

    MERGE_SIMILAR_OR_CONTAINED: This default strategy resolves conflicts
    between similar or contained entities.
    REMOVE_INTERSECTIONS: Effectively resolves both intersection conflicts
    among entities and default strategy conflicts.
    NONE: No conflict resolution will be performed.
    """

    MERGE_SIMILAR_OR_CONTAINED = "merge_similar_or_contained"
    REMOVE_INTERSECTIONS = "remove_intersections"

OperatorConfig

Hold the data of the required operator.

METHOD DESCRIPTION
from_json

Create OperatorConfig from json.

Source code in presidio_anonymizer/entities/engine/operator_config.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
class OperatorConfig:
    """Hold the data of the required operator."""

    def __init__(self, operator_name: str, params: Dict = None):
        """
        Create an operator config instance.

        :param operator_name: the name of the operator we want to work with
        :param params: the parameters the operator needs in order to work
        """
        self.operator_name = operator_name
        if not params:
            params = {}
        self.params = params
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return f"operator_name: {self.operator_name}, params: {self.params}"

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

        :param params: json e.g.: {
            "type": "mask",
            "masking_char": "*",
            "chars_to_mask": 4,
            "from_end": true
            }
        :return: OperatorConfig
        """
        operator_name = params.get("type")
        if operator_name:
            params.pop("type")
        return cls(operator_name, params)

    def __eq__(self, other: "OperatorConfig"):
        """Verify two OperatorConfigs are equal."""
        operator_name = self.operator_name == other.operator_name
        return self.params == other.params and operator_name

    def __validate_fields(self):
        validate_parameter_not_empty(
            self.operator_name, "operator config", "operator_name"
        )
from_json classmethod
from_json(params: Dict) -> OperatorConfig

Create OperatorConfig from json.

PARAMETER DESCRIPTION
params

json e.g.: { "type": "mask", "masking_char": "*", "chars_to_mask": 4, "from_end": true }

TYPE: Dict

RETURNS DESCRIPTION
OperatorConfig

OperatorConfig

Source code in presidio_anonymizer/entities/engine/operator_config.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@classmethod
def from_json(cls, params: Dict) -> "OperatorConfig":
    """
    Create OperatorConfig from json.

    :param params: json e.g.: {
        "type": "mask",
        "masking_char": "*",
        "chars_to_mask": 4,
        "from_end": true
        }
    :return: OperatorConfig
    """
    operator_name = params.get("type")
    if operator_name:
        params.pop("type")
    return cls(operator_name, params)

PIIEntity

Bases: ABC

Abstract class to hold the text we are going to operate on metadata.

Source code in presidio_anonymizer/entities/engine/pii_entity.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
class PIIEntity(ABC):
    """Abstract class to hold the text we are going to operate on metadata."""

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

    def __init__(self, start: int, end: int, entity_type: str):
        self.start = start
        self.end = end
        self.entity_type = entity_type
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return (
            f"start: {self.start}"
            f"end: {self.end},"
            f"entity_type: {self.entity_type}"
        )

    def __gt__(self, other):
        """Check one entity is greater then other by the text end index."""
        return self.start > other.start

    def __eq__(self, other):
        """Check two text metadata entities are equal."""
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
        )

    def __validate_fields(self):
        validate_parameter_exists(self.start, "result", "start")
        validate_type(self.start, "start", int)
        validate_parameter_exists(self.end, "result", "end")
        validate_type(self.end, "end", int)
        validate_parameter_not_empty(self.entity_type, "result", "entity_type")
        if self.start < 0 or self.end < 0:
            raise InvalidParamError(
                "Invalid input, result start and end must be positive"
            )
        if self.start > self.end:
            raise InvalidParamError(
                f"Invalid input, start index '{self.start}' "
                f"must be smaller than end index '{self.end}'"
            )

RecognizerResult

Bases: PIIEntity

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

METHOD DESCRIPTION
from_json

Create RecognizerResult from json.

has_conflict

Check if two recognizer results are conflicted or not.

contains

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

equal_indices

Check if the indices are equal between two results.

intersects

Check if self intersects with a different RecognizerResult.

Source code in presidio_anonymizer/entities/engine/recognizer_result.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 RecognizerResult(PIIEntity):
    """
    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
    """

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

    def __init__(self, entity_type: str, start: int, end: int, score: float):
        PIIEntity.__init__(self, start, end, entity_type)
        self.score = score
        validate_parameter_exists(score, "analyzer result", "score")

    @classmethod
    def from_json(cls, data: Dict):
        """
        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 __gt__(self, other):
        """
        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):
        """
        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):
        """
        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)

    def contains(self, other):
        """
        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):
        """
        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 intersects(self, other) -> 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)
from_json classmethod
from_json(data: Dict)

Create RecognizerResult from json.

PARAMETER DESCRIPTION
data

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

TYPE: Dict

RETURNS DESCRIPTION

RecognizerResult

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@classmethod
def from_json(cls, data: Dict):
    """
    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)
has_conflict
has_conflict(other)

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

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def has_conflict(self, other):
    """
    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)
contains
contains(other)

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

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION

bool

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
108
109
110
111
112
113
114
115
def contains(self, other):
    """
    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)

Check if the indices are equal between two results.

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
117
118
119
120
121
122
123
124
def equal_indices(self, other):
    """
    Check if the indices are equal between two results.

    :param other: another RecognizerResult
    :return:
    """
    return self.start == other.start and self.end == other.end
intersects
intersects(other) -> 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_anonymizer/entities/engine/recognizer_result.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def intersects(self, other) -> 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)

EngineResult

Engine result.

METHOD DESCRIPTION
set_text

Set a text.

add_item

Add an item.

normalize_item_indexes

Normalize the indexes to be index from start.

to_json

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.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
class EngineResult:
    """Engine result."""

    def __init__(self, text: str = None, items: List[OperatorResult] = None):
        """Create EngineResult entity.

        :param text: The anonymized text.
        :param items: List of PII entities and the indices
         of their replacements in the anonymized text.
        """
        if items is None:
            items = []
        self.text = text
        self.items = items

    def set_text(self, text: str):
        """Set a text."""
        self.text = text

    def add_item(self, item: OperatorResult):
        """Add an item.

        :param item: an item to add to the list.
        """
        self.items.append(item)

    def normalize_item_indexes(self):
        """Normalize the indexes to be index from start."""
        text_len = len(self.text)
        for result_item in self.items:
            result_item.start = text_len - result_item.end
            result_item.end = result_item.start + len(result_item.text)

    def to_json(self) -> str:
        """Return a json string serializing this instance."""
        return json.dumps(self, default=lambda x: x.__dict__)

    def __repr__(self):
        """Return a string representation of the object."""

        items_repr = (
            ",\n    ".join([str(item) for item in self.items]) if self.items else ""
        )
        return f"text: {self.text}\nitems:\n[\n    {items_repr}\n]\n"

    def __eq__(self, other) -> bool:
        """Verify two instances are equal.

        Returns true if the two instances are equal, false otherwise.
        """
        return self.text == other.text and all(
            map(lambda x, y: x == y, self.items, other.items)
        )
set_text
set_text(text: str)

Set a text.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
24
25
26
def set_text(self, text: str):
    """Set a text."""
    self.text = text
add_item
add_item(item: OperatorResult)

Add an item.

PARAMETER DESCRIPTION
item

an item to add to the list.

TYPE: OperatorResult

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
28
29
30
31
32
33
def add_item(self, item: OperatorResult):
    """Add an item.

    :param item: an item to add to the list.
    """
    self.items.append(item)
normalize_item_indexes
normalize_item_indexes()

Normalize the indexes to be index from start.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
35
36
37
38
39
40
def normalize_item_indexes(self):
    """Normalize the indexes to be index from start."""
    text_len = len(self.text)
    for result_item in self.items:
        result_item.start = text_len - result_item.end
        result_item.end = result_item.start + len(result_item.text)
to_json
to_json() -> str

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
42
43
44
def to_json(self) -> str:
    """Return a json string serializing this instance."""
    return json.dumps(self, default=lambda x: x.__dict__)

OperatorResult

Bases: PIIEntity

A class to hold data for engines results either anonymize or deanonymize.

METHOD DESCRIPTION
to_dict

Return object as Dict.

from_json

Create OperatorResult from user json.

Source code in presidio_anonymizer/entities/engine/result/operator_result.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
class OperatorResult(PIIEntity):
    """A class to hold data for engines results either anonymize or deanonymize."""

    def __init__(
        self,
        start: int,
        end: int,
        entity_type: str,
        text: str = None,
        operator: str = None,
    ):
        PIIEntity.__init__(self, start, end, entity_type)
        self.text = text
        self.operator = operator

    def __repr__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def to_dict(self) -> Dict:
        """Return object as Dict."""
        return self.__dict__

    def __str__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def __eq__(self, other: "OperatorResult") -> bool:
        """
        Verify two OperatorResults are equal.

        :param other: OperatorResult
        :return: bool
        """
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
            and self.operator == other.operator
            and self.text == other.text
        )

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

        :param json: json representation for this operator result. For example:
        {
            "start": 0,
            "end": 10,
            "key": "1111111111111111",
            "entity_type":"PERSON",
            "text":"resulted_text",
            "operator":"encrypt",
        }
        """
        start = json.get("start")
        end = json.get("end")
        entity_type = json.get("entity_type")
        text = json.get("text")
        operator = json.get("operator")
        return cls(
            start=start,
            end=end,
            entity_type=entity_type,
            text=text,
            operator=operator,
        )
to_dict
to_dict() -> Dict

Return object as Dict.

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
25
26
27
def to_dict(self) -> Dict:
    """Return object as Dict."""
    return self.__dict__
from_json classmethod
from_json(json: Dict) -> OperatorResult

Create OperatorResult from user json.

PARAMETER DESCRIPTION
json

json representation for this operator result. For example: { "start": 0, "end": 10, "key": "1111111111111111", "entity_type":"PERSON", "text":"resulted_text", "operator":"encrypt", }

TYPE: Dict

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
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
@classmethod
def from_json(cls, json: Dict) -> "OperatorResult":
    """
    Create OperatorResult from user json.

    :param json: json representation for this operator result. For example:
    {
        "start": 0,
        "end": 10,
        "key": "1111111111111111",
        "entity_type":"PERSON",
        "text":"resulted_text",
        "operator":"encrypt",
    }
    """
    start = json.get("start")
    end = json.get("end")
    entity_type = json.get("entity_type")
    text = json.get("text")
    operator = json.get("operator")
    return cls(
        start=start,
        end=end,
        entity_type=entity_type,
        text=text,
        operator=operator,
    )

DictRecognizerResult 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 DictRecognizerResult, if the input is a dictionary. In this case the recognizer_results would be the iterator of the DictRecognizerResult next level in the dictionary.

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

Source code in presidio_anonymizer/entities/engine/dict_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
@dataclass
class DictRecognizerResult:
    """
    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 DictRecognizerResult, if the input is a dictionary.
     In this case the recognizer_results would be the iterator
     of the DictRecognizerResult next level in the dictionary.
    """

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

AnonymizerEngine

Bases: EngineBase

AnonymizerEngine class.

Handles the entire logic of the Presidio-anonymizer. Gets the original text and replaces the PII entities with the desired anonymizers.

METHOD DESCRIPTION
anonymize

Anonymize method to anonymize the given text.

add_anonymizer

Add a new anonymizer to the engine.

remove_anonymizer

Remove an anonymizer from the engine.

get_anonymizers

Return a list of supported anonymizers.

Source code in presidio_anonymizer/anonymizer_engine.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
242
243
244
class AnonymizerEngine(EngineBase):
    """
    AnonymizerEngine class.

    Handles the entire logic of the Presidio-anonymizer. Gets the original text
    and replaces the PII entities with the desired anonymizers.
    """

    def anonymize(
        self,
        text: str,
        analyzer_results: List[RecognizerResult],
        operators: Optional[Dict[str, OperatorConfig]] = None,
        conflict_resolution: ConflictResolutionStrategy = (
            ConflictResolutionStrategy.MERGE_SIMILAR_OR_CONTAINED
        ),
    ) -> EngineResult:
        """Anonymize method to anonymize the given text.

        :param text: the text we are anonymizing
        :param analyzer_results: A list of RecognizerResult class -> The results we
        received from the analyzer
        :param operators: The configuration of the anonymizers we would like
        to use for each entity e.g.: {"PHONE_NUMBER":OperatorConfig("redact", {})}
        received from the analyzer
        :param conflict_resolution: The configuration designed to handle conflicts
        among entities
        :return: the anonymized text and a list of information about the
        anonymized entities.

        :example:

        >>> from presidio_anonymizer import AnonymizerEngine
        >>> from presidio_anonymizer.entities import RecognizerResult, OperatorConfig

        >>> # Initialize the engine with logger.
        >>> engine = AnonymizerEngine()

        >>> # Invoke the anonymize function with the text, analyzer results and
        >>> # Operators to define the anonymization type.
        >>> result = engine.anonymize(
        >>>     text="My name is Bond, James Bond",
        >>>     analyzer_results=[RecognizerResult(entity_type="PERSON",
        >>>                                        start=11,
        >>>                                        end=15,
        >>>                                        score=0.8),
        >>>                       RecognizerResult(entity_type="PERSON",
        >>>                                        start=17,
        >>>                                        end=27,
        >>>                                        score=0.8)],
        >>>     operators={"PERSON": OperatorConfig("replace", {"new_value": "BIP"})}
        >>> )

        >>> print(result)
        text: My name is BIP, BIP.
        items:
        [
            {'start': 16, 'end': 19, 'entity_type': 'PERSON',
             'text': 'BIP', 'operator': 'replace'},
            {'start': 11, 'end': 14, 'entity_type': 'PERSON',
             'text': 'BIP', 'operator': 'replace'}
        ]


        """
        analyzer_results = self._remove_conflicts_and_get_text_manipulation_data(
            analyzer_results, conflict_resolution
        )

        merged_results = self._merge_entities_with_whitespace_between(
            text, analyzer_results
        )

        operators = self.__check_or_add_default_operator(operators)

        return self._operate(
            text=text,
            pii_entities=merged_results,
            operators_metadata=operators,
            operator_type=OperatorType.Anonymize,
        )

    def add_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
        """
        Add a new anonymizer to the engine.

        anonymizer_cls: The anonymizer class to add to the engine.
        """
        logger.info(f"Added anonymizer {anonymizer_cls.__name__}")
        self.operators_factory.add_anonymize_operator(anonymizer_cls)

    def remove_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
        """
        Remove an anonymizer from the engine.

        anonymizer_cls: The anonymizer class to remove from the engine.
        """
        logger.info(f"Removed anonymizer {anonymizer_cls.__name__}")
        self.operators_factory.remove_anonymize_operator(anonymizer_cls)

    def _remove_conflicts_and_get_text_manipulation_data(
        self,
        analyzer_results: List[RecognizerResult],
        conflict_resolution: ConflictResolutionStrategy,
    ) -> List[RecognizerResult]:
        """
        Iterate the list and create a sorted unique results list from it.

        Only insert results which are:
        1. Indices are not contained in other result.
        2. Have the same indices as other results but with larger score.
        :return: List
        """
        tmp_analyzer_results = []
        # This list contains all elements which we need to check a single result
        # against. If a result is dropped, it can also be dropped from this list
        # since it is intersecting with another result and we selected the other one.
        other_elements = analyzer_results.copy()
        for result in analyzer_results:
            other_elements.remove(result)

            is_merge_same_entity_type = False
            for other_element in other_elements:
                if other_element.entity_type != result.entity_type:
                    continue
                if result.intersects(other_element) == 0:
                    continue

                other_element.start = min(result.start, other_element.start)
                other_element.end = max(result.end, other_element.end)
                other_element.score = max(result.score, other_element.score)
                is_merge_same_entity_type = True
                break
            if not is_merge_same_entity_type:
                other_elements.append(result)
                tmp_analyzer_results.append(result)
            else:
                self.logger.debug(
                    f"removing element {result} from " f"results list due to merge"
                )

        unique_text_metadata_elements = []
        # This list contains all elements which we need to check a single result
        # against. If a result is dropped, it can also be dropped from this list
        # since it is intersecting with another result and we selected the other one.
        other_elements = tmp_analyzer_results.copy()
        for result in tmp_analyzer_results:
            other_elements.remove(result)
            result_conflicted = self.__is_result_conflicted_with_other_elements(
                other_elements, result
            )
            if not result_conflicted:
                other_elements.append(result)
                unique_text_metadata_elements.append(result)
            else:
                self.logger.debug(
                    f"removing element {result} from results list due to conflict"
                )

        # This further improves the quality of handling the conflict between the
        # various entities overlapping. This will not drop the results insted
        # it adjust the start and end positions of overlapping results and removes
        # All types of conflicts among entities as well as text.
        if conflict_resolution == ConflictResolutionStrategy.REMOVE_INTERSECTIONS:
            unique_text_metadata_elements.sort(key=lambda element: element.start)
            elements_length = len(unique_text_metadata_elements)
            index = 0
            while index < elements_length - 1:
                current_entity = unique_text_metadata_elements[index]
                next_entity = unique_text_metadata_elements[index + 1]
                if current_entity.end <= next_entity.start:
                    index += 1
                else:
                    if current_entity.score >= next_entity.score:
                        next_entity.start = current_entity.end
                    else:
                        current_entity.end = next_entity.start
                    unique_text_metadata_elements.sort(
                        key=lambda element: element.start
                    )
            unique_text_metadata_elements = [
                element
                for element in unique_text_metadata_elements
                if element.start <= element.end
            ]
        return unique_text_metadata_elements

    def _merge_entities_with_whitespace_between(
        self, text: str, analyzer_results: List[RecognizerResult]
    ) -> List[RecognizerResult]:
        """Merge adjacent entities of the same type separated by whitespace."""
        merged_results = []
        prev_result = None
        for result in analyzer_results:
            if prev_result is not None:
                if prev_result.entity_type == result.entity_type:
                    if re.search(r"^( )+$", text[prev_result.end : result.start]):
                        merged_results.remove(prev_result)
                        result.start = prev_result.start
            merged_results.append(result)
            prev_result = result
        return merged_results

    def get_anonymizers(self) -> List[str]:
        """Return a list of supported anonymizers."""
        names = [p for p in self.operators_factory.get_anonymizers().keys()]
        return names

    @staticmethod
    def __is_result_conflicted_with_other_elements(other_elements, result):
        return any(
            [result.has_conflict(other_element) for other_element in other_elements]
        )

    @staticmethod
    def __check_or_add_default_operator(
        operators: Dict[str, OperatorConfig],
    ) -> Dict[str, OperatorConfig]:
        default_operator = OperatorConfig(DEFAULT)
        if not operators:
            return {"DEFAULT": default_operator}
        if not operators.get("DEFAULT"):
            operators["DEFAULT"] = default_operator
        return operators

anonymize

anonymize(
    text: str,
    analyzer_results: List[RecognizerResult],
    operators: Optional[Dict[str, OperatorConfig]] = None,
    conflict_resolution: ConflictResolutionStrategy = ConflictResolutionStrategy.MERGE_SIMILAR_OR_CONTAINED,
) -> EngineResult

Anonymize method to anonymize the given text.

:example:

from presidio_anonymizer import AnonymizerEngine from presidio_anonymizer.entities import RecognizerResult, OperatorConfig

Initialize the engine with logger.

engine = AnonymizerEngine()

Invoke the anonymize function with the text, analyzer results and
Operators to define the anonymization type.

result = engine.anonymize( text="My name is Bond, James Bond", analyzer_results=[RecognizerResult(entity_type="PERSON", start=11, end=15, score=0.8), RecognizerResult(entity_type="PERSON", start=17, end=27, score=0.8)], operators={"PERSON": OperatorConfig("replace", {"new_value": "BIP"})} )

print(result) text: My name is BIP, BIP. items: [ {'start': 16, 'end': 19, 'entity_type': 'PERSON', 'text': 'BIP', 'operator': 'replace'}, {'start': 11, 'end': 14, 'entity_type': 'PERSON', 'text': 'BIP', 'operator': 'replace'} ]

PARAMETER DESCRIPTION
text

the text we are anonymizing

TYPE: str

analyzer_results

A list of RecognizerResult class -> The results we received from the analyzer

TYPE: List[RecognizerResult]

operators

The configuration of the anonymizers we would like to use for each entity e.g.: {"PHONE_NUMBER":OperatorConfig("redact", {})} received from the analyzer

TYPE: Optional[Dict[str, OperatorConfig]] DEFAULT: None

conflict_resolution

The configuration designed to handle conflicts among entities

TYPE: ConflictResolutionStrategy DEFAULT: MERGE_SIMILAR_OR_CONTAINED

RETURNS DESCRIPTION
EngineResult

the anonymized text and a list of information about the anonymized entities.

Source code in presidio_anonymizer/anonymizer_engine.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
def anonymize(
    self,
    text: str,
    analyzer_results: List[RecognizerResult],
    operators: Optional[Dict[str, OperatorConfig]] = None,
    conflict_resolution: ConflictResolutionStrategy = (
        ConflictResolutionStrategy.MERGE_SIMILAR_OR_CONTAINED
    ),
) -> EngineResult:
    """Anonymize method to anonymize the given text.

    :param text: the text we are anonymizing
    :param analyzer_results: A list of RecognizerResult class -> The results we
    received from the analyzer
    :param operators: The configuration of the anonymizers we would like
    to use for each entity e.g.: {"PHONE_NUMBER":OperatorConfig("redact", {})}
    received from the analyzer
    :param conflict_resolution: The configuration designed to handle conflicts
    among entities
    :return: the anonymized text and a list of information about the
    anonymized entities.

    :example:

    >>> from presidio_anonymizer import AnonymizerEngine
    >>> from presidio_anonymizer.entities import RecognizerResult, OperatorConfig

    >>> # Initialize the engine with logger.
    >>> engine = AnonymizerEngine()

    >>> # Invoke the anonymize function with the text, analyzer results and
    >>> # Operators to define the anonymization type.
    >>> result = engine.anonymize(
    >>>     text="My name is Bond, James Bond",
    >>>     analyzer_results=[RecognizerResult(entity_type="PERSON",
    >>>                                        start=11,
    >>>                                        end=15,
    >>>                                        score=0.8),
    >>>                       RecognizerResult(entity_type="PERSON",
    >>>                                        start=17,
    >>>                                        end=27,
    >>>                                        score=0.8)],
    >>>     operators={"PERSON": OperatorConfig("replace", {"new_value": "BIP"})}
    >>> )

    >>> print(result)
    text: My name is BIP, BIP.
    items:
    [
        {'start': 16, 'end': 19, 'entity_type': 'PERSON',
         'text': 'BIP', 'operator': 'replace'},
        {'start': 11, 'end': 14, 'entity_type': 'PERSON',
         'text': 'BIP', 'operator': 'replace'}
    ]


    """
    analyzer_results = self._remove_conflicts_and_get_text_manipulation_data(
        analyzer_results, conflict_resolution
    )

    merged_results = self._merge_entities_with_whitespace_between(
        text, analyzer_results
    )

    operators = self.__check_or_add_default_operator(operators)

    return self._operate(
        text=text,
        pii_entities=merged_results,
        operators_metadata=operators,
        operator_type=OperatorType.Anonymize,
    )

add_anonymizer

add_anonymizer(anonymizer_cls: Type[Operator]) -> None

Add a new anonymizer to the engine.

anonymizer_cls: The anonymizer class to add to the engine.

Source code in presidio_anonymizer/anonymizer_engine.py
103
104
105
106
107
108
109
110
def add_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
    """
    Add a new anonymizer to the engine.

    anonymizer_cls: The anonymizer class to add to the engine.
    """
    logger.info(f"Added anonymizer {anonymizer_cls.__name__}")
    self.operators_factory.add_anonymize_operator(anonymizer_cls)

remove_anonymizer

remove_anonymizer(anonymizer_cls: Type[Operator]) -> None

Remove an anonymizer from the engine.

anonymizer_cls: The anonymizer class to remove from the engine.

Source code in presidio_anonymizer/anonymizer_engine.py
112
113
114
115
116
117
118
119
def remove_anonymizer(self, anonymizer_cls: Type[Operator]) -> None:
    """
    Remove an anonymizer from the engine.

    anonymizer_cls: The anonymizer class to remove from the engine.
    """
    logger.info(f"Removed anonymizer {anonymizer_cls.__name__}")
    self.operators_factory.remove_anonymize_operator(anonymizer_cls)

get_anonymizers

get_anonymizers() -> List[str]

Return a list of supported anonymizers.

Source code in presidio_anonymizer/anonymizer_engine.py
224
225
226
227
def get_anonymizers(self) -> List[str]:
    """Return a list of supported anonymizers."""
    names = [p for p in self.operators_factory.get_anonymizers().keys()]
    return names

BatchAnonymizerEngine

BatchAnonymizerEngine class.

A class that provides functionality to anonymize in batches.

PARAMETER DESCRIPTION
anonymizer_engine

An instance of the AnonymizerEngine class.

TYPE: Optional[AnonymizerEngine] DEFAULT: None

METHOD DESCRIPTION
anonymize_list

Anonymize a list of strings.

anonymize_dict

Anonymize values in a dictionary.

Source code in presidio_anonymizer/batch_anonymizer_engine.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
class BatchAnonymizerEngine:
    """
    BatchAnonymizerEngine class.

    A class that provides functionality to anonymize in batches.
    :param anonymizer_engine: An instance of the AnonymizerEngine class.
    """

    def __init__(self, anonymizer_engine: Optional[AnonymizerEngine] = None):
        self.anonymizer_engine = anonymizer_engine or AnonymizerEngine()

    def anonymize_list(
        self,
        texts: List[Optional[Union[str, bool, int, float]]],
        recognizer_results_list: List[List[RecognizerResult]],
        **kwargs,
    ) -> List[Union[str, Any]]:
        """
        Anonymize a list of strings.

        :param texts: List containing the texts to be anonymized (original texts).
            Items with a `type` not in `(str, bool, int, float)` will not be anonymized.
        :param recognizer_results_list: A list of lists of RecognizerResult,
        the output of the AnalyzerEngine on each text in the list.
        :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
        """
        return_list = []
        if not recognizer_results_list:
            recognizer_results_list = [[] for _ in range(len(texts))]
        for text, recognizer_results in zip(texts, recognizer_results_list):
            if type(text) in (str, bool, int, float):
                res = self.anonymizer_engine.anonymize(
                    text=str(text), analyzer_results=recognizer_results, **kwargs
                )
                return_list.append(res.text)
            else:
                return_list.append(text)

        return return_list

    def anonymize_dict(
        self, analyzer_results: Iterable[DictRecognizerResult], **kwargs
    ) -> Dict[str, str]:
        """
        Anonymize values in a dictionary.

        :param analyzer_results: Iterator of `DictRecognizerResult`
        containing the output of the AnalyzerEngine.analyze_dict on the input text.
        :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
        """

        return_dict = {}
        for result in analyzer_results:
            if isinstance(result.value, dict):
                resp = self.anonymize_dict(
                    analyzer_results=result.recognizer_results, **kwargs
                )
                return_dict[result.key] = resp

            elif isinstance(result.value, str):
                resp = self.anonymizer_engine.anonymize(
                    text=result.value,
                    analyzer_results=result.recognizer_results,
                    **kwargs,
                )
                return_dict[result.key] = resp.text

            elif isinstance(result.value, collections.abc.Iterable):
                anonymize_response = self.anonymize_list(
                    texts=result.value,
                    recognizer_results_list=result.recognizer_results,
                    **kwargs,
                )
                return_dict[result.key] = anonymize_response
            else:
                return_dict[result.key] = result.value
        return return_dict

anonymize_list

anonymize_list(
    texts: List[Optional[Union[str, bool, int, float]]],
    recognizer_results_list: List[List[RecognizerResult]],
    **kwargs
) -> List[Union[str, Any]]

Anonymize a list of strings.

PARAMETER DESCRIPTION
texts

List containing the texts to be anonymized (original texts). Items with a type not in (str, bool, int, float) will not be anonymized.

TYPE: List[Optional[Union[str, bool, int, float]]]

recognizer_results_list

A list of lists of RecognizerResult, the output of the AnalyzerEngine on each text in the list.

TYPE: List[List[RecognizerResult]]

kwargs

Additional kwargs for the AnonymizerEngine.anonymize method

DEFAULT: {}

Source code in presidio_anonymizer/batch_anonymizer_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
def anonymize_list(
    self,
    texts: List[Optional[Union[str, bool, int, float]]],
    recognizer_results_list: List[List[RecognizerResult]],
    **kwargs,
) -> List[Union[str, Any]]:
    """
    Anonymize a list of strings.

    :param texts: List containing the texts to be anonymized (original texts).
        Items with a `type` not in `(str, bool, int, float)` will not be anonymized.
    :param recognizer_results_list: A list of lists of RecognizerResult,
    the output of the AnalyzerEngine on each text in the list.
    :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
    """
    return_list = []
    if not recognizer_results_list:
        recognizer_results_list = [[] for _ in range(len(texts))]
    for text, recognizer_results in zip(texts, recognizer_results_list):
        if type(text) in (str, bool, int, float):
            res = self.anonymizer_engine.anonymize(
                text=str(text), analyzer_results=recognizer_results, **kwargs
            )
            return_list.append(res.text)
        else:
            return_list.append(text)

    return return_list

anonymize_dict

anonymize_dict(
    analyzer_results: Iterable[DictRecognizerResult], **kwargs
) -> Dict[str, str]

Anonymize values in a dictionary.

PARAMETER DESCRIPTION
analyzer_results

Iterator of DictRecognizerResult containing the output of the AnalyzerEngine.analyze_dict on the input text.

TYPE: Iterable[DictRecognizerResult]

kwargs

Additional kwargs for the AnonymizerEngine.anonymize method

DEFAULT: {}

Source code in presidio_anonymizer/batch_anonymizer_engine.py
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
def anonymize_dict(
    self, analyzer_results: Iterable[DictRecognizerResult], **kwargs
) -> Dict[str, str]:
    """
    Anonymize values in a dictionary.

    :param analyzer_results: Iterator of `DictRecognizerResult`
    containing the output of the AnalyzerEngine.analyze_dict on the input text.
    :param kwargs: Additional kwargs for the `AnonymizerEngine.anonymize` method
    """

    return_dict = {}
    for result in analyzer_results:
        if isinstance(result.value, dict):
            resp = self.anonymize_dict(
                analyzer_results=result.recognizer_results, **kwargs
            )
            return_dict[result.key] = resp

        elif isinstance(result.value, str):
            resp = self.anonymizer_engine.anonymize(
                text=result.value,
                analyzer_results=result.recognizer_results,
                **kwargs,
            )
            return_dict[result.key] = resp.text

        elif isinstance(result.value, collections.abc.Iterable):
            anonymize_response = self.anonymize_list(
                texts=result.value,
                recognizer_results_list=result.recognizer_results,
                **kwargs,
            )
            return_dict[result.key] = anonymize_response
        else:
            return_dict[result.key] = result.value
    return return_dict

DeanonymizeEngine

Bases: EngineBase

Deanonymize text that was previously anonymized.

METHOD DESCRIPTION
deanonymize

Receive the text, entities and operators to perform deanonymization over.

get_deanonymizers

Return a list of supported deanonymizers.

add_deanonymizer

Add a new deanonymizer to the engine.

remove_deanonymizer

Remove a deanonymizer from the engine.

Source code in presidio_anonymizer/deanonymize_engine.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
class DeanonymizeEngine(EngineBase):
    """Deanonymize text that was previously anonymized."""

    def deanonymize(
        self,
        text: str,
        entities: List[OperatorResult],
        operators: Dict[str, OperatorConfig],
    ) -> EngineResult:
        """
        Receive the text, entities and operators to perform deanonymization over.

        :param operators: the operators to apply on the anonymizer result entities
        :param text: the full text with the encrypted entities
        :param entities: list of encrypted entities
        :return: EngineResult - the new text and data about the deanonymized entities.
        """
        return self._operate(text, entities, operators, OperatorType.Deanonymize)

    def get_deanonymizers(self) -> List[str]:
        """Return a list of supported deanonymizers."""
        names = [p for p in self.operators_factory.get_deanonymizers().keys()]
        return names

    def add_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
        """
        Add a new deanonymizer to the engine.

        anonymizer_cls: The deanonymizer class to add to the engine.
        """
        logger.info(f"Added deanonymizer {deanonymizer_cls.__name__}")
        self.operators_factory.add_deanonymize_operator(deanonymizer_cls)

    def remove_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
        """
        Remove a deanonymizer from the engine.

        deanonymizer_cls: The deanonymizer class to remove from the engine.
        """
        logger.info(f"Removed deanonymizer {deanonymizer_cls.__name__}")
        self.operators_factory.remove_deanonymize_operator(deanonymizer_cls)

deanonymize

deanonymize(
    text: str,
    entities: List[OperatorResult],
    operators: Dict[str, OperatorConfig],
) -> EngineResult

Receive the text, entities and operators to perform deanonymization over.

PARAMETER DESCRIPTION
operators

the operators to apply on the anonymizer result entities

TYPE: Dict[str, OperatorConfig]

text

the full text with the encrypted entities

TYPE: str

entities

list of encrypted entities

TYPE: List[OperatorResult]

RETURNS DESCRIPTION
EngineResult

EngineResult - the new text and data about the deanonymized entities.

Source code in presidio_anonymizer/deanonymize_engine.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def deanonymize(
    self,
    text: str,
    entities: List[OperatorResult],
    operators: Dict[str, OperatorConfig],
) -> EngineResult:
    """
    Receive the text, entities and operators to perform deanonymization over.

    :param operators: the operators to apply on the anonymizer result entities
    :param text: the full text with the encrypted entities
    :param entities: list of encrypted entities
    :return: EngineResult - the new text and data about the deanonymized entities.
    """
    return self._operate(text, entities, operators, OperatorType.Deanonymize)

get_deanonymizers

get_deanonymizers() -> List[str]

Return a list of supported deanonymizers.

Source code in presidio_anonymizer/deanonymize_engine.py
32
33
34
35
def get_deanonymizers(self) -> List[str]:
    """Return a list of supported deanonymizers."""
    names = [p for p in self.operators_factory.get_deanonymizers().keys()]
    return names

add_deanonymizer

add_deanonymizer(deanonymizer_cls: Type[Operator]) -> None

Add a new deanonymizer to the engine.

anonymizer_cls: The deanonymizer class to add to the engine.

Source code in presidio_anonymizer/deanonymize_engine.py
37
38
39
40
41
42
43
44
def add_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
    """
    Add a new deanonymizer to the engine.

    anonymizer_cls: The deanonymizer class to add to the engine.
    """
    logger.info(f"Added deanonymizer {deanonymizer_cls.__name__}")
    self.operators_factory.add_deanonymize_operator(deanonymizer_cls)

remove_deanonymizer

remove_deanonymizer(deanonymizer_cls: Type[Operator]) -> None

Remove a deanonymizer from the engine.

deanonymizer_cls: The deanonymizer class to remove from the engine.

Source code in presidio_anonymizer/deanonymize_engine.py
46
47
48
49
50
51
52
53
def remove_deanonymizer(self, deanonymizer_cls: Type[Operator]) -> None:
    """
    Remove a deanonymizer from the engine.

    deanonymizer_cls: The deanonymizer class to remove from the engine.
    """
    logger.info(f"Removed deanonymizer {deanonymizer_cls.__name__}")
    self.operators_factory.remove_deanonymize_operator(deanonymizer_cls)

ConflictResolutionStrategy

Bases: Enum

Conflict resolution strategy.

The strategy to use when there is a conflict between two entities.

MERGE_SIMILAR_OR_CONTAINED: This default strategy resolves conflicts between similar or contained entities. REMOVE_INTERSECTIONS: Effectively resolves both intersection conflicts among entities and default strategy conflicts. NONE: No conflict resolution will be performed.

Source code in presidio_anonymizer/entities/conflict_resolution_strategy.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class ConflictResolutionStrategy(Enum):
    """Conflict resolution strategy.

    The strategy to use when there is a conflict between two entities.

    MERGE_SIMILAR_OR_CONTAINED: This default strategy resolves conflicts
    between similar or contained entities.
    REMOVE_INTERSECTIONS: Effectively resolves both intersection conflicts
    among entities and default strategy conflicts.
    NONE: No conflict resolution will be performed.
    """

    MERGE_SIMILAR_OR_CONTAINED = "merge_similar_or_contained"
    REMOVE_INTERSECTIONS = "remove_intersections"

DictRecognizerResult 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 DictRecognizerResult, if the input is a dictionary. In this case the recognizer_results would be the iterator of the DictRecognizerResult next level in the dictionary.

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

Source code in presidio_anonymizer/entities/engine/dict_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
@dataclass
class DictRecognizerResult:
    """
    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 DictRecognizerResult, if the input is a dictionary.
     In this case the recognizer_results would be the iterator
     of the DictRecognizerResult next level in the dictionary.
    """

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

EngineResult

Engine result.

METHOD DESCRIPTION
set_text

Set a text.

add_item

Add an item.

normalize_item_indexes

Normalize the indexes to be index from start.

to_json

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.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
class EngineResult:
    """Engine result."""

    def __init__(self, text: str = None, items: List[OperatorResult] = None):
        """Create EngineResult entity.

        :param text: The anonymized text.
        :param items: List of PII entities and the indices
         of their replacements in the anonymized text.
        """
        if items is None:
            items = []
        self.text = text
        self.items = items

    def set_text(self, text: str):
        """Set a text."""
        self.text = text

    def add_item(self, item: OperatorResult):
        """Add an item.

        :param item: an item to add to the list.
        """
        self.items.append(item)

    def normalize_item_indexes(self):
        """Normalize the indexes to be index from start."""
        text_len = len(self.text)
        for result_item in self.items:
            result_item.start = text_len - result_item.end
            result_item.end = result_item.start + len(result_item.text)

    def to_json(self) -> str:
        """Return a json string serializing this instance."""
        return json.dumps(self, default=lambda x: x.__dict__)

    def __repr__(self):
        """Return a string representation of the object."""

        items_repr = (
            ",\n    ".join([str(item) for item in self.items]) if self.items else ""
        )
        return f"text: {self.text}\nitems:\n[\n    {items_repr}\n]\n"

    def __eq__(self, other) -> bool:
        """Verify two instances are equal.

        Returns true if the two instances are equal, false otherwise.
        """
        return self.text == other.text and all(
            map(lambda x, y: x == y, self.items, other.items)
        )

set_text

set_text(text: str)

Set a text.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
24
25
26
def set_text(self, text: str):
    """Set a text."""
    self.text = text

add_item

add_item(item: OperatorResult)

Add an item.

PARAMETER DESCRIPTION
item

an item to add to the list.

TYPE: OperatorResult

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
28
29
30
31
32
33
def add_item(self, item: OperatorResult):
    """Add an item.

    :param item: an item to add to the list.
    """
    self.items.append(item)

normalize_item_indexes

normalize_item_indexes()

Normalize the indexes to be index from start.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
35
36
37
38
39
40
def normalize_item_indexes(self):
    """Normalize the indexes to be index from start."""
    text_len = len(self.text)
    for result_item in self.items:
        result_item.start = text_len - result_item.end
        result_item.end = result_item.start + len(result_item.text)

to_json

to_json() -> str

Return a json string serializing this instance.

Source code in presidio_anonymizer/entities/engine/result/engine_result.py
42
43
44
def to_json(self) -> str:
    """Return a json string serializing this instance."""
    return json.dumps(self, default=lambda x: x.__dict__)

InvalidParamError

Bases: Exception

Throw exception with error when user input is not valid.

param msg: Message to be added to the exception

Source code in presidio_anonymizer/entities/invalid_exception.py
 4
 5
 6
 7
 8
 9
10
11
12
class InvalidParamError(Exception):
    """Throw exception with error when user input is not valid.

    param msg: Message to be added to the exception
    """

    def __init__(self, msg: str):
        self.err_msg = msg
        super().__init__(self.err_msg)

OperatorConfig

Hold the data of the required operator.

METHOD DESCRIPTION
from_json

Create OperatorConfig from json.

Source code in presidio_anonymizer/entities/engine/operator_config.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
class OperatorConfig:
    """Hold the data of the required operator."""

    def __init__(self, operator_name: str, params: Dict = None):
        """
        Create an operator config instance.

        :param operator_name: the name of the operator we want to work with
        :param params: the parameters the operator needs in order to work
        """
        self.operator_name = operator_name
        if not params:
            params = {}
        self.params = params
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return f"operator_name: {self.operator_name}, params: {self.params}"

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

        :param params: json e.g.: {
            "type": "mask",
            "masking_char": "*",
            "chars_to_mask": 4,
            "from_end": true
            }
        :return: OperatorConfig
        """
        operator_name = params.get("type")
        if operator_name:
            params.pop("type")
        return cls(operator_name, params)

    def __eq__(self, other: "OperatorConfig"):
        """Verify two OperatorConfigs are equal."""
        operator_name = self.operator_name == other.operator_name
        return self.params == other.params and operator_name

    def __validate_fields(self):
        validate_parameter_not_empty(
            self.operator_name, "operator config", "operator_name"
        )

from_json classmethod

from_json(params: Dict) -> OperatorConfig

Create OperatorConfig from json.

PARAMETER DESCRIPTION
params

json e.g.: { "type": "mask", "masking_char": "*", "chars_to_mask": 4, "from_end": true }

TYPE: Dict

RETURNS DESCRIPTION
OperatorConfig

OperatorConfig

Source code in presidio_anonymizer/entities/engine/operator_config.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@classmethod
def from_json(cls, params: Dict) -> "OperatorConfig":
    """
    Create OperatorConfig from json.

    :param params: json e.g.: {
        "type": "mask",
        "masking_char": "*",
        "chars_to_mask": 4,
        "from_end": true
        }
    :return: OperatorConfig
    """
    operator_name = params.get("type")
    if operator_name:
        params.pop("type")
    return cls(operator_name, params)

OperatorResult

Bases: PIIEntity

A class to hold data for engines results either anonymize or deanonymize.

METHOD DESCRIPTION
to_dict

Return object as Dict.

from_json

Create OperatorResult from user json.

Source code in presidio_anonymizer/entities/engine/result/operator_result.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
class OperatorResult(PIIEntity):
    """A class to hold data for engines results either anonymize or deanonymize."""

    def __init__(
        self,
        start: int,
        end: int,
        entity_type: str,
        text: str = None,
        operator: str = None,
    ):
        PIIEntity.__init__(self, start, end, entity_type)
        self.text = text
        self.operator = operator

    def __repr__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def to_dict(self) -> Dict:
        """Return object as Dict."""
        return self.__dict__

    def __str__(self):
        """Return a string representation of the object."""
        return str(self.to_dict())

    def __eq__(self, other: "OperatorResult") -> bool:
        """
        Verify two OperatorResults are equal.

        :param other: OperatorResult
        :return: bool
        """
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
            and self.operator == other.operator
            and self.text == other.text
        )

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

        :param json: json representation for this operator result. For example:
        {
            "start": 0,
            "end": 10,
            "key": "1111111111111111",
            "entity_type":"PERSON",
            "text":"resulted_text",
            "operator":"encrypt",
        }
        """
        start = json.get("start")
        end = json.get("end")
        entity_type = json.get("entity_type")
        text = json.get("text")
        operator = json.get("operator")
        return cls(
            start=start,
            end=end,
            entity_type=entity_type,
            text=text,
            operator=operator,
        )

to_dict

to_dict() -> Dict

Return object as Dict.

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
25
26
27
def to_dict(self) -> Dict:
    """Return object as Dict."""
    return self.__dict__

from_json classmethod

from_json(json: Dict) -> OperatorResult

Create OperatorResult from user json.

PARAMETER DESCRIPTION
json

json representation for this operator result. For example: { "start": 0, "end": 10, "key": "1111111111111111", "entity_type":"PERSON", "text":"resulted_text", "operator":"encrypt", }

TYPE: Dict

Source code in presidio_anonymizer/entities/engine/result/operator_result.py
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
@classmethod
def from_json(cls, json: Dict) -> "OperatorResult":
    """
    Create OperatorResult from user json.

    :param json: json representation for this operator result. For example:
    {
        "start": 0,
        "end": 10,
        "key": "1111111111111111",
        "entity_type":"PERSON",
        "text":"resulted_text",
        "operator":"encrypt",
    }
    """
    start = json.get("start")
    end = json.get("end")
    entity_type = json.get("entity_type")
    text = json.get("text")
    operator = json.get("operator")
    return cls(
        start=start,
        end=end,
        entity_type=entity_type,
        text=text,
        operator=operator,
    )

PIIEntity

Bases: ABC

Abstract class to hold the text we are going to operate on metadata.

Source code in presidio_anonymizer/entities/engine/pii_entity.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
class PIIEntity(ABC):
    """Abstract class to hold the text we are going to operate on metadata."""

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

    def __init__(self, start: int, end: int, entity_type: str):
        self.start = start
        self.end = end
        self.entity_type = entity_type
        self.__validate_fields()

    def __repr__(self):
        """Return a string representation of the object."""
        return (
            f"start: {self.start}"
            f"end: {self.end},"
            f"entity_type: {self.entity_type}"
        )

    def __gt__(self, other):
        """Check one entity is greater then other by the text end index."""
        return self.start > other.start

    def __eq__(self, other):
        """Check two text metadata entities are equal."""
        return (
            self.start == other.start
            and self.end == other.end
            and self.entity_type == other.entity_type
        )

    def __validate_fields(self):
        validate_parameter_exists(self.start, "result", "start")
        validate_type(self.start, "start", int)
        validate_parameter_exists(self.end, "result", "end")
        validate_type(self.end, "end", int)
        validate_parameter_not_empty(self.entity_type, "result", "entity_type")
        if self.start < 0 or self.end < 0:
            raise InvalidParamError(
                "Invalid input, result start and end must be positive"
            )
        if self.start > self.end:
            raise InvalidParamError(
                f"Invalid input, start index '{self.start}' "
                f"must be smaller than end index '{self.end}'"
            )

RecognizerResult

Bases: PIIEntity

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

METHOD DESCRIPTION
from_json

Create RecognizerResult from json.

has_conflict

Check if two recognizer results are conflicted or not.

contains

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

equal_indices

Check if the indices are equal between two results.

intersects

Check if self intersects with a different RecognizerResult.

Source code in presidio_anonymizer/entities/engine/recognizer_result.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 RecognizerResult(PIIEntity):
    """
    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
    """

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

    def __init__(self, entity_type: str, start: int, end: int, score: float):
        PIIEntity.__init__(self, start, end, entity_type)
        self.score = score
        validate_parameter_exists(score, "analyzer result", "score")

    @classmethod
    def from_json(cls, data: Dict):
        """
        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 __gt__(self, other):
        """
        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):
        """
        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):
        """
        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)

    def contains(self, other):
        """
        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):
        """
        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 intersects(self, other) -> 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)

from_json classmethod

from_json(data: Dict)

Create RecognizerResult from json.

PARAMETER DESCRIPTION
data

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

TYPE: Dict

RETURNS DESCRIPTION

RecognizerResult

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@classmethod
def from_json(cls, data: Dict):
    """
    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)

has_conflict

has_conflict(other)

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

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def has_conflict(self, other):
    """
    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)

contains

contains(other)

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

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION

bool

Source code in presidio_anonymizer/entities/engine/recognizer_result.py
108
109
110
111
112
113
114
115
def contains(self, other):
    """
    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)

Check if the indices are equal between two results.

PARAMETER DESCRIPTION
other

another RecognizerResult

RETURNS DESCRIPTION
Source code in presidio_anonymizer/entities/engine/recognizer_result.py
117
118
119
120
121
122
123
124
def equal_indices(self, other):
    """
    Check if the indices are equal between two results.

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

intersects

intersects(other) -> 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_anonymizer/entities/engine/recognizer_result.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def intersects(self, other) -> 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)