All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- If
Document(...)ordumps(...)rejects your data, anyTable,ArrayorAoTyou passed in is left untouched. - A
TableorAoTpassed toDocument(mapping)is written where the mapping puts it, instead of after the rest of the section's keys. Document(mapping)anddumps(mapping)now build faster than parsing the equivalent text.dumps(mapping)is about 1.7x faster: it renders the document's lines without building theTable/Array/AoTviews over them.dumps(None)now raisesTypeErrorrather than returning"".Document(mapping)now copies aTable,ArrayorAoTyou pass in rather than keeping your object. Assign it (doc[k] = table) if you want the document to share it.- Nested mappings inside a
Tablepassed toDocument(mapping)now become[section]blocks, like nested mappings anywhere else in the argument. - A list holding a
Table.inlinestays an inline array inDocument(mapping), rather than becoming[[array.of.tables]]. - Copying a
[section]or[[array of tables]]block into another document is about twice as fast.
- Dumping a
[section]-backedTableno longer drops its own comments and spacing.
promote_inlineandpromote_arraynow raiseTOMLError, as documented, when the target is not an inline table or not an array.- Mutations that break the first row of a multi-line inline array or table no longer leave trailing whitespace after the opening bracket.
- Parse errors for duplicate or conflicting keys and headers now point at the offending key or header rather than at the line after it.
- Comments added to an inline array or inline table are now indented to match the value they belong to.
- A comment sitting above an inline array's or inline table's closing bracket now stays above an item appended after it.
- Switching an inline array or inline table to multi-line form no longer reformats its items or rewrites their comments.
- Items added to a multi-line inline array or inline table now follow the indentation and line breaks the value already uses.
- A multi-line inline array or inline table now closes its bracket at the indent of the line the value starts on.
- A generated
[table]/[[array]]header is no longer glued to the line above it, and follows the document's own spacing convention. - A key added to a table now picks up the indentation and blank-line spacing of the entry before it in more cases.
- Setting
Document.epilogueon a document that does not end in a newline now starts the epilogue on its own line. - A value TOML cannot represent is now rejected before anything changes, so a
failed assignment or
install()leaves the document untouched. - Editing large documents is much faster: adding and removing keys, items and tables, and formatting a single inline value, no longer slow down as the rest of the document grows.
- Values removed from a document, and views of values it has since replaced, can now be edited and re-assigned without corrupting either document.
Document(data=...)andupdate()no longer consume the mapping they are given.install()no longer produces invalid TOML for dotted paths through an existing inline table.- Assigning a
datetimewith a sub-minute UTC offset, or atimecarrying any timezone, now raisesValueErrorinstead of producing unparseable TOML. - Bulk array mutations now reject invalid items without partially modifying documents.
- Added
leading_blockfor multiline array and inline-table elements.
ArrayandAoTindices and repeat counts no longer truncate floats.- Invalid
AoT.insert()indices no longer modify attached documents. - Sorting child tables no longer scans unrelated document prefixes and suffixes.
- Complex table replacements no longer produce invalid TOML or crash.
format()now collapses excess blank lines while preserving comment separation.
- Added
FormatOptionsfor configuring canonical formatting.
- The
commentsargument toformat(); useFormatOptions(normalize_comments=...).
promote_inlineandpromote_arraynow preserve entry key and value formattingset_multiline()now preserves closing-bracket indentation for nested arrays and inline tables.format()now preserves closing-bracket indentation when called on a nested array or inline table directly.- Re-attaching a deleted array-of-tables (or reinstalling one of its entries as a table) no longer crashes when an entry has a direct key added ahead of an existing nested sub-section.
- Deleting an array containing an inline-table item and reassigning the same array back into a document no longer corrupts the nested item, which used to crash the next time it was mutated.
- Cloning, moving, or deleting-and-reassigning a section whose nested sub-table was declared before the section's own header no longer silently drops that nested content.
- Cross-document cloning and reuse of deleted or nested tables and arrays-of-tables now preserve formatting, ordering, and ownership more reliably, including when source and destination subtrees overlap.
- Improved the performance and formatting consistency of section and array-of-tables mutations in large documents.
del,pop, andclearon a detachedTable.section()/Table.inline()now remove the key instead of raisingAssertionError.
dumpsanddumpnow accept aMapping.
- The
indentargument ofArray(...),Array.set_multiline, andTable.set_multilineis now anint(number of spaces) instead of a string, so callers can no longer pass arbitrary indent text. - Invalid-argument errors now use the standard built-in exceptions
instead of
TOMLError: an empty or malformed key path, and a comment string containing a newline or other control character, now raiseValueError;promote_inline/promote_arrayraiseTypeErrorwhen the target value is the wrong kind.TOMLErroris reserved for tomlrt-domain conditions.
- The deprecated
tomlrt.document()andtomlrt.parse()aliases. UseDocument()andloads()instead.
- An out-of-range index in
arr[i] = valuenow raisesIndexErrorwithout corrupting the array; previously an out-of-range negative index could silently overwrite the wrong element. - Editing a "comma-first" multi-line array or inline table (comma leading each row) now preserves that layout instead of breaking it.
- Key-path lookups now raise on an empty path (e.g.
"",[],"a..b") instead of silently returning the whole container.
format()no longer drops a comment placed before a multi-line array's closing bracket.- Fixed
Document.preambledropping blank lines: reading and re-assigning it no longer collapses blank-separated comment groups.preambleis now the opening comment paragraph (up to the first blank line); comments below that blank belong to the first key or section. Document.epiloguenow preserves blank lines between trailing comment groups: it is atuple[str | None, ...](withNoneper blank line), so reading and re-assigning it no longer collapses blank-separated groups.- Sorting a multi-line inline array or table after deleting one of its end-of-line comments no longer detaches another item's comment.
- A multi-line inline array's layout is now derived consistently from its rendered shape (like inline tables): emptying and refilling one no longer leaves a half-collapsed layout, and appends in a CRLF document no longer emit a stray LF.
Array.extend()now computes its layout style once instead of per item, making a bulk extend linear rather than quadratic in the array's length.- Multi-line detection for inline arrays and tables is now memoised, so
building one with repeated
append/update/t[k] = vis linear rather than quadratic in the number of items. - Adding keys to a section table is now linear rather than quadratic, so building a large table key-by-key is much faster.
- An empty array-of-tables now renders as an empty array (
key = []) instead of being omitted, so a dumped document matches its dict view.
Array.sort()no longer drops or collapses leading comments when another item in the array carries an end-of-line comment (#185).
- Inline tables now support the comment-manipulation API (
comments,leading_comments) and multi-line layout control (multiline,set_multiline), matching inline arrays. Setting a comment promotes a single-line inline table to multi-line (TOML 1.1).
- Comment editing on multi-line inline arrays and tables now lays out irregular layouts more cleanly: a displaced item is re-indented, deleting an end-of-line comment keeps an adjacent blank line, and a comment block above unindented items lines up at column zero.
- Trailing whitespace after a comma no longer masks the row-terminating newline, so editing or restructuring such a row no longer leaves a stray space-only blank line.
- Structural edits (append, insert, delete, sort) on multi-line inline arrays and tables now preserve deliberate blank lines elsewhere in the value instead of collapsing them.
- Deleting the predecessor of a shared-row item in a multi-line inline array or table now re-indents the surviving follower to the canonical row indent instead of leaving its one-space inline separator in place.
- Deleting the only content of an implicit table no longer makes the emptied table vanish from the output.
- Synthesising a section header no longer captures a trailing sibling key under the wrong table.
- Filling a previously-empty inline table now pads the braces (
{ a = 1 }) to match synthesis and.format(). - Assigning a parsed document or table into another document now keeps its comments and formatting, instead of dropping them.
- Overwriting a section that has array-of-tables children now keeps those children under the section, instead of moving them to the end of the document.
- Re-attaching a section that was removed (via
del,pop, or overwrite) now keeps its comments and formatting, instead of dropping them.
- Overwriting a key with a body-less section (one whose only child is an array-of-tables) no longer wipes the whole document on dump.
- Overwriting a section or array-of-tables and then re-using an entry read from the displaced value no longer silently drops the entry's body.
- Sorting an array-of-tables entry that has a nested array-of-tables child no longer leaves the rendered order out of sync with the logical key order.
- Formatting fixes when dealing with arrays whose elements use leading rather than trailing commas
- Various in-place edits —
sort, overwriting a key, and appending, inserting, reversing or sorting array-of-tables entries — could place a value where re-reading the file would attribute it to the wrong table, silently corrupting the document or desyncing it from the rendered output. These cases (covering nested arrays-of-tables, dotted keys, inline tables, and out-of-order or auto-promoted section headers) are fixed. - Replacing an out-of-order subsection (e.g.
[foo.bar]defined before[foo]) with a dict / inline value no longer emits the new binding outside its parent section. Array.extend(arr)/AoT.extend(aot)(and the corresponding+=forms) no longer hang when extending a sequence with itself.- A failed out-of-range
Array.comments[i] = ...no longer silently promotes a single-line array to multi-line form.
- Assorted mutation, comment, and scanner edge-case fixes.
- Appending to, or deleting the tail of, a multi-line inline value whose last item carries an EOL comment no longer mangles the closing bracket layout.
- Reordering or deleting the last entry of a multi-line inline table no longer swaps EOL comments between entries.
- Assigning
Array.multilineto its current value is now a no-op. Array.format/Container.formatandArray.set_multiline(True)no longer drop a row-attached comment on the opening[/{line.Array.set_multiline(True)on an empty array no longer drops a bracket-line comment and now aligns the closing]to the outer indent.Table.format()on an AoT entry no longer reformats sibling entries of the same array.format()on a multi-line inline value no longer inserts a spurious blank line after an item that carries an EOL comment.- Deleting the first KV, section, or AoT entry no longer drops the document preamble.
- Deleting all items from a multi-line inline array or inline table no longer leaves a stray indent line.
- Slice-assigning to a multi-line inline array no longer leaves a stray indent inside the brackets when emptied, drops bracket-EOL comments, or wipes above-block comments on surviving items.
- Adding an entry to an empty multi-line inline table now restores the canonical indent and trailing comma.
- Emptying then re-adding to a bracketed value with a bracket-EOL comment no longer drops the comment or leaves a blank line.
- Inline-table mutation (append / delete / sort) no longer drops above-bracket
and above-entry comment blocks;
Array.sort()andArray.reverse()now preserve per-position indents and above-]comments rather than wholesale-restamping them. Array.leading_comments[i] = ()no longer silently promotes a single-line array to multi-line (the empty assignment is a semantic delete-if-present).
Container.has_header(key)predicate: does this child render with a[header]line.
Table.header_commentandTable.header_leading_commentsgetters returnNone/()on headerless containers (implicit sections, document root) instead of raising.
Container.sort()no longer fabricates a leading blank line when a nested AoT child appeared before its explicit parent header in source.format()no longer strips the indent of full-line comments inside multi-line inline arrays / inline tables.Array.set_multiline(multiline=True, ...)no longer drops the last element's EOL comment when synthesising a trailing comma, and no longer inserts a blank line before the closing bracket when the last item carries an EOL comment.Document.format()now canonicalises the document epilogue as well as the body.
Container.sortnow keeps sections and arrays-of-tables after bare keys instead of raisingValueError.
Container.format(*, comments: bool = True)andArray.format(*, comments: bool = True)reformat a section, document, table, or inline array in place. See docs/layout.md for details.
Container.sort(*, key=None, reverse=False)reorders a section, document, table, or inline table's direct child keys in place, preserving per-key trivia. Mirrorslist.sort/AoT.sort.Container.leading_blockandTable.header_leading_blockexpose the full leading-trivia region (including blank-line-separated "orphan" comments) as a tuple ofstr | None, whereNoneis a blank line.Table.is_inlineproperty to distinguish inline{...}tables from[section]blocks when walking a parsed document.
- Mutating
comments,leading_comments, orleading_blockon a detachedTable.section()/Table.inline()now raises a clearTOMLErrorpointing at the attach-first workaround instead of a misleadingKeyError("key '...' not in container"). Container.leading_blockandTable.header_leading_blockno longer overlap withDocument.preambleat the document head slot. Round-tripping or reordering sections no longer migrates the preamble into a section body.- Per-key clone of a sub-table that came from dotted-key form (e.g.
dst["x"]["v"] = src["x"]["v"]where the source was[x]\nv.w = 1) now preserves the dotted form on the destination instead of silently promoting it to an explicit[x.v]header. - Cross-document
dst["k"] = src["k"]now preserves the source section header's indent even when no leading comment is attached. - Multi-component
install("a.b", value)where the implicit parent must be synthesised now preserves source header trivia (leading comments) on the installed child, by routing through the standard__setitem__clone dispatch rather than the synthesis fallback. Same fix applies to cross-documentdst["a"] = src["a"]when the source has only implicit parents. - Cross-document
dst[k] = src[k],aot.append(entry)andaot[i] = entrynow preserve nested arrays-of-tables inside the cloned subtree. - Cross-document
dst[k] = src[k]now preserves the source table header's leading comment block. - Adding a sub-section to an empty placeholder section no longer clears
Document.preamble. Array.sort/Array.reversenow keep per-item leading comments with their items and stop the closing]from gluing to the new last item.- Cross-document
dst[k] = src_sectionunder an empty placeholder parent now demotes the parent to an implicit super-table instead of leaving a stray bare[parent]header line. - Assigning a section table or
AoTas a value of an inline table now consistently raisesTOMLErrorwith a clear message, including for detachedTable.inline()factories (previously: silently accepted, thenNotImplementedErrorat attach time). - Comments on dotted-key entries are now reachable through the dotted-parent
container (
project["urls"].comments["homepage"]). Array.sort()/Array.reverse()no longer leave the new item 0 carrying the old position-0 indent in multi-line arrays.- Cross-document
dst[k] = src[k]andAoT.sort()no longer drag the source document's preamble onto the destination or surviving entries. Array.leading_commentsdel/pop/clear at non-zero indices, and insert/append next to items carrying EOL comments, no longer misattribute or duplicate adjacent comments on re-render.
- Overwriting an implicit table with a scalar or inline value now preserves the original position in the document instead of moving the binding to the end.
- Mutations to inline tables and arrays nested inside an
Arrayare now rendered. Array.set_multilineno longer inserts\nnewlines into a CRLF document.Array.__imul__(arr *= n) now keeps replicated inline tables and arrays live: mutations through the copies render.Document.install/Document.ensure_tablenow accept anySequence[str](e.g.collections.deque), matching the documented contract and the public type signature.- Detached
AoT.append/AoT.insert/AoT[idx] = ...now accept anyMapping(e.g.MappingProxyType), matching their declared type signature and the attached paths. Document(data=...)no longer coerces a user-suppliedArrayof mappings into anAoT; the caller's explicit inline-array choice is preserved.Table.section(mapping)andAoT(entries)now reject non-string keys at construction with a clearTypeError, matchingTable.inlineandDocument(data=...). AttachedAoT.append/.add/.insertand inline-table synthesis from a plaindictnow produce the same unified"TOML keys must be str"error instead of crashing deep in the layout pipeline.copy.copy/copy.deepcopyof an inlineTablenow returns an inlineTableinstead of silently converting to a section table.
- Live-attaching an
Arrayinto a document with a different line ending no longer produces mixed\n/\r\nnewlines.
- Mutating an implicit table whose only descendants were created via chained
ensure_table/ nested AoT attaches no longer trips an internal anchor-not-found assertion.
- Rewrite internals for improved performance
- Faster
installon deep dotted paths in large documents. Table.clearno longer scales quadratically with document size.- Faster delete of sub-tables and AoT children in large documents.
AoT.clearand bulkdel aot[:]no longer scale quadratically.
- Adding a dotted key to a section now respects the section's indent and blank-line policy, and inserts a separating newline when needed.
- Reject float literals with a misplaced underscore between the exponent sign
and its digits (e.g.
1e+_1).
Table.entry/Table.get_entryfor untyped path access.
- The typed accessors (
table,array,aot, and theirget_*variants) now accept aSequence[str]as well as a dotted string.
Table.installandTable.ensure_tablenow accept anySequence[str]for the key path, not justtuple[str, ...].
tomlrt.parse(); usetomlrt.loads()instead.
Document(mapping=None)public constructor.
tomlrt.document(...); usetomlrt.Document(...)instead.
- Faster parsing, especially on large documents with many arrays-of-tables.
- Documentation pass and small typing tightenings across the public API.
The
Document.cstescape hatch is gone.
- Synthesising parent headers and installing sections no longer mix blank-line and compact styles.
- Replacing an AoT entry no longer injects a stray blank between the new entry and its surviving sibling in compact documents.
- Newly appended/inserted AoT entries now adopt the indent style of their siblings' KV lines instead of rendering flush-left.
- Public
TomlInputtype alias for typing helpers that build or mutate document fragments.
AoT.sort()now requires thekey=argument, sinceTableentries are not orderable.
- The legacy
SectionSpectag type.
Table.inline()now renders with spaced braces ({ k = v }), matching the style produced when assigning a plaindict.
- Many bug fixes
- A wide range of round-trip, comment-preservation, and structural mutation bugs across tables, arrays-of-tables, and multi-line arrays.
- A broad sweep of correctness fixes in the mutation API, covering every flavour
of structural change: assignment into and through array-of-tables,
append/insert/pop on multi-line arrays with comments, attached-AoT
installation, comment-trivia preservation across promotion and shifts, CRLF
line-ending preservation, and copy/deepcopy of
ArrayandAoTsubviews. Several silent corruptions (CST and dict-side state diverging after a mutation) are gone, and a number of error messages are now more specific about which value was rejected and why.
-
Structural assignment is now driven by the value, not the method name. The parallel
set_table/set_aot/set_arraymethods have been removed in favour of a single assignment path:doc[k] = Table.section({...}) # [k] standard section doc[k] = {...} # k = { ... } inline table doc[k] = AoT([{...}, {...}]) # [[k]] array of tables doc[k] = Array([...], multiline=True) # multi-line array value
Table.sectionis a classmethod factory returning the public tag type :class:SectionSpec. :class:AoTand :class:Arraycan now be constructed standalone and then assigned. -
New
Table.install(path, value)accepts either a dottedstrpath or atuple[str, ...]of literal segments. Tuples provide an escape for keys that legitimately contain a.::doc.install(("foo.bar",), 1) # "foo.bar" = 1 (single segment) doc.install("foo.bar", 1) # [foo]\nbar = 1 (dotted path)ensure_tablealso accepts both forms. -
__setitem__no longer splitsstrkeys on.; a plainstris always treated as a single literal segment, matching the standarddictcontract. Useinstall()for dotted-path placement.
Table.set_table,Table.set_aot,Table.set_array. Use the value-driven equivalents above, orTable.installfor dotted paths / tuple keys.
AoT.insert(0, …)now adds a blank-line separator between the newly inserted[[..]]entry and the existing one that follows it (matching sibling spacing, defaulting to blank-separated). The policy previously only looked at preceding content, so inserting before existing entries glued two[[..]]headers together.- The dict-style view of a parsed :class:
Documentno longer goes stale relative to :func:dumpsafter structural mutations. Assigning over an array-of-tables, deleting then re-binding a key, andpop()followed by re-assignment all kept showing the pre-mutation value while the rendered TOML reflected the new state. The cached per-table section scope that drove this has been replaced with on-demand derivation from the surrounding AoT entry (when there is one), so dict reads anddumpsoutput are always consistent. - Mutations on a sub-table reached via a dotted key from an ancestor section now
work correctly.
Given
poetry.name = "x"written inside[tool],doc["tool"]["poetry"].pop("name")anddoc["tool"]["poetry"]["name"] = "y"previously raisedKeyErroror duplicated the key in a new section; both now edit the original entry in place. - Setting :attr:
Document.preambleon an empty document and then adding content now renders the preamble at the top of the file. It was previously parked in the document's trailing trivia and emitted after the new content (sodumpsproducedx = 1\n# c\ninstead of# c\n\nx = 1\n); the comment also became invisible to the getter once content arrived. Migration now happens at the insertion site for any ofdoc[k] = …, :meth:Table.install, :meth:AoT.insert, or AoT assignment. - :meth:
Table.promote_arraynow carries the source inline-table KV's leading comments / blank lines onto the first new[[..]]header, and any trailing EOL comment onto the last new entry. The trivia was previously discarded outright, so promoting an inline array silently dropped any authoring comments around it. - Import of
assert_neverno longer breaks on Python 3.10. The symbol is now sourced fromtyping_extensionson interpreters older than 3.11, mirroring the existingoverrideimport.
Tableis now a realdictsubclass.isinstance(t, dict)returnsTrue,**tableunpacking works, and any third-party API typed againstdict[str, Any]/isinstance(x, dict)now accepts aTabledirectly. Reads go throughdict's native__iter__/__getitem__/__len__/__contains__; the CST is still the single source of truth for layout (whitespace, comments, key order, table-shape choices) and is kept in lock-step with the dict storage on every mutation. Held references behave like ordinary Python dict references:del doc['foo']orphans the heldTable(data preserved, mutations no longer reach the document) and re-binding the path installs a freshTablerather than re-attaching the old one. Identity is stable:doc['foo'] is doc['foo']and the same goes for nested children.Table.popnow returns the actual stored value (an orphanedTable/AoT/Arrayfor container values) rather than a deep plain-Python snapshot. UseTable.to_dict()/Array.to_list()first if you need a snapshot.- Detached tables and AoTs are now isolated from the original document.
Structural mutations on a held container after its parent removed it
(
set_table,set_aot,promote_inline,promote_array,AoT.add,AoT.append,AoT.insert…) no longer leak back into the document by re-creating the removed sections. AoT.popnow returns the live entry object that was at the given index (then orphans it), mirroringTable.popand preserving identity with whatever the caller previously read out of the AoT.Tablenow subclassesMutableMapping[str, Any](wasMutableMapping[str, TomlValue]), andTable.__getitem__returnsAny(was the strictScalar | Array | AoT | Tableunion). Symmetrically,Arraynow subclasseslist[Any]andArray.__getitem__/Array.popreturnAny. This matches whattomllib.loadsreturns (dict[str, Any]) and whattomlkitdoes, and lets chained subscripts likedoc["tool"]["poetry"]["name"]type-check withoutcast. Consumers typed againstMutableMapping[str, Any]orlist[Any](which is most of the ecosystem) now compose withTable/Arraydirectly. The strict return type is still available through the.table()/.array()/.aot()accessors and theirget_*counterparts when you want it.Array.append/extend/insert/__setitem__now type their input parameter asobjectinstead of the narrowerTomlValuealias, matchingTable.__setitem__and the underlyingvalue_to_nodeconverter. At runtime they always accepted arbitrary Python values (plaindict-> inline table, plainlist-> inline array); the annotations were lying.- Synthesised inline arrays no longer carry padding spaces inside the brackets.
[1, 2, 3]instead of[ 1, 2, 3 ], and[1]instead of[ 1 ]. Inter-element spaces are unchanged. Inline tables ({ a = 1, b = 2 }) still keep their conventional inner spacing. Parsed arrays round-trip with their original spacing. - Modest parse speedup: cache
Key.pathso the dotted-key tuple is built once per key, and pass the parent's already-scoped section list through to child_StdTableconstructors so each child's initial population walks only its own subtree instead of the whole document.
Table.get_table(key, default=None),Table.get_array(...),Table.get_aot(...)and the analogousArray.get_table(index, ...)/Array.get_array(index, ...)are typed-but-optional accessors. They mirror the strict.table()/.array()/.aot()accessors but returndefault(orNone) when the key/index is missing, rather than raising. A wrong-type entry still raises :class:TypeError: missing is "no answer", wrong shape is a bug. Overloads preserve the type of a user-supplied default.Table.to_dict()/Array.to_list()/AoT.to_list()return a deep, plain-Python copy of the view, walking nested tomlrt views into realdict/listcontainers. Intended for the interop boundary with consumers that expect actualdictobjects (fastjsonschema,pydantic, JSON encoders, code that doesisinstance(x, dict)). Scalars are returned as-is; the result shares no mutable state with the document.AoT.add(entry={})appendsentryand returns the new :class:Tableview, sparing users theaot.append(...); aot[-1]two-step when they need a handle to the freshly-added entry for further population.tomlrt.document(data=None)returns a fresh :class:Document, optionally populated from a mapping. Without arguments, equivalent totomlrt.parse("")but more discoverable for the "build a TOML file from scratch" use case. With a mapping, recursively walks the data: nested mappings become[section]blocks, lists of mappings become[[array.of.tables]]blocks, and leaf values use ordinary key-value assignment. The resulting document shares no mutable state with the input.Array.set_multiline(*, multiline, indent=" ")and the read/writeArray.multilineproperty toggle an inline array between single-line and multi-line layout.Table.set_aot(key, entries=())creates an array-of-tables atkey(overwriting any existing value) and returns the live view, so users can build[[ ... ]]sections without going through the inline-array path.Table.set_table(key, value=())creates a standard-table section atkey, replacing any existing value. Accepts dotted paths (e.g."tool.poetry"); intermediate tables are kept implicit so no empty[tool]super-table headers are emitted.Table.ensure_table(key)returns the table atkey, creating an empty section if absent. Accepts dotted paths and walks through implicit super-tables.Table.set_array(key, items=(), *, multiline=False, indent=" ")creates an inline array atkey(replacing any existing value), optionally laid out one item per line. Accepts dotted paths so a multiline array deep in the tree can be created in a single call.Document.preambleandDocument.epilogueproperties expose the comment block at the top and bottom of the document. They are blank-line-separated from any structural content (and from any "attached" leading comment of the first key), so writing one will not clobber the other or any per-key comment block.Table.set_aotnow accepts dotted paths, mirroringset_table.Table.table,Table.arrayandTable.aottyped accessors now accept dotted paths for navigation through nested structures.Table.promote_array(key)converts an existing inline array of inline tables into an array-of-tables, mirroring the existingTable.promote_inlinefor tables.
- An empty array whose source contains a newline inside the brackets (
a = [\n]) now round-trips and accepts subsequentappendcalls while preserving its multi-line shape. Table.set_aotandTable.promote_arraynow lay their[[ ... ]]blocks out with blank-line separators between entries, and with a blank line between the block and any preceding content.- Programmatically appending to an
AoT(or appending the second entry into a freshly-built one) now blank-line-separates the new[[ ... ]]header from whatever precedes it in the document, matching round-trip output of equivalent parsed input. Previously, fresh AoTs and AoTs whose new entries followed an unrelated sub-section were rendered with the headers visually glued together. When existing entries clearly establish a no-blank-line style (≥ 2 sibling gaps to learn from), that style is still respected.
Initial release.