Skip to content

Document structure

This section documents the primary document tree objects.

document

Document-level parsing, semantic classes, and raw tree access.

This subpackage provides:

  • org_parser.document.Document — the top-level semantic representation of an Org file, including keyword properties (TITLE, AUTHOR, …), the zeroth-section body, and top-level headings.
  • org_parser.document.Heading — a heading / sub-heading with its parsed components (level, TODO state, priority, title, tags, body, sub-headings).

Classes:

Name Description
Document

Representation of a full Org Mode document.

Heading

An Org Mode heading (or sub-heading).

ParseError

A single parse error captured during semantic extraction.

Document

Document(
    *,
    filename: str,
    title: RichText | str | None = None,
    author: RichText | str | None = None,
    category: RichText | str | None = None,
    description: RichText | str | None = None,
    todo: RichText | str | None = None,
    keywords: Sequence[Keyword] = (),
    properties: Properties | None = None,
    logbook: Logbook | None = None,
    body: Sequence[Element] = (),
    children: Sequence[Heading] = (),
)

Representation of a full Org Mode document.

A org_parser.document.Document exposes the zeroth-section body elements, top-level headings, and well-known keyword properties (TITLE, AUTHOR, FILETAGS, etc.) parsed from the file.

Parameters:

Name Type Description Default
filename str

The filename of the document.

required
title RichText | str | None

The value of the #+TITLE: keyword, or None.

None
author RichText | str | None

The value of the #+AUTHOR: keyword, or None.

None
category RichText | str | None

The value of the #+CATEGORY: keyword, or None.

None
description RichText | str | None

The value of the #+DESCRIPTION: keyword, or None.

None
todo RichText | str | None

The value of the #+TODO: keyword, or None.

None
keywords Sequence[Keyword]

All special keywords as an ordered list of org_parser.element.Keyword objects. Keywords in this list that share a key with one of the dedicated parameters above will override the dedicated value (last-write-wins).

()
properties Properties | None

Merged zeroth-section PROPERTIES drawer, or None.

None
logbook Logbook | None

Merged zeroth-section LOGBOOK drawer, or None.

None
body Sequence[Element]

Zeroth-section elements (excluding headings and special keywords and dedicated drawers).

()
children Sequence[Heading]

Top-level headings.

()

Example:

>>> from org_parser import loads
>>> document = loads('''
... * TODO Heading 1
... ** TODO Heading 2
... *** TODO Heading 3
... ''')
>>> document[0].title_text
'Heading 1'
>>> document[1].heading_text
'** TODO Heading 2'

Methods:

Name Description
__getitem__

Return one (or slice) of headings from the document, including subheadings.

__iter__

Iterate over all headings in file-definition order.

__len__

Return number of headings across the full document tree.

__repr__

Return a tree-oriented representation for debugging.

__str__

Return a textual representation of the document zeroth section.

from_source

Build a org_parser.document.Document from Org source text.

from_tree

Build a org_parser.document.Document from a tree-sitter parse tree.

heading_by_id

Return the heading with Org ID equal to heading_id.

heading_by_title

Return the heading with title text equal to heading_title.

mark_dirty

Mark this document as dirty.

reformat

Reformat the entire document.

render

Return the complete Org Mode text for a document including headings.

report_error

Record a parse error for node.

source_for

Return source bytes for one node span.

sync_heading_id_index

Rebuild heading lookup caches for IDs and titles.

sync_heading_title_index

Rebuild heading lookup caches for titles and IDs.

Attributes:

Name Type Description
all_headings list[Heading]

All headings in file-definition order across the full document tree.

all_states list[str]

All discovered TODO keyword states from the #+TODO: definition.

author RichText | None

The #+AUTHOR: value, or None.

body list[Element]

Zeroth-section body elements (excludes keywords and headings).

body_text str

Stringified text for all zeroth-section body elements.

category RichText | None

The effective category for this document.

children list[Heading]

Top-level headings.

description RichText | None

The #+DESCRIPTION: value, or None.

dirty bool

Whether this document has been mutated after creation.

done_states list[str]

Discovered completed TODO states from the #+TODO: definition.

errors list[ParseError]

Parse errors captured during org_parser.document.Document.from_tree construction.

filename str

The filename of the document file.

is_leaf bool

Whether this document has no top-level headings.

is_root bool

Whether this node is the root of a parsed document tree.

keywords list[Keyword]

All special keywords as an ordered list.

logbook Logbook

Merged zeroth-section LOGBOOK drawer.

properties Properties

Merged zeroth-section PROPERTIES drawer.

tags list[str]

Tags from all #+FILETAGS: keywords, as individual strings.

title RichText | None

The #+TITLE: value, or None.

todo RichText | None

The #+TODO: value, or None.

todo_states list[str]

Discovered non-completed TODO states from the #+TODO: definition.

all_headings property

all_headings: list[Heading]

All headings in file-definition order across the full document tree.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... ** Heading 2
... ''')
>>> len(document.all_headings)
2
>>> len(document[:])
2

all_states property

all_states: list[str]

All discovered TODO keyword states from the #+TODO: definition.

Example:

>>> from org_parser import loads
>>> document = loads("#+TODO: TODO WAITING | DONE CANCELLED")
>>> document.all_states
['TODO', 'WAITING', 'DONE', 'CANCELLED']

author property writable

author: RichText | None

The #+AUTHOR: value, or None.

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> document.children[0].todo
'TODO'
>>> document.author = RichText("Updated")
>>> document.author.raw
'Updated'

body property writable

body: list[Element]

Zeroth-section body elements (excludes keywords and headings).

Example:

>>> from org_parser import loads
>>> document = loads("")
>>> document.body = [Paragraph.from_source("Add some body text")]
>>> print(str(document))
Add some body text

body_text property

body_text: str

Stringified text for all zeroth-section body elements.

category property writable

category: RichText | None

The effective category for this document.

Returns the #+CATEGORY: keyword value when present. Otherwise falls back to the stem of org_parser.document.Document.filename (the basename without its file extension), which matches Org Mode's own default-category behaviour. Returns None when no filename is known (empty string).

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> document.children[0].category is None
True
>>> document.category = RichText("Updated")
>>> document.children[0].category
RichText("Updated")

children property writable

children: list[Heading]

Top-level headings.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... ** Heading 2
... *** Heading 3
... ''')
>>> document.children[0].children = document.children[0].children[0].children
>>> print(document.render())
* Heading 1
*** Heading 3

description property writable

description: RichText | None

The #+DESCRIPTION: value, or None.

dirty property

dirty: bool

Whether this document has been mutated after creation.

done_states property

done_states: list[str]

Discovered completed TODO states from the #+TODO: definition.

Example:

>>> from org_parser import loads
>>> document = loads("#+TODO: TODO WAITING | DONE CANCELLED")
>>> document.todo_states
['DONE', 'CANCELLED']

errors property

errors: list[ParseError]

Parse errors captured during org_parser.document.Document.from_tree construction.

Returns an empty list for programmatically constructed documents. The list is read-only: do not mutate it directly.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading
... SCHEDULED: yesterday
... ''')
>>> document.errors
[ParseError(
    start_point=Point(row=2, column=0),
    end_point=Point(row=2, column=20),
    text='SCHEDULED: yesterday',
    message='Encountered parser ERROR node'
)]

filename property writable

filename: str

The filename of the document file.

Example:

>>> from org_parser import dump, loads
>>> document = loads("* TODO Heading 1")
>>> document.filename = "file.org"
>>> dump(document)

is_leaf property

is_leaf: bool

Whether this document has no top-level headings.

Example:

>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> document.is_leaf
False
>>> document = loads("No headings")
>>> document.is_leaf
True

is_root property

is_root: bool

Whether this node is the root of a parsed document tree.

Example:

>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> document.is_root
True

keywords property writable

keywords: list[Keyword]

All special keywords as an ordered list.

Example:

>>> from org_parser import loads
>>> document = loads("#+OTHER: foo")
>>> document.keywords
[Keyword(key='OTHER', value=RichText('foo'))]
>>> document.keywords = []
>>> len(document.keywords)
0

logbook property writable

logbook: Logbook

Merged zeroth-section LOGBOOK drawer.

Example:

>>> from org_parser.time import Clock
>>> document = loads("#+TITLE: Logbook")
>>> document.logbook.clock_entries = [Clock.from_source("CLOCK: [2025-10-10]")]
>>> print(str(document))
#+TITLE: Logbook
:LOGBOOK:
CLOCK: [2025-10-10]
:END:

properties property writable

properties: Properties

Merged zeroth-section PROPERTIES drawer.

Example:

>>> from org_parser import loads
>>> document = loads("#+TITLE: Properties")
>>> document.properties["key"] = RichText("Value")
>>> print(str(document))
#+TITLE: Properties
:PROPERTIES:
:key: Value
:END:

tags property writable

tags: list[str]

Tags from all #+FILETAGS: keywords, as individual strings.

Returns an empty list when no #+FILETAGS: keyword is present. Multiple #+FILETAGS: lines are aggregated in keyword-list order. The returned list is a fresh copy; mutate via the setter.

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> document.children[0].tags
[]
>>> document.tags = ["tag1", "tag2"]
>>> document.children[0].tags
['tag1', 'tag2']

title property writable

title: RichText | None

The #+TITLE: value, or None.

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> document.title = RichText("Updated")
>>> document
#+TITLE: Updated
* TODO Heading 1

todo property writable

todo: RichText | None

The #+TODO: value, or None.

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> document.todo = RichText("TODO WAITING | DONE")
>>> document.todo_states
['TODO', ''WAITING']

todo_states property

todo_states: list[str]

Discovered non-completed TODO states from the #+TODO: definition.

Example:

>>> from org_parser import loads
>>> document = loads("#+TODO: TODO WAITING | DONE CANCELLED")
>>> document.todo_states
['TODO', 'WAITING']

__getitem__

__getitem__(index: int | slice) -> Heading | list[Heading]

Return one (or slice) of headings from the document, including subheadings.

__iter__

__iter__() -> Iterator[Heading]

Iterate over all headings in file-definition order.

__len__

__len__() -> int

Return number of headings across the full document tree.

__repr__

__repr__() -> str

Return a tree-oriented representation for debugging.

__str__

__str__() -> str

Return a textual representation of the document zeroth section.

When the document is clean and still backed by a parse tree, this returns the exact source slice for the zeroth section to preserve original whitespace and formatting. Once the document is dirty, this falls back to a reconstructed representation from semantic fields.

from_source classmethod

from_source(source: str, *, filename: str = '') -> Document

Build a org_parser.document.Document from Org source text.

Parameters:

Name Type Description Default
source str

Org source text to parse.

required
filename str

Optional filename assigned to the parsed document.

''

Returns:

Type Description
Document

A fully populated parse-backed org_parser.document.Document.

Raises:

Type Description
ValueError

If the source contains parse errors.

Example:

>>> from org_parser import Document
>>> document = Document.from_source("* TODO Heading 1")
>>> document.children[0].todo
'TODO'

from_tree classmethod

from_tree(
    tree: Tree, filename: str, source: bytes
) -> Document

Build a org_parser.document.Document from a tree-sitter parse tree.

Parameters:

Name Type Description Default
tree Tree

The tree_sitter.Tree returned by the parser.

required
filename str

The filename of the source document.

required
source bytes

The raw source bytes that were parsed.

required

Returns:

Type Description
Document

A fully populated org_parser.document.Document with headings built

Document

recursively.

Example:

>>> from org_parser import Document
>>> from org_parser._lang import PARSER
>>> source = "* TODO Heading 1".encode()
>>> tree = PARSER.parse(source)
>>> document = Document.from_tree(tree, "notes.org", source)
>>> document.children[0].todo
'TODO'

heading_by_id

heading_by_id(heading_id: str) -> Heading | None

Return the heading with Org ID equal to heading_id.

When multiple headings share the same ID, the last heading in document order is returned.

Lookup data is backed by the document heading caches and is refreshed by org_parser.document.Document.sync_heading_id_index and org_parser.document.Document.sync_heading_title_index.

heading_by_title

heading_by_title(heading_title: str) -> Heading | None

Return the heading with title text equal to heading_title.

Title lookup keys are normalized using .strip() and compare only the heading title text. When multiple headings share the same normalized title, the last heading in document order is returned.

Lookup data is backed by the document heading caches and is refreshed by org_parser.document.Document.sync_heading_id_index and org_parser.document.Document.sync_heading_title_index.

mark_dirty

mark_dirty() -> None

Mark this document as dirty.

Example:

>>> from org_parser import loads
>>> document = loads('''
... Some text.
... #+TITLE: Title
... More text
... ''')
>>> document.mark_dirty()
>>> document.dirty
True
>>> print(str(document))
#+TITLE: Title

Some text.
More text

reformat

reformat() -> None

Reformat the entire document.

Example:

>>> 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>

render

render() -> str

Return the complete Org Mode text for a document including headings.

For clean (unmodified) parse-backed documents the original source bytes are returned verbatim, preserving all whitespace and formatting. For dirty documents, or documents built without a backing source, the zeroth section and every heading subtree are reconstructed from their semantic fields via :func:str.

Returns:

Type Description
str

Full Org Mode text including all headings.

report_error

report_error(
    node: Node, message: str | None = None
) -> None

Record a parse error for node.

Extracts the verbatim source text for the node and appends a org_parser.document.ParseError to the internal errors list.

Parameters:

Name Type Description Default
node Node

The tree-sitter ERROR or missing node to record.

required
message str | None

Optional semantic classification. When omitted, a default category is inferred from node.

None

Raises:

Type Description
ValueError

If this document has no source bytes.

source_for

source_for(node: Node) -> bytes

Return source bytes for one node span.

Parameters:

Name Type Description Default
node Node

Tree-sitter node to slice against.

required

Returns:

Type Description
bytes

The source bytes covered by node.start_byte:node.end_byte.

Raises:

Type Description
ValueError

If this document has no source bytes.

sync_heading_id_index

sync_heading_id_index() -> None

Rebuild heading lookup caches for IDs and titles.

sync_heading_title_index

sync_heading_title_index() -> None

Rebuild heading lookup caches for titles and IDs.

Heading

Heading(
    *,
    level: int,
    document: Document,
    parent: Heading | Document,
    todo: str | None = None,
    is_comment: bool = False,
    priority: str | None = None,
    title: RichText | str | None = None,
    counter: CompletionCounter | None = None,
    heading_tags: Sequence[str] = (),
    scheduled: Timestamp | None = None,
    closed: Timestamp | None = None,
    deadline: Timestamp | None = None,
    properties: Properties | None = None,
    logbook: Logbook | None = None,
    repeats: Sequence[Repeat] = (),
    clock_entries: Sequence[Clock] = (),
    body: Sequence[Element] = (),
    children: Sequence[Heading] = (),
)

An Org Mode heading (or sub-heading).

A heading exposes the parsed components of an Org headline — stars, TODO state, priority cookie, title text, tags, completion counter — as well as the body elements and any nested sub-headings.

Parameters:

Name Type Description Default
level int

The heading level (count of leading * characters).

required
document Document

The root org_parser.document.Document that contains this heading.

required
parent Heading | Document required
todo str | None

The TODO keyword (e.g. "TODO", "DONE"), or None.

None
is_comment bool

Whether this heading uses the COMMENT marker.

False
priority str | None

The priority letter or number (e.g. "A", "1"), or None.

None
title RichText | str | None

The heading title as org_parser.text.RichText, or None.

None
counter CompletionCounter | None

Completion counter object (e.g. [1/3]), or None.

None
heading_tags Sequence[str]

A list of tag strings found on this heading line in source order.

()
repeats Sequence[Repeat]

Repeated task entries extracted from LOGBOOK and heading body.

()
clock_entries Sequence[Clock]

Clock entries extracted from LOGBOOK and heading body.

()
body Sequence[Element]

Body elements of the heading (excludes sub-headings).

()
children Sequence[Heading]

Direct sub-headings of this heading.

()

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.title_text
'Heading 1'
>>> heading.todo
'TODO'

Methods:

Name Description
__getitem__

Return one direct child heading (or heading slice).

__iter__

Iterate over direct child headings.

__len__

Return number of direct child headings.

__repr__

Return a tree-oriented representation for debugging.

__str__

Return a textual representation of this heading and its body.

add_repeat

Append one repeat and synchronize it into the logbook.

from_node

Build a org_parser.document.Heading (and its sub-tree) from a tree-sitter node.

from_source

Build one heading from Org source text.

mark_dirty

Mark this heading dirty and bubble to its parent chain.

reformat

Reformat heading descendants and this heading.

render

Return the complete Org Mode text for a heading including subheadings.

shift_subtree_levels

Shift this heading subtree levels by delta and mark nodes dirty.

Attributes:

Name Type Description
body list[Element]

Body elements (excludes sub-headings).

body_text str

Stringified text for all body elements of this heading.

category RichText | None

The effective category for this heading.

children list[Heading]

Direct sub-headings.

clock_entries list[Clock]

Clock entries from this heading's logbook and body.

closed Timestamp | None

The CLOSED planning timestamp, or None.

column int | None

Zero-based source column of this heading's parse node, or None.

counter CompletionCounter | None

The completion counter object, or None if absent.

deadline Timestamp | None

The DEADLINE planning timestamp, or None.

dependencies list[Heading]

Task dependencies that must be completed before this heading.

dirty bool

Whether this heading has been mutated after creation.

document Document

The org_parser.document.Document that ultimately contains this heading.

earliest_timestamp Timestamp | None

Earliest timestamp across planning values and logbook-derived timestamps.

has_timestamp bool

Whether this heading has any planning, repeat, or clock timestamp.

heading_category RichText | None

The CATEGORY value from this heading's own PROPERTIES drawer.

heading_tags list[str]

Tag strings found on this heading line, in source order.

heading_text str

Stringified heading line including stars and line-level fields.

id str | None

The ID value from this heading's PROPERTIES drawer.

is_blocked bool

Whether this heading is blocked by incomplete dependencies.

is_comment bool

Whether this heading is marked with the COMMENT keyword.

is_completed bool

Whether this heading's current TODO state is a done state.

is_leaf bool

Whether this heading has no direct sub-headings.

is_root bool

Whether this heading is the root node of a document tree.

latest_timestamp Timestamp | None

Latest timestamp across planning values and logbook-derived timestamps.

level int

The heading level (count of leading * characters).

line int | None

Zero-based source line of this heading's parse node, or None.

logbook Logbook

Merged heading LOGBOOK drawer.

parent Heading | Document
priority str | None

The priority value (e.g. "A", "1"), or None.

properties Properties

Merged heading PROPERTIES drawer.

repeats list[Repeat]

Repeated task entries from this heading's logbook and body.

scheduled Timestamp | None

The SCHEDULED planning timestamp, or None.

siblings list[Heading]

Other headings at the same level under the same parent.

tags list[str]

All effective tags inherited from FILETAGS and ancestor headings.

timestamps list[Timestamp]

All timestamps attached to this heading's planning and logbook data.

title RichText | None

The heading title as org_parser.text.RichText, or None.

title_text str

Stringified text for this heading's title object.

todo str | None

The TODO keyword, or None if absent.

body property writable

body: list[Element]

Body elements (excludes sub-headings).

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.body = [Paragraph.from_source("Add some body text")]
>>> print(str(heading))
* TODO Heading 1
Add some body text

body_text property

body_text: str

Stringified text for all body elements of this heading.

category property

category: RichText | None

The effective category for this heading.

Returns org_parser.document.Heading.heading_category when it is set on this heading's own PROPERTIES drawer. Otherwise the value is inherited from the parent org_parser.document.Heading or org_parser.document.Document, walking up the tree until a category is found or the document level is reached.

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> heading = loads('''
... #+CATEGORY: Category
... * TODO Heading 1
... ''').children[0]
>>> heading.heading_category
None
>>> heading.category
'Category'

children property writable

children: list[Heading]

Direct sub-headings.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... ** Heading 2
... ''')
>>> document[0].children[0].title_text
'Heading 2'

clock_entries property writable

clock_entries: list[Clock]

Clock entries from this heading's logbook and body.

Example:

>>> from org_parser import loads
>>> from org_parser.time import Clock
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.clock_entries = [Clock.from_source("CLOCK: [2025-10-10]")]
>>> print(str(heading))
* TODO Heading 1
:LOGBOOK:
CLOCK: [2025-10-10]
:END:

closed property writable

closed: Timestamp | None

The CLOSED planning timestamp, or None.

Example:

>>> from org_parser.time import Timestamp
>>> from org_parser import loads
>>> heading = loads("* Heading 1").children[0]
>>> heading.closed = Timestamp.from_source("<2026-03-29 Sun>")
>>> heading.closed.start.year
2026

column property

column: int | None

Zero-based source column of this heading's parse node, or None.

counter property writable

counter: CompletionCounter | None

The completion counter object, or None if absent.

Example:

>>> from org_parser import loads
>>> from org_parser.text import CompletionCounter
>>> heading = loads("* Heading 1").children[0]
>>> print(str(heading))
* Heading 1
>>> heading.counter = CompletionCounter("1/2")
>>> print(str(heading))
* [1/2] Heading 1

deadline property writable

deadline: Timestamp | None

The DEADLINE planning timestamp, or None.

Example:

>>> from org_parser.time import Timestamp
>>> from org_parser import loads
>>> heading = loads("* Heading 1").children[0]
>>> heading.deadline = Timestamp.from_source("<2026-03-29 Sun 18:00 -5d>")
>>> heading.deadline.start.year
2026

dependencies property

dependencies: list[Heading]

Task dependencies that must be completed before this heading.

Dependencies are computed from three sources in this order:

  • direct child headings,
  • preceding siblings when ORDERED has a non-empty value in parent.properties,
  • headings referenced by ID via this heading's BLOCKER property (space-separated IDs).

Unknown BLOCKER IDs are ignored. Duplicate dependencies are removed while preserving first occurrence order.

dirty property

dirty: bool

Whether this heading has been mutated after creation.

document property writable

document: Document

The org_parser.document.Document that ultimately contains this heading.

Example:

>>> from org_parser import loads
>>> document = loads("* TODO Heading 1")
>>> heading = documentt.children[0]
>>> heading.title_text
'Heading 1'
>>> heading.document is document
True

earliest_timestamp property

earliest_timestamp: Timestamp | None

Earliest timestamp across planning values and logbook-derived timestamps.

Example:

>>> document = loads('''
... * Heading 1
... CLOSED: <2026-03-29>
... DEADLINE: <2026-03-10>
... ''')
>>> document[0].earliest_timestamp is document[0].closed
False

has_timestamp property

has_timestamp: bool

Whether this heading has any planning, repeat, or clock timestamp.

Example:

>>> document = loads('''
... * Heading 1
... ** Heading 2
... SCHEDULED: <2026-03-29>
... ''')
>>> document[0].has_timestamp
False
>>> document[1].has_timestamp
True

heading_category property writable

heading_category: RichText | None

The CATEGORY value from this heading's own PROPERTIES drawer.

Returns the org_parser.text.RichText value of the CATEGORY node property when the heading has a PROPERTIES drawer containing that key, or None otherwise. Use org_parser.document.Heading.category to get the fully-inherited effective category.

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> heading = loads('''
... #+CATEGORY: Category
... * TODO Heading 1
... :PROPERTIES:
... :CATEGORY: Heading
... :END:
... ''').children[0]
>>> heading.heading_category
'Heading'

heading_tags property writable

heading_tags: list[str]

Tag strings found on this heading line, in source order.

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.heading_tags = ["work", "docs"]
>>> heading.heading_tags
['work', 'docs']

heading_text property

heading_text: str

Stringified heading line including stars and line-level fields.

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1  :tag:").children[0]
>>> heading.title_text
'Heading 1'
>>> heading.heading_text
'* TODO Heading 1  :tag:'

id property writable

id: str | None

The ID value from this heading's PROPERTIES drawer.

Returns None when no ID entry exists or when it renders to an empty string.

is_blocked property

is_blocked: bool

Whether this heading is blocked by incomplete dependencies.

If this heading has NOBLOCKING set to a non-empty value in its own PROPERTIES drawer, this always returns False.

Returns True when at least one dependency from org_parser.document.Heading.dependencies is not completed in the org_parser.document.Heading.is_completed sense.

is_comment property writable

is_comment: bool

Whether this heading is marked with the COMMENT keyword.

Example:

>>> from org_parser import loads
>>> heading = loads("* Heading 1").children[0]
>>> heading.heading_text
'* Heading 1'
>>> heading.is_comment = True
>>> heading.heading_text
'* COMMENT Heading 1'

is_completed property

is_completed: bool

Whether this heading's current TODO state is a done state.

Example:

>>> from org_parser import loads
>>> document = loads('''
... #+TODO: TODO WAITING | DONE
... * TODO Heading 1
... ** WAITING Heading 2
... ** DONE Heading 3
... ''')
>>> document[0].is_completed
False
>>> document[1].is_completed
False
>>> document[2].is_completed
True

is_leaf property

is_leaf: bool

Whether this heading has no direct sub-headings.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... ** Heading 2
... ''')
>>> document[0].is_leaf
False
>>> document[1].is_leaf
True

is_root property

is_root: bool

Whether this heading is the root node of a document tree.

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.is_root
False

latest_timestamp property

latest_timestamp: Timestamp | None

Latest timestamp across planning values and logbook-derived timestamps.

Example:

>>> document = loads('''
... * Heading 1
... SCHEDULED: <2026-03-29>
... ''')
>>> document[0].latest_timestamp is document[0].scheduled
True

level property writable

level: int

The heading level (count of leading * characters).

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.heading_text
'* TODO Heading 1'
>>> heading.level = 3
>>> heading.heading_text
'*** TODO Heading 1'

line property

line: int | None

Zero-based source line of this heading's parse node, or None.

logbook property writable

logbook: Logbook

Merged heading LOGBOOK drawer.

Example:

>>> from org_parser import loads
>>> from org_parser.element import Repeat
>>> from org_parser.time import Clock, Timestamp
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.logbook.clock_entries = [Clock.from_source("CLOCK: [2025-10-10]")]
>>> ts = Timestamp.from_source("<2025-10-10>")
>>> heading.logbook.repeats = [Repeat(after="DONE", before="TODO", timestamp=ts)]
>>> print(str(heading))
* TODO Heading 1
:LOGBOOK:
CLOCK: [2025-10-10]
- State "DONE"       from "TODO"       <2025-10-10>
:END:

parent property writable

parent: Heading | Document

The parent org_parser.document.Heading or org_parser.document.Document.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... ** Heading 2
... ''')
>>> document[1].parent.title_text
'Heading 1'

priority property writable

priority: str | None

The priority value (e.g. "A", "1"), or None.

Example:

>>> from org_parser import loads
>>> heading = loads("* Heading 1").children[0]
>>> heading.heading_text
'* Heading 1'
>>> heading.priority = "A"
>>> heading.heading_text
'* [#A] Heading 1'

properties property writable

properties: Properties

Merged heading PROPERTIES drawer.

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.properties["key"] = RichText("Value")
>>> print(str(heading))
* TODO Heading 1
:PROPERTIES:
:key: Value
:END:

repeats property writable

repeats: list[Repeat]

Repeated task entries from this heading's logbook and body.

Example:

>>> from org_parser import loads
>>> from org_parser.element import Repeat
>>> from org_parser.time import Timestamp
>>> heading = loads("* TODO Heading 1").children[0]
>>> ts = Timestamp.from_source("<2025-10-10>")
>>> heading.repeats = [Repeat(after="DONE", before="TODO", timestamp=ts)]
>>> print(str(heading))
* TODO Heading 1
:LOGBOOK:
- State "DONE"       from "TODO"       <2025-10-10>
:END:

scheduled property writable

scheduled: Timestamp | None

The SCHEDULED planning timestamp, or None.

Example:

>>> from org_parser import loads
>>> heading = loads('''
... * Heading 1
... SCHEDULED: <2026-03-29>
...''').children[0]
>>> heading.scheduled.start.year
2026

siblings property

siblings: list[Heading]

Other headings at the same level under the same parent.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... ** Heading 2
... ** Heading 3
... ''')
>>> document[0].siblings
[]
>>> document[1].siblings[0].title_text
'Heading 3'

tags property

tags: list[str]

All effective tags inherited from FILETAGS and ancestor headings.

Returns document FILETAGS, then each ancestor's heading_tags (outermost first), then this heading's own heading_tags. Duplicates are removed; the first occurrence is kept.

Example:

>>> from org_parser import loads
>>> heading = loads('''
... #+FILETAGS: :tag1:
... * TODO Heading 1    :tag2:
... ''').children[0]
>>> heading.heading_tags
['tag2']
>>> heading.tags
['tag1', 'tag2']

timestamps property

timestamps: list[Timestamp]

All timestamps attached to this heading's planning and logbook data.

Example:

>>> document = loads('''
... * Heading 1
... ** Heading 2
... SCHEDULED: <2026-03-29>
... ''')
>>> document[0].timestamps
[]
>>> len(document[1].timestamps)
1

title property writable

title: RichText | None

The heading title as org_parser.text.RichText, or None.

Example:

>>> from org_parser.text import RichText
>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.title_text
'Heading 1'
>>> heading.title = RichText("Updated")
>>> heading.title_text
'Updated'
>>> heading.todo
'TODO'

title_text property

title_text: str

Stringified text for this heading's title object.

todo property writable

todo: str | None

The TODO keyword, or None if absent.

Example:

>>> from org_parser import loads
>>> heading = loads("* TODO Heading 1").children[0]
>>> heading.title_text
'Heading 1'
>>> heading.todo = "DONE"
>>> heading.todo
'DONE'

__getitem__

__getitem__(index: int | slice) -> Heading | list[Heading]

Return one direct child heading (or heading slice).

__iter__

__iter__() -> Iterator[Heading]

Iterate over direct child headings.

__len__

__len__() -> int

Return number of direct child headings.

__repr__

__repr__() -> str

Return a tree-oriented representation for debugging.

__str__

__str__() -> str

Return a textual representation of this heading and its body.

When the heading is clean and still backed by a parse tree, this returns the exact source slice that spans the heading line and body section only, excluding any sub-headings. Once dirty, this is rebuilt from semantic fields.

add_repeat

add_repeat(repeat: Repeat) -> None

Append one repeat and synchronize it into the logbook.

Example:

>>> from org_parser import loads
>>> from org_parser.element import Repeat
>>> from org_parser.time import Timestamp
>>> heading = loads("* TODO Heading 1").children[0]
>>> ts = Timestamp.from_source("<2025-10-10>")
>>> heading.add_repeat(Repeat(after="DONE", before="TODO", timestamp=ts))
>>> print(str(heading))
* TODO Heading 1
:LOGBOOK:
- State "DONE"       from "TODO"       <2025-10-10>
:END:

from_node classmethod

from_node(
    node: Node,
    *,
    document: Document,
    parent: Heading | Document,
) -> Heading

Build a org_parser.document.Heading (and its sub-tree) from a tree-sitter node.

Parameters:

Name Type Description Default
node Node

A tree-sitter node of type heading.

required
document Document

The root document that contains this heading.

required
parent Heading | Document required

Returns:

Type Description
Heading

A fully populated org_parser.document.Heading with recursively built

Heading

children.

from_source classmethod

from_source(source: str) -> Heading

Build one heading from Org source text.

The source must parse to exactly one top-level heading and no other zeroth-section semantic content.

Parameters:

Name Type Description Default
source str

Org source text containing exactly one heading.

required

Returns:

Type Description
Heading

Raises:

Type Description
ValueError

If parsing fails or the structure is not one heading.

Example:

>>> from org_parser.document import Heading
>>> heading = Heading.from_source("* TODO Heading 1")
>>> heading.title_text
'Heading 1'

mark_dirty

mark_dirty() -> None

Mark this heading dirty and bubble to its parent chain.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... CLOSED: <2025-10-10>
... SCHEDULED: <2025-10-10>
... ''')
>>> document[0].mark_dirty()
>>> print(document.render())
* Heading 1
SCHEDULED: <2025-10-10> CLOSED: <2025-10-10>

reformat

reformat() -> None

Reformat heading descendants and this heading.

Example:

>>> from org_parser import loads
>>> document = loads('''
... * Heading 1
... ** Heading 2
... CLOSED: <2025-10-10>
... SCHEDULED: <2025-10-10>
... ''')
>>> document[0].reformat()
>>> print(document.render())
* Heading 1
** Heading 2
SCHEDULED: <2025-10-10> CLOSED: <2025-10-10>

render

render() -> str

Return the complete Org Mode text for a heading including subheadings.

For clean (unmodified) parse-backed headings the original source bytes are returned verbatim, preserving all whitespace and formatting. For dirty headings, or heaidngs built without a backing source, the representation is reconstructed from their semantic fields via :func:str.

Returns:

Type Description
str

Full Org Mode text including all subheadings.

shift_subtree_levels

shift_subtree_levels(delta: int) -> None

Shift this heading subtree levels by delta and mark nodes dirty.

ParseError dataclass

ParseError(
    start_point: tuple[int, int],
    end_point: tuple[int, int],
    text: str,
    _node: Node,
    message: str = "Unexpected parse node",
)

A single parse error captured during semantic extraction.

Attributes:

Name Type Description
start_point tuple[int, int]

(row, column) of the error node's start position.

end_point tuple[int, int]

(row, column) of the error node's end position.

text str

The verbatim source text span covered by the error node.

message str

Semantic classification describing the parse error category.

_node Node

The raw tree-sitter node (private; not part of public API).