praisonaiagents 0.0.22__py3-none-any.whl → 0.0.24__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.
@@ -0,0 +1,498 @@
1
+ """Tools for working with XML files.
2
+
3
+ Usage:
4
+ from praisonaiagents.tools import xml_tools
5
+ tree = xml_tools.read_xml("data.xml")
6
+
7
+ or
8
+ from praisonaiagents.tools import read_xml, write_xml, transform_xml
9
+ tree = read_xml("data.xml")
10
+ """
11
+
12
+ import logging
13
+ from typing import List, Dict, Union, Optional, Any, Tuple
14
+ from importlib import util
15
+ import xml.etree.ElementTree as ET
16
+ import xml.dom.minidom as minidom
17
+ from io import StringIO
18
+ import json
19
+
20
+ class XMLTools:
21
+ """Tools for working with XML files."""
22
+
23
+ def __init__(self):
24
+ """Initialize XMLTools."""
25
+ pass
26
+
27
+ def read_xml(
28
+ self,
29
+ filepath: str,
30
+ encoding: str = 'utf-8',
31
+ validate_schema: Optional[str] = None,
32
+ parser: str = 'lxml'
33
+ ) -> ET.Element:
34
+ """Read an XML file with optional schema validation.
35
+
36
+ Args:
37
+ filepath: Path to XML file
38
+ encoding: File encoding
39
+ validate_schema: Optional path to XSD schema file
40
+ parser: XML parser to use ('lxml' or 'etree')
41
+
42
+ Returns:
43
+ ElementTree root element
44
+ """
45
+ try:
46
+ if parser == 'lxml':
47
+ if util.find_spec('lxml') is None:
48
+ error_msg = "lxml package is not available. Please install it using: pip install lxml"
49
+ logging.error(error_msg)
50
+ return None
51
+ import lxml.etree as lxml_etree
52
+ tree = lxml_etree.parse(filepath)
53
+ root = tree.getroot()
54
+ else:
55
+ tree = ET.parse(filepath)
56
+ root = tree.getroot()
57
+
58
+ if validate_schema:
59
+ if util.find_spec('xmlschema') is None:
60
+ error_msg = "xmlschema package is not available. Please install it using: pip install xmlschema"
61
+ logging.error(error_msg)
62
+ return None
63
+ import xmlschema
64
+ schema = xmlschema.XMLSchema(validate_schema)
65
+ if not schema.is_valid(filepath):
66
+ error_msg = f"XML file does not validate against schema: {schema.validate(filepath)}"
67
+ logging.error(error_msg)
68
+ return None
69
+
70
+ return root
71
+
72
+ except Exception as e:
73
+ error_msg = f"Error reading XML file {filepath}: {str(e)}"
74
+ logging.error(error_msg)
75
+ return None
76
+
77
+ def write_xml(
78
+ self,
79
+ root: ET.Element,
80
+ filepath: str,
81
+ encoding: str = 'utf-8',
82
+ pretty: bool = True,
83
+ xml_declaration: bool = True
84
+ ) -> bool:
85
+ """Write XML Element tree to file.
86
+
87
+ Args:
88
+ root: XML Element tree root
89
+ filepath: Output file path
90
+ encoding: File encoding
91
+ pretty: Format output with proper indentation
92
+ xml_declaration: Include XML declaration
93
+
94
+ Returns:
95
+ True if successful, False otherwise
96
+ """
97
+ try:
98
+ # Convert to string
99
+ if pretty:
100
+ xml_str = minidom.parseString(
101
+ ET.tostring(root, encoding='unicode')
102
+ ).toprettyxml(indent=' ')
103
+ else:
104
+ xml_str = ET.tostring(
105
+ root,
106
+ encoding='unicode'
107
+ )
108
+
109
+ # Add declaration if requested
110
+ if xml_declaration:
111
+ if not xml_str.startswith('<?xml'):
112
+ xml_str = (
113
+ f'<?xml version="1.0" encoding="{encoding}"?>\n'
114
+ + xml_str
115
+ )
116
+
117
+ # Write to file
118
+ with open(filepath, 'w', encoding=encoding) as f:
119
+ f.write(xml_str)
120
+
121
+ return True
122
+ except Exception as e:
123
+ error_msg = f"Error writing XML file {filepath}: {str(e)}"
124
+ logging.error(error_msg)
125
+ return False
126
+
127
+ def transform_xml(
128
+ self,
129
+ xml_file: str,
130
+ xslt_file: str,
131
+ output_file: str,
132
+ params: Optional[Dict[str, str]] = None
133
+ ) -> bool:
134
+ """Transform XML using XSLT stylesheet.
135
+
136
+ Args:
137
+ xml_file: Input XML file
138
+ xslt_file: XSLT stylesheet file
139
+ output_file: Output file path
140
+ params: Optional parameters for transformation
141
+
142
+ Returns:
143
+ True if successful, False otherwise
144
+ """
145
+ try:
146
+ # Parse XML and XSLT
147
+ if util.find_spec('lxml') is None:
148
+ error_msg = "lxml package is not available. Please install it using: pip install lxml"
149
+ logging.error(error_msg)
150
+ return False
151
+ import lxml.etree as lxml_etree
152
+ xml_doc = lxml_etree.parse(xml_file)
153
+ xslt_doc = lxml_etree.parse(xslt_file)
154
+ transform = lxml_etree.XSLT(xslt_doc)
155
+
156
+ # Apply transformation
157
+ if params:
158
+ result = transform(xml_doc, **params)
159
+ else:
160
+ result = transform(xml_doc)
161
+
162
+ # Write result
163
+ result.write(
164
+ output_file,
165
+ pretty_print=True,
166
+ xml_declaration=True,
167
+ encoding='utf-8'
168
+ )
169
+
170
+ return True
171
+ except Exception as e:
172
+ error_msg = f"Error transforming XML: {str(e)}"
173
+ logging.error(error_msg)
174
+ return False
175
+
176
+ def validate_xml(
177
+ self,
178
+ xml_file: str,
179
+ schema_file: str
180
+ ) -> Tuple[bool, Optional[str]]:
181
+ """Validate XML against XSD schema.
182
+
183
+ Args:
184
+ xml_file: XML file to validate
185
+ schema_file: XSD schema file
186
+
187
+ Returns:
188
+ Tuple of (is_valid, error_message)
189
+ """
190
+ try:
191
+ if util.find_spec('xmlschema') is None:
192
+ error_msg = "xmlschema package is not available. Please install it using: pip install xmlschema"
193
+ logging.error(error_msg)
194
+ return False, error_msg
195
+ import xmlschema
196
+ schema = xmlschema.XMLSchema(schema_file)
197
+ schema.validate(xml_file)
198
+ return True, None
199
+ except xmlschema.validators.exceptions.XMLSchemaValidationError as e:
200
+ return False, str(e)
201
+ except Exception as e:
202
+ error_msg = f"Error validating XML: {str(e)}"
203
+ logging.error(error_msg)
204
+ return False, error_msg
205
+
206
+ def xml_to_dict(
207
+ self,
208
+ root: Union[str, ET.Element],
209
+ preserve_attrs: bool = True
210
+ ) -> Dict[str, Any]:
211
+ """Convert XML to dictionary.
212
+
213
+ Args:
214
+ root: XML string or Element tree root
215
+ preserve_attrs: Keep XML attributes in result
216
+
217
+ Returns:
218
+ Dict representation of XML
219
+ """
220
+ try:
221
+ # Parse XML if string
222
+ if isinstance(root, str):
223
+ if root.startswith('<'):
224
+ root = ET.fromstring(root)
225
+ else:
226
+ root = ET.parse(root).getroot()
227
+
228
+ result = {}
229
+
230
+ # Add attributes if present and requested
231
+ if preserve_attrs and root.attrib:
232
+ result['@attributes'] = dict(root.attrib)
233
+
234
+ # Add children
235
+ children = list(root)
236
+ if not children:
237
+ text = root.text
238
+ if text is not None and text.strip():
239
+ result = text.strip()
240
+ else:
241
+ for child in children:
242
+ child_data = self.xml_to_dict(child, preserve_attrs)
243
+ if child.tag in result:
244
+ if not isinstance(result[child.tag], list):
245
+ result[child.tag] = [result[child.tag]]
246
+ result[child.tag].append(child_data)
247
+ else:
248
+ result[child.tag] = child_data
249
+
250
+ return result
251
+ except Exception as e:
252
+ error_msg = f"Error converting XML to dict: {str(e)}"
253
+ logging.error(error_msg)
254
+ return {}
255
+
256
+ def dict_to_xml(
257
+ self,
258
+ data: Dict[str, Any],
259
+ root_tag: str = 'root'
260
+ ) -> Optional[ET.Element]:
261
+ """Convert dictionary to XML.
262
+
263
+ Args:
264
+ data: Dictionary to convert
265
+ root_tag: Tag for root element
266
+
267
+ Returns:
268
+ XML Element tree root
269
+ """
270
+ try:
271
+ def _create_element(
272
+ parent: ET.Element,
273
+ key: str,
274
+ value: Any
275
+ ):
276
+ """Create XML element from key-value pair."""
277
+ if key == '@attributes':
278
+ for attr_key, attr_val in value.items():
279
+ parent.set(attr_key, str(attr_val))
280
+ elif isinstance(value, dict):
281
+ child = ET.SubElement(parent, key)
282
+ for k, v in value.items():
283
+ _create_element(child, k, v)
284
+ elif isinstance(value, list):
285
+ for item in value:
286
+ child = ET.SubElement(parent, key)
287
+ if isinstance(item, dict):
288
+ for k, v in item.items():
289
+ _create_element(child, k, v)
290
+ else:
291
+ child.text = str(item)
292
+ else:
293
+ child = ET.SubElement(parent, key)
294
+ child.text = str(value)
295
+
296
+ root = ET.Element(root_tag)
297
+ for key, value in data.items():
298
+ _create_element(root, key, value)
299
+
300
+ return root
301
+ except Exception as e:
302
+ error_msg = f"Error converting dict to XML: {str(e)}"
303
+ logging.error(error_msg)
304
+ return None
305
+
306
+ def xpath_query(
307
+ self,
308
+ root: Union[str, ET.Element],
309
+ query: str,
310
+ namespaces: Optional[Dict[str, str]] = None
311
+ ) -> List[ET.Element]:
312
+ """Execute XPath query on XML.
313
+
314
+ Args:
315
+ root: XML string or Element tree root
316
+ query: XPath query string
317
+ namespaces: Optional namespace mappings
318
+
319
+ Returns:
320
+ List of matching elements
321
+ """
322
+ try:
323
+ # Parse XML if string
324
+ if isinstance(root, str):
325
+ if root.startswith('<'):
326
+ if util.find_spec('lxml') is None:
327
+ error_msg = "lxml package is not available. Please install it using: pip install lxml"
328
+ logging.error(error_msg)
329
+ return []
330
+ import lxml.etree as lxml_etree
331
+ tree = lxml_etree.fromstring(root)
332
+ else:
333
+ tree = ET.parse(root)
334
+ else:
335
+ if util.find_spec('lxml') is None:
336
+ error_msg = "lxml package is not available. Please install it using: pip install lxml"
337
+ logging.error(error_msg)
338
+ return []
339
+ import lxml.etree as lxml_etree
340
+ tree = lxml_etree.fromstring(
341
+ ET.tostring(root, encoding='unicode')
342
+ )
343
+
344
+ # Execute query
345
+ results = tree.xpath(
346
+ query,
347
+ namespaces=namespaces or {}
348
+ )
349
+
350
+ # Convert results to standard ElementTree elements
351
+ return [
352
+ ET.fromstring(lxml_etree.tostring(elem, encoding='unicode'))
353
+ for elem in results
354
+ ]
355
+ except Exception as e:
356
+ error_msg = f"Error executing XPath query: {str(e)}"
357
+ logging.error(error_msg)
358
+ return []
359
+
360
+ # Create instance for direct function access
361
+ _xml_tools = XMLTools()
362
+ read_xml = _xml_tools.read_xml
363
+ write_xml = _xml_tools.write_xml
364
+ transform_xml = _xml_tools.transform_xml
365
+ validate_xml = _xml_tools.validate_xml
366
+ xml_to_dict = _xml_tools.xml_to_dict
367
+ dict_to_xml = _xml_tools.dict_to_xml
368
+ xpath_query = _xml_tools.xpath_query
369
+
370
+ if __name__ == "__main__":
371
+ print("\n==================================================")
372
+ print("XMLTools Demonstration")
373
+ print("==================================================\n")
374
+
375
+ # Create temporary files
376
+ import tempfile
377
+ import os
378
+
379
+ temp_file = tempfile.mktemp(suffix='.xml')
380
+ try:
381
+ print("1. Creating XML Document")
382
+ print("------------------------------")
383
+ xml_content = """<?xml version="1.0" encoding="UTF-8"?>
384
+ <bookstore>
385
+ <book category="fiction">
386
+ <title>The Great Gatsby</title>
387
+ <author>F. Scott Fitzgerald</author>
388
+ <year>1925</year>
389
+ <price>10.99</price>
390
+ </book>
391
+ <book category="non-fiction">
392
+ <title>A Brief History of Time</title>
393
+ <author>Stephen Hawking</author>
394
+ <year>1988</year>
395
+ <price>15.99</price>
396
+ </book>
397
+ </bookstore>"""
398
+
399
+ with open(temp_file, 'w') as f:
400
+ f.write(xml_content)
401
+ print("Sample XML file created")
402
+ print()
403
+
404
+ print("2. Parsing XML")
405
+ print("------------------------------")
406
+ result = read_xml(temp_file)
407
+ print("XML structure:")
408
+ print(minidom.parseString(ET.tostring(result, encoding='unicode')).toprettyxml(indent=' '))
409
+ print()
410
+
411
+ print("3. Querying XML")
412
+ print("------------------------------")
413
+ xpath = "//book[@category='fiction']/title/text()"
414
+ result = xpath_query(result, xpath)
415
+ print(f"Fiction book titles (XPath: {xpath}):")
416
+ for title in result:
417
+ print(title.text)
418
+ print()
419
+
420
+ print("4. Validating XML")
421
+ print("------------------------------")
422
+ schema_file = tempfile.mktemp(suffix='.xsd')
423
+ schema_content = """<?xml version="1.0" encoding="UTF-8"?>
424
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
425
+ <xs:element name="bookstore">
426
+ <xs:complexType>
427
+ <xs:sequence>
428
+ <xs:element name="book" maxOccurs="unbounded">
429
+ <xs:complexType>
430
+ <xs:sequence>
431
+ <xs:element name="title" type="xs:string"/>
432
+ <xs:element name="author" type="xs:string"/>
433
+ <xs:element name="year" type="xs:integer"/>
434
+ <xs:element name="price" type="xs:decimal"/>
435
+ </xs:sequence>
436
+ <xs:attribute name="category" type="xs:string"/>
437
+ </xs:complexType>
438
+ </xs:element>
439
+ </xs:sequence>
440
+ </xs:complexType>
441
+ </xs:element>
442
+ </xs:schema>"""
443
+ with open(schema_file, 'w') as f:
444
+ f.write(schema_content)
445
+ result, error = validate_xml(temp_file, schema_file)
446
+ print(f"XML validation result: {result}")
447
+ if error:
448
+ print(f"Error: {error}")
449
+ print()
450
+
451
+ print("5. Transforming XML")
452
+ print("------------------------------")
453
+ xslt_content = """<?xml version="1.0" encoding="UTF-8"?>
454
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
455
+ <xsl:template match="/">
456
+ <html>
457
+ <body>
458
+ <h2>Bookstore Inventory</h2>
459
+ <table>
460
+ <tr>
461
+ <th>Title</th>
462
+ <th>Author</th>
463
+ <th>Price</th>
464
+ </tr>
465
+ <xsl:for-each select="bookstore/book">
466
+ <tr>
467
+ <td><xsl:value-of select="title"/></td>
468
+ <td><xsl:value-of select="author"/></td>
469
+ <td><xsl:value-of select="price"/></td>
470
+ </tr>
471
+ </xsl:for-each>
472
+ </table>
473
+ </body>
474
+ </html>
475
+ </xsl:template>
476
+ </xsl:stylesheet>"""
477
+
478
+ xslt_file = tempfile.mktemp(suffix='.xslt')
479
+ with open(xslt_file, 'w') as f:
480
+ f.write(xslt_content)
481
+
482
+ output_file = tempfile.mktemp(suffix='.html')
483
+ result = transform_xml(temp_file, xslt_file, output_file)
484
+ print(f"XML transformation result: {result}")
485
+ if result and os.path.exists(output_file):
486
+ print("\nTransformed HTML content:")
487
+ with open(output_file, 'r') as f:
488
+ print(f.read())
489
+
490
+ finally:
491
+ # Clean up temporary files
492
+ for file in [temp_file, schema_file, xslt_file, output_file]:
493
+ if os.path.exists(file):
494
+ os.unlink(file)
495
+
496
+ print("\n==================================================")
497
+ print("Demonstration Complete")
498
+ print("==================================================")