A lightweight Python library for parsing custom markup languages, built and used by AutoGPT
GravitasML is purpose-built for parsing simple markup structures, particularly LLM-generated outputs.
By design, it excludes XML features that can introduce security risks:
Perfect for:
GravitasML is immune to common XML vulnerabilities because it simply doesn't implement the features that enable them:
| Attack Type | GravitasML |
|---|---|
| Billion Laughs | ✅ Safe (no entity support) |
| Quadratic Blowup | ✅ Safe (no entity expansion) |
| External Entity Expansion (XXE) | ✅ Safe (no external resources) |
| DTD Retrieval | ✅ Safe (no DTD support) |
| Decompression Bomb | ✅ Safe (no decompression) |
Perfect for parsing LLM outputs and other scenarios where you need simple, secure markup processing.
GravitasML transforms custom markup into Python data structures:
| no_parse filter to preserve raw content without parsingpip install gravitasml
Or with Poetry:
poetry add gravitasml
from gravitasml.token import tokenize
from gravitasml.parser import Parser
# Parse simple markup
markup = "<name>GravitasML</name>"
tokens = tokenize(markup)
parser = Parser(tokens)
result = parser.parse()
print(result) # {'name': 'GravitasML'}
from gravitasml.token import tokenize
from gravitasml.parser import Parser
markup = """
<person>
<name>John Doe</name>
<contact>
<email>john@example.com</email>
<phone>555-0123</phone>
</contact>
</person>
"""
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {
# 'person': {
# 'name': 'John Doe',
# 'contact': {
# 'email': 'john@example.com',
# 'phone': '555-0123'
# }
# }
# }
Transform your markup directly into validated Pydantic models:
from pydantic import BaseModel
from gravitasml.token import tokenize
from gravitasml.parser import Parser
class Contact(BaseModel):
email: str
phone: str
class Person(BaseModel):
name: str
contact: Contact
markup = """
<person>
<name>Jane Smith</name>
<contact>
<email>jane@example.com</email>
<phone>555-9876</phone>
</contact>
</person>
"""
tokens = tokenize(markup)
parser = Parser(tokens)
person = parser.parse_to_pydantic(Person)
print(person.name) # Jane Smith
print(person.contact.email) # jane@example.com
GravitasML automatically converts repeated tags into lists:
from gravitasml.token import tokenize
from gravitasml.parser import Parser
markup = "<tag><a>value1</a><a>value2</a></tag>"
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {'tag': [{'a': 'value1'}, {'a': 'value2'}]}
# Multiple root tags with the same name also become a list
markup2 = "<tag>content1</tag><tag>content2</tag>"
tokens2 = tokenize(markup2)
result2 = Parser(tokens2).parse()
# Result: [{'tag': 'content1'}, {'tag': 'content2'}]
Tag names are automatically normalized - spaces become underscores and names are lowercased:
from gravitasml.token import tokenize
from gravitasml.parser import Parser
# Spaces in tag names are converted to underscores
markup = "<User Profile><First Name>Alice</First Name></User Profile>"
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {'user_profile': {'first_name': 'Alice'}}
Use the | no_parse filter to prevent recursive parsing of content, keeping it as a raw string:
from gravitasml.token import tokenize
from gravitasml.parser import Parser, parse_markup
# Content inside no_parse tags is preserved as raw string
markup = '<html | no_parse><div class="example"><p>Hello <strong>world</strong></p></div></html>'
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {'html': '<div class="example"><p>Hello<strong>world</strong></p></div>'}
# Use the convenience function for automatic whitespace preservation
result = parse_markup('<tag | no_parse> <inner> content </inner> </tag>')
# Result: {'tag': ' <inner> content </inner> '}
# Mix parsed and no_parse content
mixed_markup = """
<document>
<title>My Document</title>
<raw_html | no_parse>
<div class="content">
<p>This HTML is preserved exactly as written</p>
<script>alert('Even scripts!')</script>
</div>
</raw_html>
<processed>This content is parsed normally</processed>
</document>
"""
result = parse_markup(mixed_markup)
# Result: {
# 'document': {
# 'title': 'My Document',
# 'raw_html': '<div class="content"><p>This HTML is preserved exactly as written</p><script>alert(\'Even scripts!\')</script></div>',
# 'processed': 'This content is parsed normally'
# }
# }
Use Cases for No-Parse Filter:
GravitasML uses a two-stage parsing approach:
gravitasml.token) - Converts raw markup into a stream of tokensgravitasml.parser) - Builds a tree structure and converts to Python objectsGravitasML comes with a test suite. To run the tests, execute the following command:
python -m unittest discover -v
GravitasML has minimal dependencies:
We welcome contributions! GravitasML uses:
To contribute:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)See our CI/CD workflow for the automated checks your PR must pass.
GravitasML is designed for simplicity. It currently does not support:
<tag attr="value">)<tag />)These limitations are intentional to keep the library focused and easy to use. If you need these features, consider using Python's built-in xml.etree.ElementTree or third-party libraries like lxml.
GravitasML is built on the principle that not every markup parsing task needs the complexity of full XML processing. Sometimes you just want to convert simple markup to Python dictionaries without the overhead of namespaces, DTDs, or complex validation rules.
GravitasML is licensed under the MIT License - see the LICENSE file for details.
Built by the AutoGPT Team and used in the AutoGPT project.
Simple markup parsing for modern Python applications.
Python
100.0%
A lightweight Python library for parsing custom markup languages, built and used by AutoGPT
GravitasML is purpose-built for parsing simple markup structures, particularly LLM-generated outputs.
By design, it excludes XML features that can introduce security risks:
Perfect for:
GravitasML is immune to common XML vulnerabilities because it simply doesn't implement the features that enable them:
| Attack Type | GravitasML |
|---|---|
| Billion Laughs | ✅ Safe (no entity support) |
| Quadratic Blowup | ✅ Safe (no entity expansion) |
| External Entity Expansion (XXE) | ✅ Safe (no external resources) |
| DTD Retrieval | ✅ Safe (no DTD support) |
| Decompression Bomb | ✅ Safe (no decompression) |
Perfect for parsing LLM outputs and other scenarios where you need simple, secure markup processing.
GravitasML transforms custom markup into Python data structures:
| no_parse filter to preserve raw content without parsingpip install gravitasml
Or with Poetry:
poetry add gravitasml
from gravitasml.token import tokenize
from gravitasml.parser import Parser
# Parse simple markup
markup = "<name>GravitasML</name>"
tokens = tokenize(markup)
parser = Parser(tokens)
result = parser.parse()
print(result) # {'name': 'GravitasML'}
from gravitasml.token import tokenize
from gravitasml.parser import Parser
markup = """
<person>
<name>John Doe</name>
<contact>
<email>john@example.com</email>
<phone>555-0123</phone>
</contact>
</person>
"""
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {
# 'person': {
# 'name': 'John Doe',
# 'contact': {
# 'email': 'john@example.com',
# 'phone': '555-0123'
# }
# }
# }
Transform your markup directly into validated Pydantic models:
from pydantic import BaseModel
from gravitasml.token import tokenize
from gravitasml.parser import Parser
class Contact(BaseModel):
email: str
phone: str
class Person(BaseModel):
name: str
contact: Contact
markup = """
<person>
<name>Jane Smith</name>
<contact>
<email>jane@example.com</email>
<phone>555-9876</phone>
</contact>
</person>
"""
tokens = tokenize(markup)
parser = Parser(tokens)
person = parser.parse_to_pydantic(Person)
print(person.name) # Jane Smith
print(person.contact.email) # jane@example.com
GravitasML automatically converts repeated tags into lists:
from gravitasml.token import tokenize
from gravitasml.parser import Parser
markup = "<tag><a>value1</a><a>value2</a></tag>"
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {'tag': [{'a': 'value1'}, {'a': 'value2'}]}
# Multiple root tags with the same name also become a list
markup2 = "<tag>content1</tag><tag>content2</tag>"
tokens2 = tokenize(markup2)
result2 = Parser(tokens2).parse()
# Result: [{'tag': 'content1'}, {'tag': 'content2'}]
Tag names are automatically normalized - spaces become underscores and names are lowercased:
from gravitasml.token import tokenize
from gravitasml.parser import Parser
# Spaces in tag names are converted to underscores
markup = "<User Profile><First Name>Alice</First Name></User Profile>"
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {'user_profile': {'first_name': 'Alice'}}
Use the | no_parse filter to prevent recursive parsing of content, keeping it as a raw string:
from gravitasml.token import tokenize
from gravitasml.parser import Parser, parse_markup
# Content inside no_parse tags is preserved as raw string
markup = '<html | no_parse><div class="example"><p>Hello <strong>world</strong></p></div></html>'
tokens = tokenize(markup)
result = Parser(tokens).parse()
# Result: {'html': '<div class="example"><p>Hello<strong>world</strong></p></div>'}
# Use the convenience function for automatic whitespace preservation
result = parse_markup('<tag | no_parse> <inner> content </inner> </tag>')
# Result: {'tag': ' <inner> content </inner> '}
# Mix parsed and no_parse content
mixed_markup = """
<document>
<title>My Document</title>
<raw_html | no_parse>
<div class="content">
<p>This HTML is preserved exactly as written</p>
<script>alert('Even scripts!')</script>
</div>
</raw_html>
<processed>This content is parsed normally</processed>
</document>
"""
result = parse_markup(mixed_markup)
# Result: {
# 'document': {
# 'title': 'My Document',
# 'raw_html': '<div class="content"><p>This HTML is preserved exactly as written</p><script>alert(\'Even scripts!\')</script></div>',
# 'processed': 'This content is parsed normally'
# }
# }
Use Cases for No-Parse Filter:
GravitasML uses a two-stage parsing approach:
gravitasml.token) - Converts raw markup into a stream of tokensgravitasml.parser) - Builds a tree structure and converts to Python objectsGravitasML comes with a test suite. To run the tests, execute the following command:
python -m unittest discover -v
GravitasML has minimal dependencies:
We welcome contributions! GravitasML uses:
To contribute:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)See our CI/CD workflow for the automated checks your PR must pass.
GravitasML is designed for simplicity. It currently does not support:
<tag attr="value">)<tag />)These limitations are intentional to keep the library focused and easy to use. If you need these features, consider using Python's built-in xml.etree.ElementTree or third-party libraries like lxml.
GravitasML is built on the principle that not every markup parsing task needs the complexity of full XML processing. Sometimes you just want to convert simple markup to Python dictionaries without the overhead of namespaces, DTDs, or complex validation rules.
GravitasML is licensed under the MIT License - see the LICENSE file for details.
Built by the AutoGPT Team and used in the AutoGPT project.
Simple markup parsing for modern Python applications.
Python
100.0%