# containers

URL: /patitas/api/parsing/containers/
Section: parsing
Description: Container Stack Architecture for CommonMark Parsing.

This module implements a Container Stack for managing nested block-level
containers during Markdown parsing. The stack provides:

1. Explicit indent ownership - Each container "claims" an indent range
2. Automatic state propagation - Looseness propagates upward on pop
3. Centralized indent queries - find_owner() replaces scattered logic

Usage:
    stack = ContainerStack()  # Initializes with DOCUMENT frame

    # Push a list container
    stack.push(ContainerFrame(
        container_type=ContainerType.LIST,
        start_indent=0,
        content_indent=2,
    ))

    # Find owner for a given indent
    owner, depth = stack.find_owner(4)

    # Pop and propagate state
    frame = stack.pop()

---

> For a complete page index, fetch /patitas/llms.txt.

Open LLM text
(/patitas/api/parsing/containers/index.txt)

Share with AI

Ask Claude
(https://claude.ai/new?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Fcontainers%2Findex.txt)

Ask ChatGPT
(https://chatgpt.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Fcontainers%2Findex.txt)

Ask Gemini
(https://gemini.google.com/app?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Fcontainers%2Findex.txt)

Ask Copilot
(https://copilot.microsoft.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Fcontainers%2Findex.txt)

Module

#
`parsing.containers`

Container Stack Architecture for CommonMark Parsing.

This module implements a Container Stack for managing nested block-level
containers during Markdown parsing. The stack provides:

- Explicit indent ownership - Each container "claims" an indent range

- Automatic state propagation - Looseness propagates upward on pop

- Centralized indent queries - find_owner() replaces scattered logic

Usage:

```
stack = ContainerStack()  # Initializes with DOCUMENT frame

# Push a list container
stack.push(ContainerFrame(
    container_type=ContainerType.LIST,
    start_indent=0,
    content_indent=2,
))

# Find owner for a given indent
owner, depth = stack.find_owner(4)

# Pop and propagate state
frame = stack.pop()
```

3Classes

## Classes

`ContainerType`

0

▼

Types of block-level containers in CommonMark.

Bases:

`Enum`

Types of block-level containers in CommonMark.

`ContainerFrame`

14

▼

A frame on the container stack representing a parsing context.

Each container "claims" an indent r…

A frame on the container stack representing a parsing context.

Each container "claims" an indent range. Tokens are routed to
the deepest container that claims their indent.

#### Attributes

Name
Type
Description

`container_type`

`ContainerType (/patitas/api/parsing/containers/#ContainerType)`

The type of container (LIST, LIST_ITEM, etc.)

`start_indent`

`int`

Where this container started (marker position)

`content_indent`

`int`

Minimum indent for content continuation

`marker_width`

`int`

Width of the marker (e.g., 2 for "- ")

`max_sibling_indent`

`int`

For lists, marker siblings can appear at start_indent to start_indent+3

`is_loose`

`bool`

Whether the container has blank lines between content

`saw_blank_line`

`bool`

Whether a blank line was seen in this container

`ordered`

`bool`

For lists, whether the list is ordered

`bullet_char`

`str`

For unordered lists, the bullet character

`start_number`

`int`

For ordered lists, the starting number

#### Methods

`owns_content`

1

`bool`

▼

Does this container own content at this indent level?

`def owns_content(self, indent: int) -> bool`

##### Parameters

Name
Type
Description

`indent`
`—`

The indent level to check

##### Returns

`bool`

True if content at this indent belongs to this container

`owns_marker`

1

`bool`

▼

Could a list marker at this indent be a sibling in this container?

Only valid …

`def owns_marker(self, indent: int) -> bool`

Could a list marker at this indent be a sibling in this container?

Only valid for LIST containers.

##### Parameters

Name
Type
Description

`indent`
`—`

The indent level of the marker

##### Returns

`bool`

True if a marker at this indent is a sibling item

`is_nested_marker`

1

`bool`

▼

Would a marker at this indent start a nested list?

`def is_nested_marker(self, indent: int) -> bool`

##### Parameters

Name
Type
Description

`indent`
`—`

The indent level of the marker

##### Returns

`bool`

True if a marker at this indent starts a nested list

Internal Methods
1

▼

`__post_init__`

0

▼

Set default max_sibling_indent if not provided.

`def __post_init__(self) -> None`

`ContainerStack`

13

▼

Manages the stack of active containers during parsing.

Invariant: stack[0] is always DOCUMENT, sta…

Manages the stack of active containers during parsing.

Invariant: stack[0] is always DOCUMENT, stack[-1] is innermost container.

Usage:

```
stack = ContainerStack()  # Initializes with DOCUMENT frame
stack.push(frame)         # Push new container
stack.pop()               # Pop and propagate state
stack.find_owner(indent)  # Find container owning this indent
```

#### Attributes

Name
Type
Description

`_stack`

`list[ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame)]`

—

#### Methods

`push`

1

▼

Push a new container onto the stack.

`def push(self, frame: ContainerFrame) -> None`

##### Parameters

Name
Type
Description

`frame`
`—`

The container frame to push

`pop`

0

`ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame)`

▼

Pop the innermost container.

Propagates state (looseness, blank lines) to pare…

`def pop(self) -> ContainerFrame`

Pop the innermost container.

Propagates state (looseness, blank lines) to parent if applicable.

- When popping a LIST_ITEM with blank lines, propagates to parent LIST

Note: For blank lines BETWEEN items (sibling separation), use
mark_parent_list_loose() directly instead of relying on propagation.
This propagation handles blank lines WITHIN item content.

Note: Nested LIST does NOT propagate to parent LIST_ITEM, as nested
list looseness should not affect the outer list's tightness.

##### Returns

`ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame)`

The popped container frame

`current`

0

`ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame)`

▼

Get the innermost container.

`def current(self) -> ContainerFrame`

##### Returns

`ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame)`

The current (innermost) container frame

`depth`

0

`int`

▼

Current nesting depth (document = 0).

`def depth(self) -> int`

##### Returns

`int`

The number of containers above DOCUMENT

`find_owner`

1

`tuple[ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame), in…`

▼

Find which container owns content at this indent.

Walks from innermost to oute…

`def find_owner(self, indent: int) -> tuple[ContainerFrame, int]`

Find which container owns content at this indent.

Walks from innermost to outermost, returns first container
that claims the indent.

##### Parameters

Name
Type
Description

`indent`
`—`

The indent level to check

##### Returns

`tuple[ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame), int]`

(owner_frame, stack_index) - the frame and its position in stack

`find_sibling_list`

1

`tuple[ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame), in…`

▼

Find a list container where a marker at this indent would be a sibling.

`def find_sibling_list(self, marker_indent: int) -> tuple[ContainerFrame, int] | None`

##### Parameters

Name
Type
Description

`marker_indent`
`—`

The indent level of the marker

##### Returns

`tuple[ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame), int] | None`

(frame, index) if found, None if marker starts new list at document level

`pop_until`

1

`list[ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame)]`

▼

Pop containers until stack has target_index + 1 elements.

`def pop_until(self, target_index: int) -> list[ContainerFrame]`

##### Parameters

Name
Type
Description

`target_index`
`—`

The index to stop at (inclusive)

##### Returns

`list[ContainerFrame (/patitas/api/parsing/containers/#ContainerFrame)]`

List of popped frames (innermost first)

`mark_loose`

0

▼

Mark current container as loose (saw blank line with content after).

`def mark_loose(self) -> None`

`mark_blank_line`

0

▼

Mark that a blank line was seen in current container.

`def mark_blank_line(self) -> None`

`mark_parent_list_loose`

0

▼

Mark the parent LIST container as loose.

Called when a blank line between sibl…

`def mark_parent_list_loose(self) -> None`

Mark the parent LIST container as loose.

Called when a blank line between sibling items is detected.
The current frame is LIST_ITEM, and the parent should be LIST.

`update_content_indent`

1

▼

Update the current container's content_indent to the actual value.

Called when…

`def update_content_indent(self, actual_content_indent: int) -> None`

Update the current container's content_indent to the actual value.

Called when the first content line is parsed and we know the actual
column position where content starts. This allows find_owner() to
use the correct indent for subsequent content.

##### Parameters

Name
Type
Description

`actual_content_indent`
`—`

The actual content indent from first line

Internal Methods
1

▼

`__post_init__`

0

▼

Initialize with DOCUMENT frame.

`def __post_init__(self) -> None`
