srx-lib-azure 0.1.8__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of srx-lib-azure might be problematic. Click here for more details.
- srx_lib_azure/__init__.py +7 -1
- srx_lib_azure/document.py +262 -0
- srx_lib_azure-0.2.0.dist-info/METADATA +131 -0
- srx_lib_azure-0.2.0.dist-info/RECORD +8 -0
- srx_lib_azure-0.1.8.dist-info/METADATA +0 -70
- srx_lib_azure-0.1.8.dist-info/RECORD +0 -7
- {srx_lib_azure-0.1.8.dist-info → srx_lib_azure-0.2.0.dist-info}/WHEEL +0 -0
srx_lib_azure/__init__.py
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
from .blob import AzureBlobService
|
|
2
|
+
from .document import AzureDocumentIntelligenceService
|
|
2
3
|
from .email import EmailService
|
|
3
4
|
from .table import AzureTableService
|
|
4
5
|
|
|
5
|
-
__all__ = [
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AzureBlobService",
|
|
8
|
+
"AzureDocumentIntelligenceService",
|
|
9
|
+
"AzureTableService",
|
|
10
|
+
"EmailService",
|
|
11
|
+
]
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import io
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Dict, Any, Optional
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from azure.ai.documentintelligence import DocumentIntelligenceClient
|
|
10
|
+
from azure.ai.documentintelligence.models import AnalyzeDocumentRequest, AnalyzeResult
|
|
11
|
+
from azure.core.credentials import AzureKeyCredential
|
|
12
|
+
from azure.core.exceptions import (
|
|
13
|
+
ClientAuthenticationError,
|
|
14
|
+
HttpResponseError,
|
|
15
|
+
ServiceRequestError,
|
|
16
|
+
)
|
|
17
|
+
except Exception: # pragma: no cover - optional dependency at import time
|
|
18
|
+
DocumentIntelligenceClient = None # type: ignore
|
|
19
|
+
AnalyzeDocumentRequest = None # type: ignore
|
|
20
|
+
AnalyzeResult = None # type: ignore
|
|
21
|
+
AzureKeyCredential = None # type: ignore
|
|
22
|
+
ClientAuthenticationError = None # type: ignore
|
|
23
|
+
HttpResponseError = None # type: ignore
|
|
24
|
+
ServiceRequestError = None # type: ignore
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AzureDocumentIntelligenceService:
|
|
28
|
+
"""Wrapper for Azure Document Intelligence (OCR/Document Analysis).
|
|
29
|
+
|
|
30
|
+
Does not raise on missing configuration to keep the library optional.
|
|
31
|
+
If not configured, analysis calls return error responses with descriptive messages.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
*,
|
|
37
|
+
endpoint: Optional[str] = None,
|
|
38
|
+
key: Optional[str] = None,
|
|
39
|
+
warn_if_unconfigured: bool = False,
|
|
40
|
+
):
|
|
41
|
+
"""Initialize Document Intelligence service.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
endpoint: Azure Document Intelligence endpoint URL
|
|
45
|
+
key: Azure Document Intelligence API key
|
|
46
|
+
warn_if_unconfigured: Whether to log a warning if not configured
|
|
47
|
+
"""
|
|
48
|
+
self.endpoint = endpoint or os.getenv("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT")
|
|
49
|
+
self.key = key or os.getenv("AZURE_DOCUMENT_INTELLIGENCE_KEY")
|
|
50
|
+
|
|
51
|
+
if not self.endpoint or not self.key or DocumentIntelligenceClient is None:
|
|
52
|
+
self.client = None
|
|
53
|
+
if warn_if_unconfigured:
|
|
54
|
+
logger.warning(
|
|
55
|
+
"AzureDocumentIntelligenceService not configured "
|
|
56
|
+
"(missing endpoint/key or azure-ai-documentintelligence SDK). "
|
|
57
|
+
"Calls will return error responses."
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
try:
|
|
61
|
+
self.client = DocumentIntelligenceClient(
|
|
62
|
+
endpoint=self.endpoint, credential=AzureKeyCredential(self.key)
|
|
63
|
+
)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
self.client = None
|
|
66
|
+
logger.warning("DocumentIntelligenceClient initialization failed: %s", e)
|
|
67
|
+
|
|
68
|
+
async def analyze_document_from_url(
|
|
69
|
+
self, url: str, model_id: str = "prebuilt-read"
|
|
70
|
+
) -> Dict[str, Any]:
|
|
71
|
+
"""Analyze a document from a URL using Azure Document Intelligence.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
url: URL of the document to analyze (must be accessible to Azure)
|
|
75
|
+
model_id: Model to use (default: "prebuilt-read" for OCR)
|
|
76
|
+
Other options: "prebuilt-layout", "prebuilt-invoice", etc.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Dict with analysis results:
|
|
80
|
+
- success (bool): Whether analysis succeeded
|
|
81
|
+
- content (str | None): Extracted text content
|
|
82
|
+
- pages (list[dict] | None): Page information
|
|
83
|
+
- page_count (int | None): Total number of pages
|
|
84
|
+
- confidence (float | None): Average OCR confidence (0-1)
|
|
85
|
+
- model_id (str | None): Model used
|
|
86
|
+
- metadata (dict | None): Additional metadata
|
|
87
|
+
- error (str | None): Error message if failed
|
|
88
|
+
"""
|
|
89
|
+
if not self.client:
|
|
90
|
+
logger.warning("Document analysis from URL skipped: service not configured")
|
|
91
|
+
return {
|
|
92
|
+
"success": False,
|
|
93
|
+
"error": "Document Intelligence service not configured",
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
logger.info(f"Starting document analysis from URL: {url} (model: {model_id})")
|
|
98
|
+
|
|
99
|
+
# Run the blocking operation in a thread pool
|
|
100
|
+
poller = await asyncio.to_thread(
|
|
101
|
+
self.client.begin_analyze_document,
|
|
102
|
+
model_id,
|
|
103
|
+
AnalyzeDocumentRequest(url_source=url),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Wait for the result
|
|
107
|
+
result: AnalyzeResult = await asyncio.to_thread(poller.result)
|
|
108
|
+
|
|
109
|
+
logger.info(
|
|
110
|
+
f"Document analysis completed (model: {model_id}, pages: {len(result.pages or [])})"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return self._format_result(result, model_id)
|
|
114
|
+
|
|
115
|
+
except ClientAuthenticationError as e:
|
|
116
|
+
logger.error(f"Authentication failed for document analysis: {e}")
|
|
117
|
+
return {"success": False, "error": f"Authentication failed: {e}"}
|
|
118
|
+
except HttpResponseError as e:
|
|
119
|
+
logger.error(f"Azure service error analyzing document: {e.status_code} - {e.message}")
|
|
120
|
+
return {
|
|
121
|
+
"success": False,
|
|
122
|
+
"error": f"Azure service error ({e.status_code}): {e.message}",
|
|
123
|
+
}
|
|
124
|
+
except ServiceRequestError as e:
|
|
125
|
+
logger.error(f"Network error analyzing document: {e}")
|
|
126
|
+
return {"success": False, "error": f"Network error: {e}"}
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.error(f"Unexpected error analyzing document from URL: {e}")
|
|
129
|
+
return {"success": False, "error": f"Unexpected error: {e}"}
|
|
130
|
+
|
|
131
|
+
async def analyze_document_from_bytes(
|
|
132
|
+
self, file_content: bytes, model_id: str = "prebuilt-read"
|
|
133
|
+
) -> Dict[str, Any]:
|
|
134
|
+
"""Analyze a document from bytes using Azure Document Intelligence.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
file_content: Document content as bytes (PDF, image, etc.)
|
|
138
|
+
model_id: Model to use (default: "prebuilt-read" for OCR)
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Dict with analysis results (same format as analyze_document_from_url)
|
|
142
|
+
"""
|
|
143
|
+
if not self.client:
|
|
144
|
+
logger.warning("Document analysis from bytes skipped: service not configured")
|
|
145
|
+
return {
|
|
146
|
+
"success": False,
|
|
147
|
+
"error": "Document Intelligence service not configured",
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
logger.info(
|
|
152
|
+
f"Starting document analysis from bytes (size: {len(file_content)} bytes, model: {model_id})"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Create a file-like object from bytes
|
|
156
|
+
file_stream = io.BytesIO(file_content)
|
|
157
|
+
|
|
158
|
+
# Run the blocking operation in a thread pool
|
|
159
|
+
poller = await asyncio.to_thread(
|
|
160
|
+
self.client.begin_analyze_document,
|
|
161
|
+
model_id,
|
|
162
|
+
body=file_stream,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Wait for the result
|
|
166
|
+
result: AnalyzeResult = await asyncio.to_thread(poller.result)
|
|
167
|
+
|
|
168
|
+
logger.info(
|
|
169
|
+
f"Document analysis completed (model: {model_id}, pages: {len(result.pages or [])})"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return self._format_result(result, model_id)
|
|
173
|
+
|
|
174
|
+
except ClientAuthenticationError as e:
|
|
175
|
+
logger.error(f"Authentication failed for document analysis: {e}")
|
|
176
|
+
return {"success": False, "error": f"Authentication failed: {e}"}
|
|
177
|
+
except HttpResponseError as e:
|
|
178
|
+
logger.error(f"Azure service error analyzing document: {e.status_code} - {e.message}")
|
|
179
|
+
return {
|
|
180
|
+
"success": False,
|
|
181
|
+
"error": f"Azure service error ({e.status_code}): {e.message}",
|
|
182
|
+
}
|
|
183
|
+
except ServiceRequestError as e:
|
|
184
|
+
logger.error(f"Network error analyzing document: {e}")
|
|
185
|
+
return {"success": False, "error": f"Network error: {e}"}
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.error(f"Unexpected error analyzing document from bytes: {e}")
|
|
188
|
+
return {"success": False, "error": f"Unexpected error: {e}"}
|
|
189
|
+
|
|
190
|
+
def _format_result(self, result: AnalyzeResult, model_id: str) -> Dict[str, Any]:
|
|
191
|
+
"""Format the AnalyzeResult into a dict response.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
result: Azure Document Intelligence AnalyzeResult
|
|
195
|
+
model_id: Model ID used for analysis
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Formatted dict with extracted content and metadata
|
|
199
|
+
"""
|
|
200
|
+
# Extract all text content
|
|
201
|
+
content_parts: list[str] = []
|
|
202
|
+
pages_info: list[Dict[str, Any]] = []
|
|
203
|
+
total_confidence = 0.0
|
|
204
|
+
confidence_count = 0
|
|
205
|
+
|
|
206
|
+
if result.pages:
|
|
207
|
+
for page in result.pages:
|
|
208
|
+
# Collect page info
|
|
209
|
+
page_info = {
|
|
210
|
+
"page_number": page.page_number,
|
|
211
|
+
"width": page.width,
|
|
212
|
+
"height": page.height,
|
|
213
|
+
"unit": page.unit,
|
|
214
|
+
"lines_count": len(page.lines or []),
|
|
215
|
+
"words_count": len(page.words or []),
|
|
216
|
+
}
|
|
217
|
+
pages_info.append(page_info)
|
|
218
|
+
|
|
219
|
+
# Extract text from lines
|
|
220
|
+
if page.lines:
|
|
221
|
+
for line in page.lines:
|
|
222
|
+
content_parts.append(line.content)
|
|
223
|
+
# Track confidence if available
|
|
224
|
+
if hasattr(line, "confidence") and line.confidence is not None:
|
|
225
|
+
total_confidence += line.confidence
|
|
226
|
+
confidence_count += 1
|
|
227
|
+
|
|
228
|
+
# Combine all content with newlines
|
|
229
|
+
full_content = "\n".join(content_parts)
|
|
230
|
+
|
|
231
|
+
# Calculate average confidence
|
|
232
|
+
avg_confidence = total_confidence / confidence_count if confidence_count > 0 else None
|
|
233
|
+
|
|
234
|
+
# Build metadata
|
|
235
|
+
metadata: Dict[str, Any] = {
|
|
236
|
+
"content_format": (
|
|
237
|
+
result.content_format if hasattr(result, "content_format") else None
|
|
238
|
+
),
|
|
239
|
+
"api_version": result.api_version if hasattr(result, "api_version") else None,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
# Add languages if detected
|
|
243
|
+
if hasattr(result, "languages") and result.languages:
|
|
244
|
+
metadata["languages"] = [
|
|
245
|
+
{"locale": lang.locale, "confidence": lang.confidence} for lang in result.languages
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
# Add styles if detected (e.g., handwriting)
|
|
249
|
+
if hasattr(result, "styles") and result.styles:
|
|
250
|
+
metadata["has_handwriting"] = any(
|
|
251
|
+
style.is_handwritten for style in result.styles if hasattr(style, "is_handwritten")
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
"success": True,
|
|
256
|
+
"content": full_content if full_content else None,
|
|
257
|
+
"pages": pages_info if pages_info else None,
|
|
258
|
+
"page_count": len(pages_info) if pages_info else None,
|
|
259
|
+
"confidence": avg_confidence,
|
|
260
|
+
"model_id": model_id,
|
|
261
|
+
"metadata": metadata,
|
|
262
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: srx-lib-azure
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Azure helpers for SRX services: Blob, Email, Table, Document Intelligence
|
|
5
|
+
Author-email: SRX <dev@srx.id>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: azure-ai-documentintelligence>=1.0.0
|
|
8
|
+
Requires-Dist: azure-communication-email>=1.0.0
|
|
9
|
+
Requires-Dist: azure-data-tables>=12.7.0
|
|
10
|
+
Requires-Dist: azure-storage-blob>=12.22.0
|
|
11
|
+
Requires-Dist: loguru>=0.7.2
|
|
12
|
+
Provides-Extra: all
|
|
13
|
+
Requires-Dist: azure-ai-documentintelligence>=1.0.0; extra == 'all'
|
|
14
|
+
Provides-Extra: document
|
|
15
|
+
Requires-Dist: azure-ai-documentintelligence>=1.0.0; extra == 'document'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# srx-lib-azure
|
|
19
|
+
|
|
20
|
+
Lightweight wrappers over Azure SDKs used across SRX services.
|
|
21
|
+
|
|
22
|
+
What it includes:
|
|
23
|
+
- **Blob**: upload/download helpers, SAS URL generation
|
|
24
|
+
- **Email** (Azure Communication Services): simple async sender
|
|
25
|
+
- **Table**: simple CRUD helpers
|
|
26
|
+
- **Document Intelligence** (OCR): document analysis from URLs or bytes
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
PyPI (public):
|
|
31
|
+
|
|
32
|
+
- `pip install srx-lib-azure`
|
|
33
|
+
|
|
34
|
+
uv (pyproject):
|
|
35
|
+
```
|
|
36
|
+
[project]
|
|
37
|
+
dependencies = ["srx-lib-azure>=0.1.0"]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
Blob:
|
|
43
|
+
```
|
|
44
|
+
from srx_lib_azure.blob import AzureBlobService
|
|
45
|
+
blob = AzureBlobService()
|
|
46
|
+
url = await blob.upload_file(upload_file, "documents/report.pdf")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Email:
|
|
50
|
+
```
|
|
51
|
+
from srx_lib_azure.email import EmailService
|
|
52
|
+
svc = EmailService()
|
|
53
|
+
await svc.send_notification("user@example.com", "Subject", "Hello", html=False)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Table:
|
|
57
|
+
```
|
|
58
|
+
from srx_lib_azure.table import AzureTableService
|
|
59
|
+
store = AzureTableService()
|
|
60
|
+
store.ensure_table("events")
|
|
61
|
+
store.upsert_entity("events", {"PartitionKey":"p","RowKey":"r","EventType":"x"})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Document Intelligence (OCR):
|
|
65
|
+
```python
|
|
66
|
+
from srx_lib_azure import AzureDocumentIntelligenceService
|
|
67
|
+
|
|
68
|
+
# Initialize with endpoint and key
|
|
69
|
+
doc_service = AzureDocumentIntelligenceService(
|
|
70
|
+
endpoint="https://your-resource.cognitiveservices.azure.com/",
|
|
71
|
+
key="your-api-key"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Analyze document from URL
|
|
75
|
+
result = await doc_service.analyze_document_from_url(
|
|
76
|
+
url="https://example.com/document.pdf",
|
|
77
|
+
model_id="prebuilt-read" # or "prebuilt-layout", "prebuilt-invoice", etc.
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Analyze document from bytes
|
|
81
|
+
with open("document.pdf", "rb") as f:
|
|
82
|
+
content = f.read()
|
|
83
|
+
result = await doc_service.analyze_document_from_bytes(
|
|
84
|
+
file_content=content,
|
|
85
|
+
model_id="prebuilt-read"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Result structure:
|
|
89
|
+
# {
|
|
90
|
+
# "success": True/False,
|
|
91
|
+
# "content": "extracted text...",
|
|
92
|
+
# "pages": [{"page_number": 1, "width": 8.5, ...}, ...],
|
|
93
|
+
# "page_count": 10,
|
|
94
|
+
# "confidence": 0.98,
|
|
95
|
+
# "model_id": "prebuilt-read",
|
|
96
|
+
# "metadata": {...},
|
|
97
|
+
# "error": None # or error message if failed
|
|
98
|
+
# }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Environment Variables
|
|
102
|
+
|
|
103
|
+
- **Blob & Table**: `AZURE_STORAGE_CONNECTION_STRING` (required)
|
|
104
|
+
- **Email (ACS)**: `ACS_CONNECTION_STRING`, `EMAIL_SENDER`
|
|
105
|
+
- **Document Intelligence**: `AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT`, `AZURE_DOCUMENT_INTELLIGENCE_KEY`
|
|
106
|
+
- **Optional**: `AZURE_STORAGE_ACCOUNT_KEY`, `AZURE_BLOB_URL`, `AZURE_SAS_TOKEN`
|
|
107
|
+
|
|
108
|
+
## Optional Dependencies
|
|
109
|
+
|
|
110
|
+
All services are optional and won't break if their dependencies aren't installed:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Base installation (includes all services by default)
|
|
114
|
+
pip install srx-lib-azure
|
|
115
|
+
|
|
116
|
+
# Or install only what you need - document intelligence is optional
|
|
117
|
+
pip install srx-lib-azure[document] # Adds Document Intelligence support
|
|
118
|
+
|
|
119
|
+
# Install with all optional dependencies
|
|
120
|
+
pip install srx-lib-azure[all]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If you import a service without its required Azure SDK, it will log a warning but won't crash.
|
|
124
|
+
|
|
125
|
+
## Release
|
|
126
|
+
|
|
127
|
+
Tag `vX.Y.Z` to publish to GitHub Packages via Actions.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
Proprietary © SRX
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
srx_lib_azure/__init__.py,sha256=I2nQgMpTptWYHMqHfSYYv3cuC5wkeeYpawQ31zVN5mQ,283
|
|
2
|
+
srx_lib_azure/blob.py,sha256=pAOIPAImhIChMCoxQhe79N11vLF24yAlw0R8gcnitvA,12730
|
|
3
|
+
srx_lib_azure/document.py,sha256=gAVIJRRwtDscAYAUKH7XiAqdCSmoZ-TpzivWS12XQXM,10551
|
|
4
|
+
srx_lib_azure/email.py,sha256=qZWZUjoUMocivU-S7Qa4mJV8aj4jtQPfy868wnPgPlA,4837
|
|
5
|
+
srx_lib_azure/table.py,sha256=-RPMlZ2gzXi-6bJSkGFuV5XyIp8-3pdwulI-IEZUs1Y,18607
|
|
6
|
+
srx_lib_azure-0.2.0.dist-info/METADATA,sha256=l0KQ0OTRlwZsDD8Z6lzGiJYTzbfYp5mB5Pg6x7LrUZI,3584
|
|
7
|
+
srx_lib_azure-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
+
srx_lib_azure-0.2.0.dist-info/RECORD,,
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: srx-lib-azure
|
|
3
|
-
Version: 0.1.8
|
|
4
|
-
Summary: Azure helpers for SRX services: Blob, Email, Table
|
|
5
|
-
Author-email: SRX <dev@srx.id>
|
|
6
|
-
Requires-Python: >=3.12
|
|
7
|
-
Requires-Dist: azure-communication-email>=1.0.0
|
|
8
|
-
Requires-Dist: azure-data-tables>=12.7.0
|
|
9
|
-
Requires-Dist: azure-storage-blob>=12.22.0
|
|
10
|
-
Requires-Dist: loguru>=0.7.2
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
|
|
13
|
-
# srx-lib-azure
|
|
14
|
-
|
|
15
|
-
Lightweight wrappers over Azure SDKs used across SRX services.
|
|
16
|
-
|
|
17
|
-
What it includes:
|
|
18
|
-
- Blob: upload/download helpers, SAS URL generation
|
|
19
|
-
- Email (Azure Communication Services): simple async sender
|
|
20
|
-
- Table: simple CRUD helpers
|
|
21
|
-
|
|
22
|
-
## Install
|
|
23
|
-
|
|
24
|
-
PyPI (public):
|
|
25
|
-
|
|
26
|
-
- `pip install srx-lib-azure`
|
|
27
|
-
|
|
28
|
-
uv (pyproject):
|
|
29
|
-
```
|
|
30
|
-
[project]
|
|
31
|
-
dependencies = ["srx-lib-azure>=0.1.0"]
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Usage
|
|
35
|
-
|
|
36
|
-
Blob:
|
|
37
|
-
```
|
|
38
|
-
from srx_lib_azure.blob import AzureBlobService
|
|
39
|
-
blob = AzureBlobService()
|
|
40
|
-
url = await blob.upload_file(upload_file, "documents/report.pdf")
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Email:
|
|
44
|
-
```
|
|
45
|
-
from srx_lib_azure.email import EmailService
|
|
46
|
-
svc = EmailService()
|
|
47
|
-
await svc.send_notification("user@example.com", "Subject", "Hello", html=False)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Table:
|
|
51
|
-
```
|
|
52
|
-
from srx_lib_azure.table import AzureTableService
|
|
53
|
-
store = AzureTableService()
|
|
54
|
-
store.ensure_table("events")
|
|
55
|
-
store.upsert_entity("events", {"PartitionKey":"p","RowKey":"r","EventType":"x"})
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Environment Variables
|
|
59
|
-
|
|
60
|
-
- Blob & Table: `AZURE_STORAGE_CONNECTION_STRING` (required)
|
|
61
|
-
- Email (ACS): `ACS_CONNECTION_STRING`, `EMAIL_SENDER`
|
|
62
|
-
- Optional: `AZURE_STORAGE_ACCOUNT_KEY`, `AZURE_BLOB_URL`, `AZURE_SAS_TOKEN`
|
|
63
|
-
|
|
64
|
-
## Release
|
|
65
|
-
|
|
66
|
-
Tag `vX.Y.Z` to publish to GitHub Packages via Actions.
|
|
67
|
-
|
|
68
|
-
## License
|
|
69
|
-
|
|
70
|
-
Proprietary © SRX
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
srx_lib_azure/__init__.py,sha256=rtG8P2NjXvS28KOdfj3Ad6bbjQLaKYUd1prSaKmv1eU,173
|
|
2
|
-
srx_lib_azure/blob.py,sha256=pAOIPAImhIChMCoxQhe79N11vLF24yAlw0R8gcnitvA,12730
|
|
3
|
-
srx_lib_azure/email.py,sha256=qZWZUjoUMocivU-S7Qa4mJV8aj4jtQPfy868wnPgPlA,4837
|
|
4
|
-
srx_lib_azure/table.py,sha256=-RPMlZ2gzXi-6bJSkGFuV5XyIp8-3pdwulI-IEZUs1Y,18607
|
|
5
|
-
srx_lib_azure-0.1.8.dist-info/METADATA,sha256=2kvpBmQ25Es7i_Da8uhCBU-OrArzMGM5J_5bRF8EDc4,1600
|
|
6
|
-
srx_lib_azure-0.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
-
srx_lib_azure-0.1.8.dist-info/RECORD,,
|
|
File without changes
|