Coverage for presidio_analyzer / predefined_recognizers / third_party / azure_ai_language.py: 72%
60 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
1import logging
2import os
3from typing import List, Optional
5try:
6 from azure.ai.textanalytics import TextAnalyticsClient
7 from azure.core.credentials import AzureKeyCredential
9except ImportError:
10 TextAnalyticsClient = None
11 AzureKeyCredential = None
12from presidio_analyzer import AnalysisExplanation, RecognizerResult, RemoteRecognizer
13from presidio_analyzer.nlp_engine import NlpArtifacts
15logger = logging.getLogger("presidio-analyzer")
18class AzureAILanguageRecognizer(RemoteRecognizer):
19 """Wrapper for PII detection using Azure AI Language."""
21 def __init__(
22 self,
23 supported_entities: Optional[List[str]] = None,
24 supported_language: str = "en",
25 ta_client: Optional["TextAnalyticsClient"] = None,
26 azure_ai_key: Optional[str] = None,
27 azure_ai_endpoint: Optional[str] = None,
28 ):
29 """
30 Wrap the PII detection in Azure AI Language.
32 :param supported_entities: List of supported entities for this recognizer.
33 If None, all supported entities will be used.
34 :param supported_language: Language code to use for the recognizer.
35 :param ta_client: object of type TextAnalyticsClient. If missing,
36 the client will be created using the key and endpoint.
37 :param azure_ai_key: Azure AI for language key
38 :param azure_ai_endpoint: Azure AI for language endpoint
40 For more info, see https://learn.microsoft.com/en-us/azure/ai-services/language-service/personally-identifiable-information/overview
41 """
43 super().__init__(
44 supported_entities=supported_entities,
45 supported_language=supported_language,
46 name="Azure AI Language PII",
47 version="5.2.0",
48 )
50 is_available = bool(TextAnalyticsClient)
51 if not ta_client and not is_available:
52 raise ValueError(
53 "Azure AI Language is not available. "
54 "Please install the required dependencies:"
55 "1. azure-ai-textanalytics"
56 "2. azure-core"
57 )
59 if not supported_entities:
60 self.supported_entities = self.__get_azure_ai_supported_entities()
62 if not ta_client:
63 ta_client = self.__authenticate_client(azure_ai_key, azure_ai_endpoint)
64 self.ta_client = ta_client
66 def get_supported_entities(self) -> List[str]:
67 """
68 Return the list of entities this recognizer can identify.
70 :return: A list of the supported entities by this recognizer
71 """
72 return self.supported_entities
74 @staticmethod
75 def __get_azure_ai_supported_entities() -> List[str]:
76 """Return the list of all supported entities for Azure AI Language."""
77 from azure.ai.textanalytics._models import PiiEntityCategory
79 return [r.value.upper() for r in PiiEntityCategory]
81 @staticmethod
82 def __authenticate_client(key: str, endpoint: str) -> TextAnalyticsClient:
83 """Authenticate the client using the key and endpoint.
85 :param key: Azure AI Language key
86 :param endpoint: Azure AI Language endpoint
87 """
88 key = key if key else os.getenv("AZURE_AI_KEY", None)
89 endpoint = endpoint if endpoint else os.getenv("AZURE_AI_ENDPOINT", None)
90 if key is None:
91 raise ValueError(
92 "Azure AI Language key is required. "
93 "Please provide a key or set the AZURE_AI_KEY environment variable."
94 )
95 if endpoint is None:
96 raise ValueError(
97 "Azure AI Language endpoint is required. "
98 "Please provide an endpoint "
99 "or set the AZURE_AI_ENDPOINT environment variable."
100 )
102 ta_credential = AzureKeyCredential(key)
103 text_analytics_client = TextAnalyticsClient(
104 endpoint=endpoint, credential=ta_credential
105 )
106 return text_analytics_client
108 def analyze(
109 self, text: str, entities: List[str] = None, nlp_artifacts: NlpArtifacts = None
110 ) -> List[RecognizerResult]:
111 """
112 Analyze text using Azure AI Language.
114 :param text: Text to analyze
115 :param entities: List of entities to return
116 :param nlp_artifacts: Object of type NlpArtifacts, not used in this recognizer.
117 :return: A list of RecognizerResult, one per each entity found in the text.
118 """
119 if not entities:
120 entities = self.supported_entities
121 response = self.ta_client.recognize_pii_entities(
122 [text], language=self.supported_language
123 )
124 results = [doc for doc in response if not doc.is_error]
125 recognizer_results = []
126 for res in results:
127 for entity in res.entities:
128 entity.category = entity.category.upper()
129 if entity.category.lower() not in [
130 ent.lower() for ent in self.supported_entities
131 ]:
132 continue
133 if entity.category.lower() not in [ent.lower() for ent in entities]:
134 continue
135 analysis_explanation = AzureAILanguageRecognizer._build_explanation(
136 original_score=entity.confidence_score,
137 entity_type=entity.category,
138 )
139 recognizer_results.append(
140 RecognizerResult(
141 entity_type=entity.category,
142 start=entity.offset,
143 end=entity.offset + entity.length,
144 score=entity.confidence_score,
145 analysis_explanation=analysis_explanation,
146 )
147 )
149 return recognizer_results
151 @staticmethod
152 def _build_explanation(
153 original_score: float, entity_type: str
154 ) -> AnalysisExplanation:
155 explanation = AnalysisExplanation(
156 recognizer=AzureAILanguageRecognizer.__class__.__name__,
157 original_score=original_score,
158 textual_explanation=f"Identified as {entity_type} by Azure AI Language",
159 )
160 return explanation