regexml 0.1.0__tar.gz
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.
- regexml-0.1.0/.gitignore +56 -0
- regexml-0.1.0/LICENSE +21 -0
- regexml-0.1.0/PKG-INFO +168 -0
- regexml-0.1.0/PROGRESS.md +111 -0
- regexml-0.1.0/README.md +141 -0
- regexml-0.1.0/SPECIFICATION.md +315 -0
- regexml-0.1.0/pyproject.toml +46 -0
- regexml-0.1.0/src/regexml/__init__.py +24 -0
- regexml-0.1.0/src/regexml/attributes.py +60 -0
- regexml-0.1.0/src/regexml/extractor.py +121 -0
- regexml-0.1.0/src/regexml/inspector.py +85 -0
- regexml-0.1.0/src/regexml/models.py +176 -0
- regexml-0.1.0/src/regexml/nested.py +150 -0
- regexml-0.1.0/src/regexml/utils.py +29 -0
- regexml-0.1.0/tests/test_basic.py +64 -0
- regexml-0.1.0/tests/test_chaining.py +62 -0
- regexml-0.1.0/tests/test_inspector.py +57 -0
- regexml-0.1.0/tests/test_malformed.py +64 -0
- regexml-0.1.0/tests/test_nested.py +54 -0
regexml-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# Virtual environments
|
|
30
|
+
.env
|
|
31
|
+
.venv
|
|
32
|
+
env/
|
|
33
|
+
venv/
|
|
34
|
+
ENV/
|
|
35
|
+
env.bak/
|
|
36
|
+
venv.bak/
|
|
37
|
+
|
|
38
|
+
# Testing & Coverage
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# IDE & OS
|
|
52
|
+
.vscode/
|
|
53
|
+
.idea/
|
|
54
|
+
*.swp
|
|
55
|
+
*.swo
|
|
56
|
+
.DS_Store
|
regexml-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ishan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
regexml-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: regexml
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight, fault-tolerant, text-first XML extraction library for Python.
|
|
5
|
+
Project-URL: Homepage, https://github.com/visitishan/regexml
|
|
6
|
+
Project-URL: Repository, https://github.com/visitishan/regexml
|
|
7
|
+
Project-URL: Issues, https://github.com/visitishan/regexml/issues
|
|
8
|
+
Author-email: Ishan <ishan@example.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: extractor,fault-tolerant,llm,parser,regex,scraping,xml
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# regexml 🚀
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/regexml/)
|
|
31
|
+
[](https://python.org)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
34
|
+
A lightweight, fault-tolerant, text-first XML extraction library for Python. Available on PyPI: **[pypi.org/project/regexml](https://pypi.org/project/regexml/)**.
|
|
35
|
+
|
|
36
|
+
Unlike traditional XML parsers (`xml.etree.ElementTree`, `lxml`) that enforce strict document validation and crash with `ParseError` on incomplete or malformed markup, **`regexml`** treats XML purely as plain text. It extracts values directly using regular expressions and resilient pattern matching.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🌟 Why `regexml`?
|
|
41
|
+
|
|
42
|
+
- **Fault-Tolerant & Resilient:** Doesn't crash on unescaped `&`, `<`, `>`, missing closing tags, or broken XML headers.
|
|
43
|
+
- **Perfect for LLM Outputs:** Cleanly extract tags like `<thought>`, `<answer>`, or `<code>` from AI responses.
|
|
44
|
+
- **Fluent Dot-Syntax Chaining:** Call `data.tagvalue("div").tagvalue("name")` seamlessly.
|
|
45
|
+
- **Balanced Outermost Tag Extraction:** Accurately captures outer tags with nested tags of the same name without premature cut-offs.
|
|
46
|
+
- **Truncated Stream Recovery:** If an XML stream cuts off prematurely (e.g. at line 1000 of 1200), it safely captures data up to EOF without failing.
|
|
47
|
+
- **Tag Inspector & Hierarchy:** Built-in `check_tags()` discovers all tags, reports unclosed/truncated tags, and renders visual ASCII trees.
|
|
48
|
+
- **Zero Heavy Dependencies:** Built 100% on Python standard library.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 📦 Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install regexml
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## ⚡ Quick Start
|
|
61
|
+
|
|
62
|
+
### 1. Fluent Dot-Syntax (`XMLText`)
|
|
63
|
+
|
|
64
|
+
Wrap any string with `XMLText` to unlock dot-methods while retaining 100% string compatibility:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from regexml import XMLText
|
|
68
|
+
|
|
69
|
+
raw_data = """
|
|
70
|
+
<article id="101">
|
|
71
|
+
<title>Resilient XML Extraction</title>
|
|
72
|
+
<author>
|
|
73
|
+
<first_name>Ishan</first_name>
|
|
74
|
+
<last_name>Jain</last_name>
|
|
75
|
+
</author>
|
|
76
|
+
</article>
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
doc = XMLText(raw_data)
|
|
80
|
+
|
|
81
|
+
# Extract single tag value:
|
|
82
|
+
print(doc.tagvalue("title"))
|
|
83
|
+
# Output: Resilient XML Extraction
|
|
84
|
+
|
|
85
|
+
# Method Chaining:
|
|
86
|
+
print(doc.tagvalue("author").tagvalue("first_name"))
|
|
87
|
+
# Output: Ishan
|
|
88
|
+
|
|
89
|
+
# Path Shorthand:
|
|
90
|
+
print(doc.tagvalue("author/last_name"))
|
|
91
|
+
# Output: Jain
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 2. Functional API (Without Wrapping)
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from regexml import tagvalue, tagvalues
|
|
98
|
+
|
|
99
|
+
data = "<user><name>Alice</name><name>Bob</name></user>"
|
|
100
|
+
|
|
101
|
+
print(tagvalue(data, "name")) # Alice
|
|
102
|
+
print(tagvalues(data, "name")) # ['Alice', 'Bob']
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🎯 Advanced Features
|
|
108
|
+
|
|
109
|
+
### Balanced Nested Tags of the Same Name
|
|
110
|
+
Standard regex chops off at the first closing tag. `regexml` tracks depth counter to return the true balanced outermost tag:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
xml = """
|
|
114
|
+
<div id="outer">
|
|
115
|
+
Hello
|
|
116
|
+
<div id="inner">World</div>
|
|
117
|
+
Welcome!
|
|
118
|
+
</div>
|
|
119
|
+
"""
|
|
120
|
+
doc = XMLText(xml)
|
|
121
|
+
print(doc.tagvalue("div"))
|
|
122
|
+
# Output:
|
|
123
|
+
# Hello
|
|
124
|
+
# <div id="inner">World</div>
|
|
125
|
+
# Welcome!
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Truncated / Broken XML Recovery
|
|
129
|
+
If your XML was cut off mid-stream (e.g., from network drop or LLM token limit):
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
truncated_xml = "<user><id>123</id><bio>Software engineer who loves Python... [CUT OFF]"
|
|
133
|
+
doc = XMLText(truncated_xml)
|
|
134
|
+
|
|
135
|
+
bio = doc.tagvalue("bio")
|
|
136
|
+
print(bio) # "Software engineer who loves Python... [CUT OFF]"
|
|
137
|
+
print(bio.is_truncated) # True
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Tag Inspection & Broken Markup Detection (`check_tags`)
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
report = doc.check_tags()
|
|
144
|
+
|
|
145
|
+
# View all tags found:
|
|
146
|
+
print(report.tags) # ['user', 'id', 'bio']
|
|
147
|
+
|
|
148
|
+
# Pinpoint unclosed tags (truncated points):
|
|
149
|
+
print(report.unclosed_tags) # ['user', 'bio']
|
|
150
|
+
|
|
151
|
+
# Print terminal ASCII tree:
|
|
152
|
+
print(report.tree())
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Output:**
|
|
156
|
+
```text
|
|
157
|
+
└── user (unclosed)
|
|
158
|
+
├── id
|
|
159
|
+
└── bio (unclosed)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The tree instantly visualizes the markup hierarchy and flags unclosed tags, making it easy to pinpoint where the XML stream cut off.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 📄 License
|
|
167
|
+
|
|
168
|
+
MIT License. Developed by Ishan.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Project Progress & Roadmap: `regexml`
|
|
2
|
+
|
|
3
|
+
This document tracks the end-to-end development lifecycle of the **`regexml`** library. Use this checklist to monitor current progress, upcoming milestones, and completed features.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📊 Overall Progress Summary
|
|
8
|
+
|
|
9
|
+
| Metric | Status |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **Current Phase** | Phase 6: Packaging, Documentation & Release |
|
|
12
|
+
| **Overall Completion** | 85% |
|
|
13
|
+
| **Target Python Version** | Python 3.9+ |
|
|
14
|
+
| **Dependencies** | Python Standard Library (`re`, `typing`, `dataclasses`) |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🗺️ Development Phases
|
|
19
|
+
|
|
20
|
+
### Phase 1: Specification & Project Foundation ✅ (Completed)
|
|
21
|
+
- [x] Create Technical Specification document (`SPECIFICATION.md`)
|
|
22
|
+
- [x] Create Progress Tracking document (`PROGRESS.md`)
|
|
23
|
+
- [x] Initialize Git repository & `.gitignore`
|
|
24
|
+
- [x] Create GitHub repository: [visitishan/regexml](https://github.com/visitishan/regexml)
|
|
25
|
+
- [x] Initialize Python package layout (`src/regexml/`, `tests/`)
|
|
26
|
+
- [x] Create `pyproject.toml` with build metadata, dependencies, and linting tools
|
|
27
|
+
- [x] Create initial `README.md` with problem statement, quick start, and documentation
|
|
28
|
+
- [x] Create `LICENSE` (MIT)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### Phase 2: Core Regex Extraction Engine ✅ (Completed)
|
|
33
|
+
- [x] Implement data structures (`TagMatch`) in `models.py`
|
|
34
|
+
- [x] Implement core single-tag extractor `tagvalue(text, tag)` in `extractor.py`
|
|
35
|
+
- [x] Implement multi-occurrence extractor `tagvalues(text, tag)`
|
|
36
|
+
- [x] Implement `XMLText(str)` subclass wrapper for fluent dot-syntax (`my_text.tagvalue("div")`)
|
|
37
|
+
- [x] Implement Method Chaining support (`my_text.tagvalue("div").tagvalue("name").tagvalue("first_name")`)
|
|
38
|
+
- [x] Implement Safe Chaining (prevent `AttributeError` when intermediate tags are missing)
|
|
39
|
+
- [x] Implement Path Shorthand (`my_text.tagvalue("div/name/first_name")`)
|
|
40
|
+
- [x] Add basic parameters:
|
|
41
|
+
- [x] `include_tags: bool` (keep or strip surrounding `<tag>...</tag>`)
|
|
42
|
+
- [x] `strip_whitespace: bool`
|
|
43
|
+
- [x] `ignore_case: bool`
|
|
44
|
+
- [x] `multiline: bool` (DOTALL support)
|
|
45
|
+
- [x] Write unit tests for standard and clean XML snippets (`test_basic.py`)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### Phase 3: Fault-Tolerance & Attribute Handling ✅ (Completed)
|
|
50
|
+
- [x] Implement resilient attribute parser (`attributes.py`):
|
|
51
|
+
- [x] Support double-quoted, single-quoted, and unquoted values
|
|
52
|
+
- [x] Support boolean attributes (e.g. `<input disabled>`)
|
|
53
|
+
- [x] Filter extraction by matching attributes (`match_attributes={'id': 'main'}`)
|
|
54
|
+
- [x] Implement self-closing tag handling (`<tag ... />`)
|
|
55
|
+
- [x] Implement `tagmatch(text, tag)` returning `TagMatch` objects
|
|
56
|
+
- [x] Implement Truncated XML Graceful Recovery (`allow_unclosed=True`):
|
|
57
|
+
- [x] Extract content up to EOF or next peer tag when closing tag is missing
|
|
58
|
+
- [x] Attach `is_truncated = True` flag to matches
|
|
59
|
+
- [x] Implement CDATA handling (`<![CDATA[ ... ]]>`)
|
|
60
|
+
- [x] Write tests for dirty, truncated, and malformed XML (`test_malformed.py`)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Phase 4: Advanced Scenarios (Nesting, Hierarchy & Inspection) ✅ (Completed)
|
|
65
|
+
- [x] Implement balanced depth-counter scanner for identical nested tags (`nested.py`)
|
|
66
|
+
- [x] Guarantee outermost opening tag matches corresponding balanced closing tag
|
|
67
|
+
- [x] Fallback graceful slice if XML is truncated mid-nest
|
|
68
|
+
- [x] Implement Tag Inspection & Hierarchy Engine (`check_tags()` in `inspector.py`):
|
|
69
|
+
- [x] Extract all distinct tags and their frequency counts
|
|
70
|
+
- [x] Identify unclosed / truncated tags via stack analysis
|
|
71
|
+
- [x] Build nested parent-child hierarchy map
|
|
72
|
+
- [x] Generate visual ASCII tree representation (`report.tree()`)
|
|
73
|
+
- [x] Handle XML namespaces:
|
|
74
|
+
- [x] Strict mode (`ns:tag`)
|
|
75
|
+
- [x] Relaxed mode (match tag name regardless of namespace prefix)
|
|
76
|
+
- [x] Implement batch extractor `extract_tags(text, tags=['a', 'b', 'c'])`
|
|
77
|
+
- [x] Implement `strip_inner_tags: bool` to recursively remove all inner child markup
|
|
78
|
+
- [x] Write tests for deeply nested, inspected, and namespaced tags (`test_nested.py`, `test_inspector.py`)
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### Phase 5: LLM & Real-world Edge Cases ✅ (Completed)
|
|
83
|
+
- [x] Build test suite with real LLM prompt outputs (e.g., `<thought>`, `<code>`, markdown inside XML)
|
|
84
|
+
- [x] Handle unescaped characters (`<`, `>`, `&`, unclosed brackets in text)
|
|
85
|
+
- [x] Add comment stripping (`ignore_comments=True`)
|
|
86
|
+
- [x] All 23 unit tests across 5 test suites passing with 100% success rate
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Phase 6: Packaging, Documentation & Release ⏳ (In Progress)
|
|
91
|
+
- [x] Write full API reference in `README.md` with copy-pasteable examples
|
|
92
|
+
- [x] Setup `unittest` test suite with 23 passing test cases
|
|
93
|
+
- [ ] Verify package build with `python -m build`
|
|
94
|
+
- [ ] Add PyPI publishing workflow / instructions
|
|
95
|
+
- [ ] Final code review and version `0.1.0` tag
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 📝 Activity & Decision Log
|
|
100
|
+
|
|
101
|
+
| Date | Activity / Decision | Status |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| **2026-10-31** | Project inception: Decided on text-first, regex-based XML extraction to handle malformed XML & LLM outputs. | Done |
|
|
104
|
+
| **2026-10-31** | Created `SPECIFICATION.md` detailing API signatures, regex patterns, nesting strategy, and fault tolerance. | Done |
|
|
105
|
+
| **2026-10-31** | Created `PROGRESS.md` to track milestones across all 6 phases. | Done |
|
|
106
|
+
| **2026-11-01** | Added `XMLText(str)` subclass for fluent dot-syntax (`my_text.tagvalue("div")`) and finalized Outermost Balanced Depth Scanner algorithm in spec. | Done |
|
|
107
|
+
| **2026-11-01** | Designed Method Chaining (`.tagvalue().tagvalue()`), Safe Chaining (no AttributeError), and Path shorthand (`"div/name/first_name"`). | Done |
|
|
108
|
+
| **2026-11-01** | Designed Truncated XML Graceful Recovery (`allow_unclosed=True`, `is_truncated`) and Tag Inspection & Hierarchy Engine (`check_tags()` + `.tree()`). | Done |
|
|
109
|
+
| **2026-11-01** | Created public GitHub repository [visitishan/regexml](https://github.com/visitishan/regexml) and pushed initial commit. | Done |
|
|
110
|
+
| **2026-11-01** | Implemented core engine: `models.py`, `extractor.py`, `nested.py`, `inspector.py`, `attributes.py`, `utils.py`. | Done |
|
|
111
|
+
| **2026-11-01** | Added 23 unit tests across 5 test suites (`test_basic.py`, `test_nested.py`, `test_chaining.py`, `test_malformed.py`, `test_inspector.py`) — 100% passed. | Done |
|
regexml-0.1.0/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# regexml 🚀
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/regexml/)
|
|
4
|
+
[](https://python.org)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
A lightweight, fault-tolerant, text-first XML extraction library for Python. Available on PyPI: **[pypi.org/project/regexml](https://pypi.org/project/regexml/)**.
|
|
8
|
+
|
|
9
|
+
Unlike traditional XML parsers (`xml.etree.ElementTree`, `lxml`) that enforce strict document validation and crash with `ParseError` on incomplete or malformed markup, **`regexml`** treats XML purely as plain text. It extracts values directly using regular expressions and resilient pattern matching.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🌟 Why `regexml`?
|
|
14
|
+
|
|
15
|
+
- **Fault-Tolerant & Resilient:** Doesn't crash on unescaped `&`, `<`, `>`, missing closing tags, or broken XML headers.
|
|
16
|
+
- **Perfect for LLM Outputs:** Cleanly extract tags like `<thought>`, `<answer>`, or `<code>` from AI responses.
|
|
17
|
+
- **Fluent Dot-Syntax Chaining:** Call `data.tagvalue("div").tagvalue("name")` seamlessly.
|
|
18
|
+
- **Balanced Outermost Tag Extraction:** Accurately captures outer tags with nested tags of the same name without premature cut-offs.
|
|
19
|
+
- **Truncated Stream Recovery:** If an XML stream cuts off prematurely (e.g. at line 1000 of 1200), it safely captures data up to EOF without failing.
|
|
20
|
+
- **Tag Inspector & Hierarchy:** Built-in `check_tags()` discovers all tags, reports unclosed/truncated tags, and renders visual ASCII trees.
|
|
21
|
+
- **Zero Heavy Dependencies:** Built 100% on Python standard library.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📦 Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install regexml
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## ⚡ Quick Start
|
|
34
|
+
|
|
35
|
+
### 1. Fluent Dot-Syntax (`XMLText`)
|
|
36
|
+
|
|
37
|
+
Wrap any string with `XMLText` to unlock dot-methods while retaining 100% string compatibility:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from regexml import XMLText
|
|
41
|
+
|
|
42
|
+
raw_data = """
|
|
43
|
+
<article id="101">
|
|
44
|
+
<title>Resilient XML Extraction</title>
|
|
45
|
+
<author>
|
|
46
|
+
<first_name>Ishan</first_name>
|
|
47
|
+
<last_name>Jain</last_name>
|
|
48
|
+
</author>
|
|
49
|
+
</article>
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
doc = XMLText(raw_data)
|
|
53
|
+
|
|
54
|
+
# Extract single tag value:
|
|
55
|
+
print(doc.tagvalue("title"))
|
|
56
|
+
# Output: Resilient XML Extraction
|
|
57
|
+
|
|
58
|
+
# Method Chaining:
|
|
59
|
+
print(doc.tagvalue("author").tagvalue("first_name"))
|
|
60
|
+
# Output: Ishan
|
|
61
|
+
|
|
62
|
+
# Path Shorthand:
|
|
63
|
+
print(doc.tagvalue("author/last_name"))
|
|
64
|
+
# Output: Jain
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. Functional API (Without Wrapping)
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from regexml import tagvalue, tagvalues
|
|
71
|
+
|
|
72
|
+
data = "<user><name>Alice</name><name>Bob</name></user>"
|
|
73
|
+
|
|
74
|
+
print(tagvalue(data, "name")) # Alice
|
|
75
|
+
print(tagvalues(data, "name")) # ['Alice', 'Bob']
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 🎯 Advanced Features
|
|
81
|
+
|
|
82
|
+
### Balanced Nested Tags of the Same Name
|
|
83
|
+
Standard regex chops off at the first closing tag. `regexml` tracks depth counter to return the true balanced outermost tag:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
xml = """
|
|
87
|
+
<div id="outer">
|
|
88
|
+
Hello
|
|
89
|
+
<div id="inner">World</div>
|
|
90
|
+
Welcome!
|
|
91
|
+
</div>
|
|
92
|
+
"""
|
|
93
|
+
doc = XMLText(xml)
|
|
94
|
+
print(doc.tagvalue("div"))
|
|
95
|
+
# Output:
|
|
96
|
+
# Hello
|
|
97
|
+
# <div id="inner">World</div>
|
|
98
|
+
# Welcome!
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Truncated / Broken XML Recovery
|
|
102
|
+
If your XML was cut off mid-stream (e.g., from network drop or LLM token limit):
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
truncated_xml = "<user><id>123</id><bio>Software engineer who loves Python... [CUT OFF]"
|
|
106
|
+
doc = XMLText(truncated_xml)
|
|
107
|
+
|
|
108
|
+
bio = doc.tagvalue("bio")
|
|
109
|
+
print(bio) # "Software engineer who loves Python... [CUT OFF]"
|
|
110
|
+
print(bio.is_truncated) # True
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Tag Inspection & Broken Markup Detection (`check_tags`)
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
report = doc.check_tags()
|
|
117
|
+
|
|
118
|
+
# View all tags found:
|
|
119
|
+
print(report.tags) # ['user', 'id', 'bio']
|
|
120
|
+
|
|
121
|
+
# Pinpoint unclosed tags (truncated points):
|
|
122
|
+
print(report.unclosed_tags) # ['user', 'bio']
|
|
123
|
+
|
|
124
|
+
# Print terminal ASCII tree:
|
|
125
|
+
print(report.tree())
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Output:**
|
|
129
|
+
```text
|
|
130
|
+
└── user (unclosed)
|
|
131
|
+
├── id
|
|
132
|
+
└── bio (unclosed)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The tree instantly visualizes the markup hierarchy and flags unclosed tags, making it easy to pinpoint where the XML stream cut off.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 📄 License
|
|
140
|
+
|
|
141
|
+
MIT License. Developed by Ishan.
|