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 |
None
|
author
|
RichText | str | None
|
The value of the |
None
|
category
|
RichText | str | None
|
The value of the |
None
|
description
|
RichText | str | None
|
The value of the |
None
|
todo
|
RichText | str | None
|
The value of the |
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 |
None
|
logbook
|
Logbook | None
|
Merged zeroth-section |
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 |
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 |
author |
RichText | None
|
The |
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 |
dirty |
bool
|
Whether this document has been mutated after creation. |
done_states |
list[str]
|
Discovered completed TODO states from the |
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 |
properties |
Properties
|
Merged zeroth-section |
tags |
list[str]
|
Tags from all |
title |
RichText | None
|
The |
todo |
RichText | None
|
The |
todo_states |
list[str]
|
Discovered non-completed TODO states from the |
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
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
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__
¶
Return one (or slice) of headings from the document, including subheadings.
__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 |
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 |
required |
message
|
str | None
|
Optional semantic classification. When omitted, a
default category is inferred from |
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 |
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 |
required |
document
|
Document
|
The root org_parser.document.Document that contains this heading. |
required |
parent
|
Heading | Document
|
The parent org_parser.document.Heading or org_parser.document.Document. |
required |
todo
|
str | None
|
The TODO keyword (e.g. |
None
|
is_comment
|
bool
|
Whether this heading uses the |
False
|
priority
|
str | None
|
The priority letter or number (e.g. |
None
|
title
|
RichText | str | None
|
The heading title as org_parser.text.RichText, or None. |
None
|
counter
|
CompletionCounter | None
|
Completion counter object (e.g. |
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 |
()
|
clock_entries
|
Sequence[Clock]
|
Clock entries extracted from |
()
|
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 |
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 |
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 |
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 |
is_blocked |
bool
|
Whether this heading is blocked by incomplete dependencies. |
is_comment |
bool
|
Whether this heading is marked with the |
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 |
line |
int | None
|
Zero-based source line of this heading's parse node, or None. |
logbook |
Logbook
|
Merged heading |
parent |
Heading | Document
|
The parent org_parser.document.Heading or org_parser.document.Document. |
priority |
str | None
|
The priority value (e.g. |
properties |
Properties
|
Merged heading |
repeats |
list[Repeat]
|
Repeated task entries from this heading's logbook and body. |
scheduled |
Timestamp | None
|
The |
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
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
ORDEREDhas a non-empty value inparent.properties, - headings referenced by
IDvia this heading'sBLOCKERproperty (space-separated IDs).
Unknown BLOCKER IDs are ignored. Duplicate dependencies are removed
while preserving first occurrence order.
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'
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
¶
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'
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__
¶
Return one direct child heading (or heading slice).
__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
¶
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 |
required |
document
|
Document
|
The root document that contains this heading. |
required |
parent
|
Heading | Document
|
The parent org_parser.document.Heading or org_parser.document.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
|
The parsed org_parser.document.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]
|
|
end_point |
tuple[int, int]
|
|
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). |