Mutability¶
org-parser keeps a mutable semantic tree. You can edit document fields,
heading fields, and element lists directly, then serialize back to Org text.
Dirty flags¶
Every major semantic node tracks whether it has been mutated:
Document.dirtyHeading.dirtyElement.dirtyRichText.dirtyTimestamp.dirty
Mutating a child object marks that object dirty and bubbles the dirty flag up to its parent chain.
>>> from org_parser import loads
>>> document = loads("* TODO Task\n")
>>> document.dirty, document[0].dirty
(False, False)
>>> document[0].title = "Updated task"
>>> document.dirty, document[0].dirty
(True, True)
Preserving source formatting by default¶
When a parsed tree is still clean (dirty == False), rendering returns the
original source slices, including the original formatting and ordering.
>>> from org_parser import loads
>>> document = loads("""Some text.
... #+TITLE: Title
... More text
... """)
>>> document.render()
'Some text.\n#+TITLE: Title\nMore text\n'
Reconstructed output when dirty¶
Once the tree is dirty, output is reconstructed from semantic fields rather than copied from parse-tree source slices.
>>> from org_parser import loads
>>> document = loads("""Some text.
... #+TITLE: Title
... More text
... """)
>>> document.mark_dirty()
>>> document.render()
'#+TITLE: Title\nSome text.\nMore text\n'
reformat() operation¶
Use reformat() to force a subtree into scratch-built rendering style.
This marks the subtree dirty and normalizes output according to render logic.
>>> from org_parser import loads
>>> document = loads("""* Heading 1
... ** Heading 2
... CLOSED: <2025-10-10>
... SCHEDULED: <2025-10-10>
... """)
>>> document.reformat()
>>> print(document.render())
* Heading 1
** Heading 2
SCHEDULED: <2025-10-10> CLOSED: <2025-10-10>
DirtyList wrapper¶
List-like properties such as document.children, document.body,
heading.children, and heading.body return a DirtyList wrapper.
DirtyList behaves like a regular mutable list, but each in-place mutation
(append, insert, pop, remove, clear, reverse, and extend) runs an
internal callback that updates ownership/structure and marks parents dirty.
>>> from org_parser import loads
>>> document = loads("* Parent\n")
>>> document.dirty
False
>>> child = loads("* Child\n")[0]
>>> document[0].children.append(child)
>>> document.dirty
True
>>> document[1].heading_text
'** Child'
>>> from org_parser import loads
>>> from org_parser.element import Paragraph
>>> heading = loads("* TODO Task\n")[0]
>>> heading.body.append(Paragraph.from_source("Extra details"))
>>> heading.dirty, heading.parent.dirty
(True, True)
>>> heading.render()
'* TODO Task\nExtra details\n'