Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tomlkit/items.py: 60%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1347 statements  

1from __future__ import annotations 

2 

3import abc 

4import copy 

5import dataclasses 

6import inspect 

7import re 

8import string 

9 

10from collections.abc import Collection 

11from collections.abc import Iterable 

12from collections.abc import Iterator 

13from collections.abc import Sequence 

14from datetime import date 

15from datetime import datetime 

16from datetime import time 

17from datetime import timedelta 

18from datetime import tzinfo 

19from enum import Enum 

20from typing import TYPE_CHECKING 

21from typing import Any 

22from typing import TypeVar 

23from typing import overload 

24 

25from tomlkit._compat import PY38 

26from tomlkit._compat import decode 

27from tomlkit._types import _CustomDict 

28from tomlkit._types import _CustomFloat 

29from tomlkit._types import _CustomInt 

30from tomlkit._types import _CustomList 

31from tomlkit._utils import CONTROL_CHARS 

32from tomlkit._utils import escape_string 

33from tomlkit.exceptions import ConvertError 

34from tomlkit.exceptions import InvalidStringError 

35 

36 

37if TYPE_CHECKING: 

38 from typing import Protocol 

39 

40 from tomlkit import container 

41 from tomlkit.container import OutOfOrderTableProxy 

42 

43 class Encoder(Protocol): 

44 def __call__(self, __value: Any, /) -> Item: ... 

45 

46 

47ItemT = TypeVar("ItemT", bound="Item") 

48CUSTOM_ENCODERS: list[Encoder] = [] 

49AT = TypeVar("AT", bound="AbstractTable") 

50 

51 

52@overload 

53def item(value: bool, _parent: Item | None = ..., _sort_keys: bool = ...) -> Bool: ... # type: ignore[overload-overlap] 

54 

55 

56@overload 

57def item(value: int, _parent: Item | None = ..., _sort_keys: bool = ...) -> Integer: ... 

58 

59 

60@overload 

61def item(value: float, _parent: Item | None = ..., _sort_keys: bool = ...) -> Float: ... 

62 

63 

64@overload 

65def item(value: str, _parent: Item | None = ..., _sort_keys: bool = ...) -> String: ... 

66 

67 

68@overload 

69def item( # type: ignore[overload-overlap] 

70 value: datetime, _parent: Item | None = ..., _sort_keys: bool = ... 

71) -> DateTime: ... 

72 

73 

74@overload 

75def item(value: date, _parent: Item | None = ..., _sort_keys: bool = ...) -> Date: ... 

76 

77 

78@overload 

79def item(value: time, _parent: Item | None = ..., _sort_keys: bool = ...) -> Time: ... 

80 

81 

82@overload 

83def item( 

84 value: Sequence[dict[str, Any]], _parent: Item | None = ..., _sort_keys: bool = ... 

85) -> AoT: ... 

86 

87 

88@overload 

89def item( 

90 value: Sequence[Any], _parent: Item | None = ..., _sort_keys: bool = ... 

91) -> Array: ... 

92 

93 

94@overload 

95def item( 

96 value: dict[str, Any], _parent: Array = ..., _sort_keys: bool = ... 

97) -> InlineTable: ... 

98 

99 

100@overload 

101def item( 

102 value: dict[str, Any], _parent: Item | None = ..., _sort_keys: bool = ... 

103) -> Table: ... 

104 

105 

106@overload 

107def item(value: ItemT, _parent: Item | None = ..., _sort_keys: bool = ...) -> ItemT: ... 

108 

109 

110@overload 

111def item(value: object, _parent: Item | None = ..., _sort_keys: bool = ...) -> Item: ... 

112 

113 

114def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> Item: 

115 """Create a TOML item from a Python object. 

116 

117 :Example: 

118 

119 >>> item(42) 

120 42 

121 >>> item([1, 2, 3]) 

122 [1, 2, 3] 

123 >>> item({'a': 1, 'b': 2}) 

124 a = 1 

125 b = 2 

126 """ 

127 

128 from tomlkit.container import Container 

129 

130 if isinstance(value, Item): 

131 return value 

132 

133 if isinstance(value, bool): 

134 return Bool(value, Trivia()) 

135 elif isinstance(value, int): 

136 return Integer(value, Trivia(), str(value)) 

137 elif isinstance(value, float): 

138 return Float(value, Trivia(), str(value)) 

139 elif isinstance(value, dict): 

140 table_constructor = ( 

141 InlineTable if isinstance(_parent, (Array, InlineTable)) else Table 

142 ) 

143 val = table_constructor(Container(), Trivia(), False) 

144 for k, v in sorted( 

145 value.items(), 

146 key=lambda i: (isinstance(i[1], dict), i[0]) if _sort_keys else 1, 

147 ): 

148 val[k] = item(v, _parent=val, _sort_keys=_sort_keys) 

149 

150 return val 

151 elif isinstance(value, (list, tuple)): 

152 a: AoT | Array 

153 if ( 

154 value 

155 and all(isinstance(v, dict) for v in value) 

156 and (_parent is None or isinstance(_parent, Table)) 

157 ): 

158 a = AoT([]) 

159 table_constructor = Table 

160 else: 

161 a = Array([], Trivia()) 

162 table_constructor = InlineTable 

163 

164 for v in value: 

165 if isinstance(v, dict): 

166 table = table_constructor(Container(), Trivia(), True) 

167 

168 for k, _v in sorted( 

169 v.items(), 

170 key=lambda i: (isinstance(i[1], dict), i[0] if _sort_keys else 1), 

171 ): 

172 i = item(_v, _parent=table, _sort_keys=_sort_keys) 

173 if isinstance(table, InlineTable): 

174 i.trivia.trail = "" 

175 

176 table[k] = i 

177 

178 v = table 

179 

180 a.append(v) 

181 

182 return a 

183 elif isinstance(value, str): 

184 return String.from_raw(value) 

185 elif isinstance(value, datetime): 

186 return DateTime( 

187 value.year, 

188 value.month, 

189 value.day, 

190 value.hour, 

191 value.minute, 

192 value.second, 

193 value.microsecond, 

194 value.tzinfo, 

195 Trivia(), 

196 value.isoformat().replace("+00:00", "Z"), 

197 ) 

198 elif isinstance(value, date): 

199 return Date(value.year, value.month, value.day, Trivia(), value.isoformat()) 

200 elif isinstance(value, time): 

201 return Time( 

202 value.hour, 

203 value.minute, 

204 value.second, 

205 value.microsecond, 

206 value.tzinfo, 

207 Trivia(), 

208 value.isoformat(), 

209 ) 

210 else: 

211 for encoder in CUSTOM_ENCODERS: 

212 try: 

213 # Check if encoder accepts keyword arguments for backward compatibility 

214 sig = inspect.signature(encoder) 

215 if "_parent" in sig.parameters or any( 

216 p.kind == p.VAR_KEYWORD for p in sig.parameters.values() 

217 ): 

218 # New style encoder that can accept additional parameters 

219 rv = encoder(value, _parent=_parent, _sort_keys=_sort_keys) # type: ignore[call-arg] 

220 else: 

221 # Old style encoder that only accepts value 

222 rv = encoder(value) 

223 except ConvertError: 

224 pass 

225 else: 

226 if not isinstance(rv, Item): 

227 raise ConvertError( 

228 f"Custom encoder is expected to return an instance of Item, got {type(rv)}" 

229 ) 

230 return rv 

231 

232 raise ConvertError(f"Unable to convert an object of {type(value)} to a TOML item") 

233 

234 

235class StringType(Enum): 

236 # Single Line Basic 

237 SLB = '"' 

238 # Multi Line Basic 

239 MLB = '"""' 

240 # Single Line Literal 

241 SLL = "'" 

242 # Multi Line Literal 

243 MLL = "'''" 

244 

245 @classmethod 

246 def select(cls, literal: bool = False, multiline: bool = False) -> StringType: 

247 return { 

248 (False, False): cls.SLB, 

249 (False, True): cls.MLB, 

250 (True, False): cls.SLL, 

251 (True, True): cls.MLL, 

252 }[(literal, multiline)] 

253 

254 @property 

255 def escaped_sequences(self) -> Collection[str]: 

256 # https://toml.io/en/v1.0.0#string 

257 escaped_in_basic = CONTROL_CHARS | {"\\"} 

258 allowed_in_multiline = {"\n", "\r"} 

259 return { 

260 StringType.SLB: escaped_in_basic | {'"'}, 

261 StringType.MLB: (escaped_in_basic | {'"""'}) - allowed_in_multiline, 

262 StringType.SLL: (), 

263 StringType.MLL: (), 

264 }[self] 

265 

266 @property 

267 def invalid_sequences(self) -> Collection[str]: 

268 # https://toml.io/en/v1.0.0#string 

269 forbidden_in_literal = CONTROL_CHARS - {"\t"} 

270 allowed_in_multiline = {"\n", "\r"} 

271 return { 

272 StringType.SLB: (), 

273 StringType.MLB: (), 

274 StringType.SLL: forbidden_in_literal | {"'"}, 

275 StringType.MLL: (forbidden_in_literal | {"'''"}) - allowed_in_multiline, 

276 }[self] 

277 

278 @property 

279 def unit(self) -> str: 

280 return self.value[0] 

281 

282 def is_basic(self) -> bool: 

283 return self is StringType.SLB or self is StringType.MLB 

284 

285 def is_literal(self) -> bool: 

286 return self is StringType.SLL or self is StringType.MLL 

287 

288 def is_singleline(self) -> bool: 

289 return self is StringType.SLB or self is StringType.SLL 

290 

291 def is_multiline(self) -> bool: 

292 return self is StringType.MLB or self is StringType.MLL 

293 

294 def toggle(self) -> StringType: 

295 return { 

296 StringType.SLB: StringType.MLB, 

297 StringType.MLB: StringType.SLB, 

298 StringType.SLL: StringType.MLL, 

299 StringType.MLL: StringType.SLL, 

300 }[self] 

301 

302 

303class BoolType(Enum): 

304 TRUE = "true" 

305 FALSE = "false" 

306 

307 def __bool__(self) -> bool: 

308 return {BoolType.TRUE: True, BoolType.FALSE: False}[self] 

309 

310 def __iter__(self) -> Iterator[str]: 

311 return iter(self.value) 

312 

313 def __len__(self) -> int: 

314 return len(self.value) 

315 

316 

317@dataclasses.dataclass 

318class Trivia: 

319 """ 

320 Trivia information (aka metadata). 

321 """ 

322 

323 # Whitespace before a value. 

324 indent: str = "" 

325 # Whitespace after a value, but before a comment. 

326 comment_ws: str = "" 

327 # Comment, starting with # character, or empty string if no comment. 

328 comment: str = "" 

329 # Trailing newline. 

330 trail: str = "\n" 

331 

332 def copy(self) -> Trivia: 

333 return dataclasses.replace(self) 

334 

335 

336class KeyType(Enum): 

337 """ 

338 The type of a Key. 

339 

340 Keys can be bare (unquoted), or quoted using basic ("), or literal (') 

341 quotes following the same escaping rules as single-line StringType. 

342 """ 

343 

344 Bare = "" 

345 Basic = '"' 

346 Literal = "'" 

347 

348 

349class Key(abc.ABC): 

350 """Base class for a key""" 

351 

352 sep: str 

353 _original: str 

354 _keys: list[SingleKey] 

355 _dotted: bool 

356 key: str 

357 

358 @abc.abstractmethod 

359 def __hash__(self) -> int: 

360 pass 

361 

362 @abc.abstractmethod 

363 def __eq__(self, __o: object) -> bool: 

364 pass 

365 

366 def is_dotted(self) -> bool: 

367 """If the key is followed by other keys""" 

368 return self._dotted 

369 

370 def __iter__(self) -> Iterator[SingleKey]: 

371 return iter(self._keys) 

372 

373 def concat(self, other: Key) -> DottedKey: 

374 """Concatenate keys into a dotted key""" 

375 keys = self._keys + other._keys 

376 return DottedKey(keys, sep=self.sep) 

377 

378 def is_multi(self) -> bool: 

379 """Check if the key contains multiple keys""" 

380 return len(self._keys) > 1 

381 

382 def as_string(self) -> str: 

383 """The TOML representation""" 

384 return self._original 

385 

386 def __str__(self) -> str: 

387 return self.as_string() 

388 

389 def __repr__(self) -> str: 

390 return f"<Key {self.as_string()}>" 

391 

392 

393class SingleKey(Key): 

394 """A single key""" 

395 

396 def __init__( 

397 self, 

398 k: str, 

399 t: KeyType | None = None, 

400 sep: str | None = None, 

401 original: str | None = None, 

402 ) -> None: 

403 if not isinstance(k, str): 

404 raise TypeError("Keys must be strings") 

405 

406 if t is None: 

407 if not k or any( 

408 c not in string.ascii_letters + string.digits + "-" + "_" for c in k 

409 ): 

410 t = KeyType.Basic 

411 else: 

412 t = KeyType.Bare 

413 

414 self.t = t 

415 if sep is None: 

416 sep = " = " 

417 

418 self.sep = sep 

419 self.key = k 

420 if original is None: 

421 key_str = escape_string(k) if t == KeyType.Basic else k 

422 original = f"{t.value}{key_str}{t.value}" 

423 

424 self._original = original 

425 self._keys = [self] 

426 self._dotted = False 

427 

428 @property 

429 def delimiter(self) -> str: 

430 """The delimiter: double quote/single quote/none""" 

431 return self.t.value 

432 

433 def is_bare(self) -> bool: 

434 """Check if the key is bare""" 

435 return self.t == KeyType.Bare 

436 

437 def __hash__(self) -> int: 

438 return hash(self.key) 

439 

440 def __eq__(self, other: Any) -> bool: 

441 if isinstance(other, Key): 

442 return isinstance(other, SingleKey) and self.key == other.key 

443 

444 return bool(self.key == other) 

445 

446 

447class DottedKey(Key): 

448 def __init__( 

449 self, 

450 keys: Iterable[SingleKey], 

451 sep: str | None = None, 

452 original: str | None = None, 

453 ) -> None: 

454 self._keys = list(keys) 

455 if original is None: 

456 original = ".".join(k.as_string() for k in self._keys) 

457 

458 self.sep = " = " if sep is None else sep 

459 self._original = original 

460 self._dotted = False 

461 self.key = ".".join(k.key for k in self._keys) 

462 

463 def __hash__(self) -> int: 

464 return hash(tuple(self._keys)) 

465 

466 def __eq__(self, __o: object) -> bool: 

467 return isinstance(__o, DottedKey) and self._keys == __o._keys 

468 

469 

470class Item: 

471 """ 

472 An item within a TOML document. 

473 """ 

474 

475 def __init__(self, trivia: Trivia) -> None: 

476 self._trivia = trivia 

477 

478 @property 

479 def trivia(self) -> Trivia: 

480 """The trivia element associated with this item""" 

481 return self._trivia 

482 

483 @property 

484 def discriminant(self) -> int: 

485 raise NotImplementedError() 

486 

487 def as_string(self) -> str: 

488 """The TOML representation""" 

489 raise NotImplementedError() 

490 

491 @property 

492 def value(self) -> Any: 

493 return self 

494 

495 def unwrap(self) -> Any: 

496 """Returns as pure python object (ppo)""" 

497 raise NotImplementedError() 

498 

499 # Helpers 

500 

501 def comment(self, comment: str) -> Item: 

502 """Attach a comment to this item""" 

503 if not comment.strip().startswith("#"): 

504 comment = "# " + comment 

505 

506 self._trivia.comment_ws = " " 

507 self._trivia.comment = comment 

508 

509 return self 

510 

511 def indent(self, indent: int) -> Item: 

512 """Indent this item with given number of spaces""" 

513 if self._trivia.indent.startswith("\n"): 

514 self._trivia.indent = "\n" + " " * indent 

515 else: 

516 self._trivia.indent = " " * indent 

517 

518 return self 

519 

520 def is_boolean(self) -> bool: 

521 return isinstance(self, Bool) 

522 

523 def is_table(self) -> bool: 

524 return isinstance(self, Table) 

525 

526 def is_inline_table(self) -> bool: 

527 return isinstance(self, InlineTable) 

528 

529 def is_aot(self) -> bool: 

530 return isinstance(self, AoT) 

531 

532 def _getstate(self, protocol: int = 3) -> tuple[object, ...]: 

533 return (self._trivia,) 

534 

535 def __reduce__(self) -> tuple[type, tuple[object, ...]]: 

536 return self.__reduce_ex__(2) 

537 

538 def __reduce_ex__(self, protocol: int) -> tuple[type, tuple[object, ...]]: # type: ignore[override] 

539 return self.__class__, self._getstate(protocol) 

540 

541 def __getitem__(self, key: Key | str | int) -> Any: 

542 raise TypeError(f"{type(self).__name__} does not support item access") 

543 

544 def __setitem__(self, key: Key | str | int, value: Any) -> None: 

545 raise TypeError(f"{type(self).__name__} does not support item assignment") 

546 

547 def __delitem__(self, key: Key | str | int) -> None: 

548 raise TypeError(f"{type(self).__name__} does not support item deletion") 

549 

550 

551class Whitespace(Item): 

552 """ 

553 A whitespace literal. 

554 """ 

555 

556 def __init__(self, s: str, fixed: bool = False) -> None: 

557 self._s = s 

558 self._fixed = fixed 

559 

560 @property 

561 def s(self) -> str: 

562 return self._s 

563 

564 @property 

565 def value(self) -> str: 

566 """The wrapped string of the whitespace""" 

567 return self._s 

568 

569 @property 

570 def trivia(self) -> Trivia: 

571 raise RuntimeError("Called trivia on a Whitespace variant.") 

572 

573 @property 

574 def discriminant(self) -> int: 

575 return 0 

576 

577 def is_fixed(self) -> bool: 

578 """If the whitespace is fixed, it can't be merged or discarded from the output.""" 

579 return self._fixed 

580 

581 def as_string(self) -> str: 

582 return self._s 

583 

584 def __repr__(self) -> str: 

585 return f"<{self.__class__.__name__} {self._s!r}>" 

586 

587 def _getstate(self, protocol: int = 3) -> tuple[str, bool]: 

588 return self._s, self._fixed 

589 

590 

591class Comment(Item): 

592 """ 

593 A comment literal. 

594 """ 

595 

596 @property 

597 def discriminant(self) -> int: 

598 return 1 

599 

600 def as_string(self) -> str: 

601 return ( 

602 f"{self._trivia.indent}{decode(self._trivia.comment)}{self._trivia.trail}" 

603 ) 

604 

605 def __str__(self) -> str: 

606 return f"{self._trivia.indent}{decode(self._trivia.comment)}" 

607 

608 

609class Integer(Item, _CustomInt): 

610 """ 

611 An integer literal. 

612 """ 

613 

614 def __new__(cls, value: int, trivia: Trivia, raw: str) -> Integer: 

615 return int.__new__(cls, value) 

616 

617 def __init__(self, value: int, trivia: Trivia, raw: str) -> None: 

618 super().__init__(trivia) 

619 self._original = value 

620 self._raw = raw 

621 self._sign = False 

622 

623 if re.match(r"^[+\-]\d+$", raw): 

624 self._sign = True 

625 

626 def unwrap(self) -> int: 

627 return self._original 

628 

629 __int__ = unwrap 

630 

631 def __hash__(self) -> int: 

632 return hash(self.unwrap()) 

633 

634 @property 

635 def discriminant(self) -> int: 

636 return 2 

637 

638 @property 

639 def value(self) -> int: 

640 """The wrapped integer value""" 

641 return self 

642 

643 def as_string(self) -> str: 

644 return self._raw 

645 

646 def _new(self, result: int) -> Integer: 

647 raw = str(result) 

648 if self._sign and result >= 0: 

649 raw = f"+{raw}" 

650 

651 return Integer(result, self._trivia, raw) 

652 

653 def _getstate(self, protocol: int = 3) -> tuple[int, Trivia, str]: 

654 return int(self), self._trivia, self._raw 

655 

656 # int methods — explicit typed wrappers 

657 def __abs__(self) -> Integer: 

658 return self._new(int.__abs__(self)) 

659 

660 def __add__(self, other: object) -> Integer: 

661 result = int.__add__(self, other) # type: ignore[operator] 

662 if result is NotImplemented: 

663 return result # type: ignore[return-value] 

664 return self._new(result) 

665 

666 def __and__(self, other: object) -> Integer: 

667 result = int.__and__(self, other) # type: ignore[operator] 

668 if result is NotImplemented: 

669 return result # type: ignore[return-value] 

670 return self._new(result) 

671 

672 def __ceil__(self) -> Integer: 

673 return self._new(int.__ceil__(self)) 

674 

675 __eq__ = int.__eq__ 

676 

677 def __floor__(self) -> Integer: 

678 return self._new(int.__floor__(self)) 

679 

680 def __floordiv__(self, other: object) -> Integer: 

681 result = int.__floordiv__(self, other) # type: ignore[operator] 

682 if result is NotImplemented: 

683 return result # type: ignore[return-value] 

684 return self._new(result) 

685 

686 def __invert__(self) -> Integer: 

687 return self._new(int.__invert__(self)) 

688 

689 __le__ = int.__le__ 

690 

691 def __lshift__(self, other: object) -> Integer: 

692 result = int.__lshift__(self, other) # type: ignore[operator] 

693 if result is NotImplemented: 

694 return result # type: ignore[return-value] 

695 return self._new(result) 

696 

697 __lt__ = int.__lt__ 

698 

699 def __mod__(self, other: object) -> Integer: 

700 result = int.__mod__(self, other) # type: ignore[operator] 

701 if result is NotImplemented: 

702 return result # type: ignore[return-value] 

703 return self._new(result) 

704 

705 def __mul__(self, other: object) -> Integer: 

706 result = int.__mul__(self, other) # type: ignore[operator] 

707 if result is NotImplemented: 

708 return result # type: ignore[return-value] 

709 return self._new(result) 

710 

711 def __neg__(self) -> Integer: 

712 return self._new(int.__neg__(self)) 

713 

714 def __or__(self, other: object) -> Integer: 

715 result = int.__or__(self, other) # type: ignore[operator] 

716 if result is NotImplemented: 

717 return result # type: ignore[return-value] 

718 return self._new(result) 

719 

720 def __pos__(self) -> Integer: 

721 return self._new(int.__pos__(self)) 

722 

723 def __pow__(self, other: int, mod: int | None = None) -> Integer: # type: ignore[override] 

724 result = ( 

725 int.__pow__(self, other) if mod is None else int.__pow__(self, other, mod) 

726 ) 

727 return self._new(result) 

728 

729 def __radd__(self, other: object) -> Integer: 

730 result = int.__radd__(self, other) # type: ignore[operator] 

731 if result is NotImplemented: 

732 return result # type: ignore[return-value] 

733 return self._new(result) 

734 

735 def __rand__(self, other: object) -> Integer: 

736 result = int.__rand__(self, other) # type: ignore[operator] 

737 if result is NotImplemented: 

738 return result # type: ignore[return-value] 

739 return self._new(result) 

740 

741 def __rfloordiv__(self, other: object) -> Integer: 

742 result = int.__rfloordiv__(self, other) # type: ignore[operator] 

743 if result is NotImplemented: 

744 return result # type: ignore[return-value] 

745 return self._new(result) 

746 

747 def __rlshift__(self, other: object) -> Integer: 

748 result = int.__rlshift__(self, other) # type: ignore[operator] 

749 if result is NotImplemented: 

750 return result # type: ignore[return-value] 

751 return self._new(result) 

752 

753 def __rmod__(self, other: object) -> Integer: 

754 result = int.__rmod__(self, other) # type: ignore[operator] 

755 if result is NotImplemented: 

756 return result # type: ignore[return-value] 

757 return self._new(result) 

758 

759 def __rmul__(self, other: object) -> Integer: 

760 result = int.__rmul__(self, other) # type: ignore[operator] 

761 if result is NotImplemented: 

762 return result # type: ignore[return-value] 

763 return self._new(result) 

764 

765 def __ror__(self, other: object) -> Integer: 

766 result = int.__ror__(self, other) # type: ignore[operator] 

767 if result is NotImplemented: 

768 return result # type: ignore[return-value] 

769 return self._new(result) 

770 

771 def __round__(self, ndigits: int = 0) -> Integer: # type: ignore[override] 

772 return self._new(int.__round__(self, ndigits)) 

773 

774 def __rpow__(self, other: int, mod: int | None = None) -> Integer: # type: ignore[misc] 

775 result = ( 

776 int.__rpow__(self, other) if mod is None else int.__rpow__(self, other, mod) 

777 ) 

778 return self._new(result) 

779 

780 def __rrshift__(self, other: object) -> Integer: 

781 result = int.__rrshift__(self, other) # type: ignore[operator] 

782 if result is NotImplemented: 

783 return result # type: ignore[return-value] 

784 return self._new(result) 

785 

786 def __rshift__(self, other: object) -> Integer: 

787 result = int.__rshift__(self, other) # type: ignore[operator] 

788 if result is NotImplemented: 

789 return result # type: ignore[return-value] 

790 return self._new(result) 

791 

792 def __rxor__(self, other: object) -> Integer: 

793 result = int.__rxor__(self, other) # type: ignore[operator] 

794 if result is NotImplemented: 

795 return result # type: ignore[return-value] 

796 return self._new(result) 

797 

798 def __sub__(self, other: object) -> Integer: 

799 result = int.__sub__(self, other) # type: ignore[operator] 

800 if result is NotImplemented: 

801 return result # type: ignore[return-value] 

802 return self._new(result) 

803 

804 def __rsub__(self, other: object) -> Integer: 

805 result = int.__rsub__(self, other) # type: ignore[operator] 

806 if result is NotImplemented: 

807 return result # type: ignore[return-value] 

808 return self._new(result) 

809 

810 def __trunc__(self) -> Integer: 

811 return self._new(int.__trunc__(self)) 

812 

813 def __xor__(self, other: object) -> Integer: 

814 result = int.__xor__(self, other) # type: ignore[operator] 

815 if result is NotImplemented: 

816 return result # type: ignore[return-value] 

817 return self._new(result) 

818 

819 def __rtruediv__(self, other: object) -> Float: 

820 result = int.__rtruediv__(self, other) # type: ignore[operator] 

821 if result is NotImplemented: 

822 return result # type: ignore[return-value] 

823 return Float._new(self, result) # type: ignore[arg-type] 

824 

825 def __truediv__(self, other: object) -> Float: 

826 result = int.__truediv__(self, other) # type: ignore[operator] 

827 if result is NotImplemented: 

828 return result # type: ignore[return-value] 

829 return Float._new(self, result) # type: ignore[arg-type] 

830 

831 

832class Float(Item, _CustomFloat): 

833 """ 

834 A float literal. 

835 """ 

836 

837 def __new__(cls, value: float, trivia: Trivia, raw: str) -> Float: 

838 return float.__new__(cls, value) 

839 

840 def __init__(self, value: float, trivia: Trivia, raw: str) -> None: 

841 super().__init__(trivia) 

842 self._original = value 

843 self._raw = raw 

844 self._sign = False 

845 

846 if re.match(r"^[+\-].+$", raw): 

847 self._sign = True 

848 

849 def unwrap(self) -> float: 

850 return self._original 

851 

852 __float__ = unwrap 

853 

854 def __hash__(self) -> int: 

855 return hash(self.unwrap()) 

856 

857 @property 

858 def discriminant(self) -> int: 

859 return 3 

860 

861 @property 

862 def value(self) -> float: 

863 """The wrapped float value""" 

864 return self 

865 

866 def as_string(self) -> str: 

867 return self._raw 

868 

869 def _new(self, result: float) -> Float: 

870 raw = str(result) 

871 

872 if self._sign and result >= 0: 

873 raw = f"+{raw}" 

874 

875 return Float(result, self._trivia, raw) 

876 

877 def _getstate(self, protocol: int = 3) -> tuple[float, Trivia, str]: 

878 return float(self), self._trivia, self._raw 

879 

880 # float methods — explicit typed wrappers 

881 def __abs__(self) -> Float: 

882 return self._new(float.__abs__(self)) 

883 

884 def __add__(self, other: object) -> Float: 

885 result = float.__add__(self, other) # type: ignore[operator] 

886 if result is NotImplemented: 

887 return result # type: ignore[return-value] 

888 return self._new(result) 

889 

890 __eq__ = float.__eq__ 

891 

892 def __floordiv__(self, other: object) -> Float: 

893 result = float.__floordiv__(self, other) # type: ignore[operator] 

894 if result is NotImplemented: 

895 return result # type: ignore[return-value] 

896 return self._new(result) 

897 

898 __le__ = float.__le__ 

899 __lt__ = float.__lt__ 

900 

901 def __mod__(self, other: object) -> Float: 

902 result = float.__mod__(self, other) # type: ignore[operator] 

903 if result is NotImplemented: 

904 return result # type: ignore[return-value] 

905 return self._new(result) 

906 

907 def __mul__(self, other: object) -> Float: 

908 result = float.__mul__(self, other) # type: ignore[operator] 

909 if result is NotImplemented: 

910 return result # type: ignore[return-value] 

911 return self._new(result) 

912 

913 def __neg__(self) -> Float: 

914 return self._new(float.__neg__(self)) 

915 

916 def __pos__(self) -> Float: 

917 return self._new(float.__pos__(self)) 

918 

919 def __pow__(self, other: object, mod: None = None) -> Float: 

920 result = float.__pow__(self, other) # type: ignore[operator] 

921 if result is NotImplemented: 

922 return result # type: ignore[no-any-return] 

923 return self._new(result) 

924 

925 def __radd__(self, other: object) -> Float: 

926 result = float.__radd__(self, other) # type: ignore[operator] 

927 if result is NotImplemented: 

928 return result # type: ignore[return-value] 

929 return self._new(result) 

930 

931 def __rfloordiv__(self, other: object) -> Float: 

932 result = float.__rfloordiv__(self, other) # type: ignore[operator] 

933 if result is NotImplemented: 

934 return result # type: ignore[return-value] 

935 return self._new(result) 

936 

937 def __rmod__(self, other: object) -> Float: 

938 result = float.__rmod__(self, other) # type: ignore[operator] 

939 if result is NotImplemented: 

940 return result # type: ignore[return-value] 

941 return self._new(result) 

942 

943 def __rmul__(self, other: object) -> Float: 

944 result = float.__rmul__(self, other) # type: ignore[operator] 

945 if result is NotImplemented: 

946 return result # type: ignore[return-value] 

947 return self._new(result) 

948 

949 def __round__(self, ndigits: int = 0) -> Float: # type: ignore[override] 

950 return self._new(float.__round__(self, ndigits)) 

951 

952 def __rpow__(self, other: object, mod: None = None) -> Float: 

953 result = float.__rpow__(self, other) # type: ignore[operator] 

954 if result is NotImplemented: 

955 return result # type: ignore[no-any-return] 

956 return self._new(result) 

957 

958 def __rtruediv__(self, other: object) -> Float: 

959 result = float.__rtruediv__(self, other) # type: ignore[operator] 

960 if result is NotImplemented: 

961 return result # type: ignore[return-value] 

962 return self._new(result) 

963 

964 def __truediv__(self, other: object) -> Float: 

965 result = float.__truediv__(self, other) # type: ignore[operator] 

966 if result is NotImplemented: 

967 return result # type: ignore[return-value] 

968 return self._new(result) 

969 

970 def __sub__(self, other: object) -> Float: 

971 result = float.__sub__(self, other) # type: ignore[operator] 

972 if result is NotImplemented: 

973 return result # type: ignore[return-value] 

974 return self._new(result) 

975 

976 def __rsub__(self, other: object) -> Float: 

977 result = float.__rsub__(self, other) # type: ignore[operator] 

978 if result is NotImplemented: 

979 return result # type: ignore[return-value] 

980 return self._new(result) 

981 

982 __trunc__ = float.__trunc__ 

983 __ceil__ = float.__ceil__ 

984 __floor__ = float.__floor__ 

985 

986 

987class Bool(Item): 

988 """ 

989 A boolean literal. 

990 """ 

991 

992 def __init__(self, t: int | BoolType, trivia: Trivia) -> None: 

993 super().__init__(trivia) 

994 

995 self._value = bool(t) 

996 

997 def unwrap(self) -> bool: 

998 return bool(self) 

999 

1000 @property 

1001 def discriminant(self) -> int: 

1002 return 4 

1003 

1004 @property 

1005 def value(self) -> bool: 

1006 """The wrapped boolean value""" 

1007 return self._value 

1008 

1009 def as_string(self) -> str: 

1010 return str(self._value).lower() 

1011 

1012 def _getstate(self, protocol: int = 3) -> tuple[bool, Trivia]: 

1013 return self._value, self._trivia 

1014 

1015 def __bool__(self) -> bool: 

1016 return self._value 

1017 

1018 __nonzero__ = __bool__ 

1019 

1020 def __eq__(self, other: object) -> bool: 

1021 if not isinstance(other, bool): 

1022 return NotImplemented 

1023 

1024 return other == self._value 

1025 

1026 def __hash__(self) -> int: 

1027 return hash(self._value) 

1028 

1029 def __repr__(self) -> str: 

1030 return repr(self._value) 

1031 

1032 

1033class DateTime(Item, datetime): 

1034 """ 

1035 A datetime literal. 

1036 """ 

1037 

1038 def __new__( 

1039 cls, 

1040 year: int, 

1041 month: int, 

1042 day: int, 

1043 hour: int, 

1044 minute: int, 

1045 second: int, 

1046 microsecond: int, 

1047 tzinfo: tzinfo | None, 

1048 trivia: Trivia | None = None, 

1049 raw: str | None = None, 

1050 **kwargs: object, 

1051 ) -> DateTime: 

1052 return datetime.__new__( 

1053 cls, 

1054 year, 

1055 month, 

1056 day, 

1057 hour, 

1058 minute, 

1059 second, 

1060 microsecond, 

1061 tzinfo=tzinfo, 

1062 ) 

1063 

1064 def __init__( 

1065 self, 

1066 year: int, 

1067 month: int, 

1068 day: int, 

1069 hour: int, 

1070 minute: int, 

1071 second: int, 

1072 microsecond: int, 

1073 tzinfo: tzinfo | None, 

1074 trivia: Trivia | None = None, 

1075 raw: str | None = None, 

1076 **kwargs: object, 

1077 ) -> None: 

1078 super().__init__(trivia or Trivia()) 

1079 

1080 self._raw = raw or self.isoformat() 

1081 

1082 def unwrap(self) -> datetime: 

1083 ( 

1084 year, 

1085 month, 

1086 day, 

1087 hour, 

1088 minute, 

1089 second, 

1090 microsecond, 

1091 tzinfo, 

1092 _, 

1093 _, 

1094 ) = self._getstate() 

1095 return datetime(year, month, day, hour, minute, second, microsecond, tzinfo) 

1096 

1097 @property 

1098 def discriminant(self) -> int: 

1099 return 5 

1100 

1101 @property 

1102 def value(self) -> datetime: 

1103 return self 

1104 

1105 def as_string(self) -> str: 

1106 return self._raw 

1107 

1108 def __add__(self, other: timedelta) -> DateTime: 

1109 if PY38: 

1110 result = datetime( 

1111 self.year, 

1112 self.month, 

1113 self.day, 

1114 self.hour, 

1115 self.minute, 

1116 self.second, 

1117 self.microsecond, 

1118 self.tzinfo, 

1119 ).__add__(other) 

1120 else: 

1121 result = super().__add__(other) 

1122 

1123 return self._new(result) 

1124 

1125 @overload # type: ignore[override] 

1126 def __sub__(self, other: timedelta) -> DateTime: ... 

1127 

1128 @overload 

1129 def __sub__(self, other: datetime) -> timedelta: ... 

1130 

1131 def __sub__(self, other: timedelta | datetime) -> DateTime | timedelta: 

1132 if PY38: 

1133 result = datetime( 

1134 self.year, 

1135 self.month, 

1136 self.day, 

1137 self.hour, 

1138 self.minute, 

1139 self.second, 

1140 self.microsecond, 

1141 self.tzinfo, 

1142 ).__sub__(other) 

1143 else: 

1144 result = super().__sub__(other) # type: ignore[operator] 

1145 

1146 if isinstance(result, datetime): 

1147 result = self._new(result) 

1148 

1149 return result 

1150 

1151 def replace(self, *args: object, **kwargs: object) -> DateTime: 

1152 return self._new(super().replace(*args, **kwargs)) # type: ignore[arg-type] 

1153 

1154 def astimezone(self, tz: tzinfo) -> DateTime: # type: ignore[override] 

1155 result = super().astimezone(tz) 

1156 if PY38: 

1157 return result 

1158 return self._new(result) 

1159 

1160 def _new(self, result: datetime) -> DateTime: 

1161 raw = result.isoformat() 

1162 

1163 return DateTime( 

1164 result.year, 

1165 result.month, 

1166 result.day, 

1167 result.hour, 

1168 result.minute, 

1169 result.second, 

1170 result.microsecond, 

1171 result.tzinfo, 

1172 self._trivia, 

1173 raw, 

1174 ) 

1175 

1176 def _getstate( 

1177 self, protocol: int = 3 

1178 ) -> tuple[int, int, int, int, int, int, int, tzinfo | None, Trivia, str]: 

1179 return ( 

1180 self.year, 

1181 self.month, 

1182 self.day, 

1183 self.hour, 

1184 self.minute, 

1185 self.second, 

1186 self.microsecond, 

1187 self.tzinfo, 

1188 self._trivia, 

1189 self._raw, 

1190 ) 

1191 

1192 

1193class Date(Item, date): 

1194 """ 

1195 A date literal. 

1196 """ 

1197 

1198 def __new__( 

1199 cls, 

1200 year: int, 

1201 month: int, 

1202 day: int, 

1203 trivia: Trivia | None = None, 

1204 raw: str = "", 

1205 ) -> Date: 

1206 return date.__new__(cls, year, month, day) 

1207 

1208 def __init__( 

1209 self, 

1210 year: int, 

1211 month: int, 

1212 day: int, 

1213 trivia: Trivia | None = None, 

1214 raw: str = "", 

1215 ) -> None: 

1216 super().__init__(trivia or Trivia()) 

1217 

1218 self._raw = raw 

1219 

1220 def unwrap(self) -> date: 

1221 (year, month, day, _, _) = self._getstate() 

1222 return date(year, month, day) 

1223 

1224 @property 

1225 def discriminant(self) -> int: 

1226 return 6 

1227 

1228 @property 

1229 def value(self) -> date: 

1230 return self 

1231 

1232 def as_string(self) -> str: 

1233 return self._raw 

1234 

1235 def __add__(self, other: timedelta) -> Date: 

1236 if PY38: 

1237 result = date(self.year, self.month, self.day).__add__(other) 

1238 else: 

1239 result = super().__add__(other) 

1240 

1241 return self._new(result) 

1242 

1243 @overload # type: ignore[override] 

1244 def __sub__(self, other: timedelta) -> Date: ... 

1245 

1246 @overload 

1247 def __sub__(self, other: date) -> timedelta: ... 

1248 

1249 def __sub__(self, other: timedelta | date) -> Date | timedelta: 

1250 if PY38: 

1251 result = date(self.year, self.month, self.day).__sub__(other) 

1252 else: 

1253 result = super().__sub__(other) # type: ignore[operator] 

1254 

1255 if isinstance(result, date): 

1256 result = self._new(result) 

1257 

1258 return result 

1259 

1260 def replace(self, *args: object, **kwargs: object) -> Date: 

1261 return self._new(super().replace(*args, **kwargs)) # type: ignore[arg-type] 

1262 

1263 def _new(self, result: date) -> Date: 

1264 raw = result.isoformat() 

1265 

1266 return Date(result.year, result.month, result.day, self._trivia, raw) 

1267 

1268 def _getstate(self, protocol: int = 3) -> tuple[int, int, int, Trivia, str]: 

1269 return (self.year, self.month, self.day, self._trivia, self._raw) 

1270 

1271 

1272class Time(Item, time): 

1273 """ 

1274 A time literal. 

1275 """ 

1276 

1277 def __new__( 

1278 cls, 

1279 hour: int, 

1280 minute: int, 

1281 second: int, 

1282 microsecond: int, 

1283 tzinfo: tzinfo | None, 

1284 trivia: Trivia | None = None, 

1285 raw: str = "", 

1286 ) -> Time: 

1287 return time.__new__(cls, hour, minute, second, microsecond, tzinfo) 

1288 

1289 def __init__( 

1290 self, 

1291 hour: int, 

1292 minute: int, 

1293 second: int, 

1294 microsecond: int, 

1295 tzinfo: tzinfo | None, 

1296 trivia: Trivia | None = None, 

1297 raw: str = "", 

1298 ) -> None: 

1299 super().__init__(trivia or Trivia()) 

1300 

1301 self._raw = raw 

1302 

1303 def unwrap(self) -> time: 

1304 (hour, minute, second, microsecond, tzinfo, _, _) = self._getstate() 

1305 return time(hour, minute, second, microsecond, tzinfo) 

1306 

1307 @property 

1308 def discriminant(self) -> int: 

1309 return 7 

1310 

1311 @property 

1312 def value(self) -> time: 

1313 return self 

1314 

1315 def as_string(self) -> str: 

1316 return self._raw 

1317 

1318 def replace(self, *args: object, **kwargs: object) -> Time: 

1319 return self._new(super().replace(*args, **kwargs)) # type: ignore[arg-type] 

1320 

1321 def _new(self, result: time) -> Time: 

1322 raw = result.isoformat() 

1323 

1324 return Time( 

1325 result.hour, 

1326 result.minute, 

1327 result.second, 

1328 result.microsecond, 

1329 result.tzinfo, 

1330 self._trivia, 

1331 raw, 

1332 ) 

1333 

1334 def _getstate( 

1335 self, protocol: int = 3 

1336 ) -> tuple[int, int, int, int, tzinfo | None, Trivia, str]: 

1337 return ( 

1338 self.hour, 

1339 self.minute, 

1340 self.second, 

1341 self.microsecond, 

1342 self.tzinfo, 

1343 self._trivia, 

1344 self._raw, 

1345 ) 

1346 

1347 

1348class _ArrayItemGroup: 

1349 __slots__ = ("comma", "comment", "indent", "value") 

1350 

1351 def __init__( 

1352 self, 

1353 value: Item | None = None, 

1354 indent: Whitespace | None = None, 

1355 comma: Whitespace | None = None, 

1356 comment: Comment | None = None, 

1357 ) -> None: 

1358 self.value = value 

1359 self.indent = indent 

1360 self.comma = comma 

1361 self.comment = comment 

1362 

1363 def __iter__(self) -> Iterator[Item]: 

1364 return ( 

1365 x 

1366 for x in (self.indent, self.value, self.comma, self.comment) 

1367 if x is not None 

1368 ) 

1369 

1370 def __repr__(self) -> str: 

1371 return repr(tuple(self)) 

1372 

1373 def is_whitespace(self) -> bool: 

1374 return self.value is None and self.comment is None 

1375 

1376 def __bool__(self) -> bool: 

1377 try: 

1378 next(iter(self)) 

1379 except StopIteration: 

1380 return False 

1381 return True 

1382 

1383 

1384class Array(Item, _CustomList): # type: ignore[type-arg] 

1385 """ 

1386 An array literal 

1387 """ 

1388 

1389 def __init__( 

1390 self, value: list[Item], trivia: Trivia, multiline: bool = False 

1391 ) -> None: 

1392 super().__init__(trivia) 

1393 list.__init__( 

1394 self, 

1395 [v for v in value if not isinstance(v, (Whitespace, Comment, Null))], 

1396 ) 

1397 self._index_map: dict[int, int] = {} 

1398 self._value = self._group_values(value) 

1399 self._multiline = multiline 

1400 self._reindex() 

1401 

1402 def _group_values(self, value: list[Item]) -> list[_ArrayItemGroup]: 

1403 """Group the values into (indent, value, comma, comment) tuples""" 

1404 groups = [] 

1405 this_group = _ArrayItemGroup() 

1406 start_new_group = False 

1407 for item in value: 

1408 if isinstance(item, Whitespace): 

1409 if "," not in item.s or start_new_group: 

1410 groups.append(this_group) 

1411 this_group = _ArrayItemGroup(indent=item) 

1412 start_new_group = False 

1413 else: 

1414 if this_group.value is None: 

1415 # when comma is met and no value is provided, add a dummy Null 

1416 this_group.value = Null() 

1417 this_group.comma = item 

1418 elif isinstance(item, Comment): 

1419 if this_group.value is None: 

1420 this_group.value = Null() 

1421 this_group.comment = item 

1422 # Comments are the last item in a group. 

1423 start_new_group = True 

1424 elif this_group.value is None: 

1425 this_group.value = item 

1426 else: 

1427 groups.append(this_group) 

1428 this_group = _ArrayItemGroup(value=item) 

1429 groups.append(this_group) 

1430 return [group for group in groups if group] 

1431 

1432 def unwrap(self) -> list[Any]: 

1433 unwrapped = [] 

1434 for v in self: 

1435 if hasattr(v, "unwrap"): 

1436 unwrapped.append(v.unwrap()) 

1437 else: 

1438 unwrapped.append(v) 

1439 return unwrapped 

1440 

1441 @property 

1442 def discriminant(self) -> int: 

1443 return 8 

1444 

1445 @property 

1446 def value(self) -> list[Item]: 

1447 return self 

1448 

1449 def _iter_items(self) -> Iterator[Item]: 

1450 for v in self._value: 

1451 yield from v 

1452 

1453 def multiline(self, multiline: bool) -> Array: 

1454 """Change the array to display in multiline or not. 

1455 

1456 :Example: 

1457 

1458 >>> a = item([1, 2, 3]) 

1459 >>> print(a.as_string()) 

1460 [1, 2, 3] 

1461 >>> print(a.multiline(True).as_string()) 

1462 [ 

1463 1, 

1464 2, 

1465 3, 

1466 ] 

1467 """ 

1468 self._multiline = multiline 

1469 

1470 return self 

1471 

1472 def as_string(self) -> str: 

1473 if not self._multiline or not self._value: 

1474 return f"[{''.join(v.as_string() for v in self._iter_items())}]" 

1475 

1476 s = "[\n" 

1477 s += "".join( 

1478 self.trivia.indent 

1479 + " " * 4 

1480 + v.value.as_string() 

1481 + ("," if not isinstance(v.value, Null) else "") 

1482 + (v.comment.as_string() if v.comment is not None else "") 

1483 + "\n" 

1484 for v in self._value 

1485 if v.value is not None 

1486 ) 

1487 s += self.trivia.indent + "]" 

1488 

1489 return s 

1490 

1491 def _reindex(self) -> None: 

1492 self._index_map.clear() 

1493 index = 0 

1494 for i, v in enumerate(self._value): 

1495 if v.value is None or isinstance(v.value, Null): 

1496 continue 

1497 self._index_map[index] = i 

1498 index += 1 

1499 

1500 def add_line( 

1501 self, 

1502 *items: Any, 

1503 indent: str = " ", 

1504 comment: str | None = None, 

1505 add_comma: bool = True, 

1506 newline: bool = True, 

1507 ) -> None: 

1508 """Add multiple items in a line to control the format precisely. 

1509 When add_comma is True, only accept actual values and 

1510 ", " will be added between values automatically. 

1511 

1512 :Example: 

1513 

1514 >>> a = array() 

1515 >>> a.add_line(1, 2, 3) 

1516 >>> a.add_line(4, 5, 6) 

1517 >>> a.add_line(indent="") 

1518 >>> print(a.as_string()) 

1519 [ 

1520 1, 2, 3, 

1521 4, 5, 6, 

1522 ] 

1523 """ 

1524 new_values: list[Item] = [] 

1525 first_indent = f"\n{indent}" if newline else indent 

1526 if first_indent: 

1527 new_values.append(Whitespace(first_indent)) 

1528 whitespace = "" 

1529 data_values = [] 

1530 for i, el in enumerate(items): 

1531 it = item(el, _parent=self) 

1532 if isinstance(it, Comment) or (add_comma and isinstance(el, Whitespace)): 

1533 raise ValueError(f"item type {type(it)} is not allowed in add_line") 

1534 if not isinstance(it, Whitespace): 

1535 if whitespace: 

1536 new_values.append(Whitespace(whitespace)) 

1537 whitespace = "" 

1538 new_values.append(it) 

1539 data_values.append(it.value) 

1540 if add_comma: 

1541 new_values.append(Whitespace(",")) 

1542 if i != len(items) - 1: 

1543 new_values.append(Whitespace(" ")) 

1544 elif "," not in it.s: 

1545 whitespace += it.s 

1546 else: 

1547 new_values.append(it) 

1548 if whitespace: 

1549 new_values.append(Whitespace(whitespace)) 

1550 if comment: 

1551 indent = " " if items else "" 

1552 new_values.append( 

1553 Comment(Trivia(indent=indent, comment=f"# {comment}", trail="")) 

1554 ) 

1555 list.extend(self, data_values) 

1556 if len(self._value) > 0: 

1557 last_item = self._value[-1] 

1558 last_value_item = next( 

1559 ( 

1560 v 

1561 for v in self._value[::-1] 

1562 if v.value is not None and not isinstance(v.value, Null) 

1563 ), 

1564 None, 

1565 ) 

1566 if last_value_item is not None: 

1567 last_value_item.comma = Whitespace(",") 

1568 if last_item.is_whitespace(): 

1569 self._value[-1:-1] = self._group_values(new_values) 

1570 else: 

1571 self._value.extend(self._group_values(new_values)) 

1572 else: 

1573 self._value.extend(self._group_values(new_values)) 

1574 self._reindex() 

1575 

1576 def clear(self) -> None: 

1577 """Clear the array.""" 

1578 list.clear(self) 

1579 self._index_map.clear() 

1580 self._value.clear() 

1581 

1582 def __len__(self) -> int: 

1583 return list.__len__(self) 

1584 

1585 def item(self, index: int) -> Item: 

1586 return list.__getitem__(self, index) # type: ignore[no-any-return] 

1587 

1588 def __getitem__(self, key: int | slice) -> Any: # type: ignore[override] 

1589 return list.__getitem__(self, key) 

1590 

1591 def __setitem__(self, key: int | slice, value: Any) -> None: # type: ignore[override] 

1592 it = item(value, _parent=self) 

1593 list.__setitem__(self, key, it) 

1594 if isinstance(key, slice): 

1595 raise ValueError("slice assignment is not supported") 

1596 if key < 0: 

1597 key += len(self) 

1598 self._value[self._index_map[key]].value = it 

1599 

1600 def insert(self, pos: int, value: Any) -> None: # type: ignore[override] 

1601 it = item(value, _parent=self) 

1602 length = len(self) 

1603 if not isinstance(it, (Comment, Whitespace)): 

1604 list.insert(self, pos, it) 

1605 if pos < 0: 

1606 pos += length 

1607 if pos < 0: 

1608 pos = 0 

1609 

1610 idx = 0 # insert position of the self._value list 

1611 default_indent = " " 

1612 if pos < length: 

1613 try: 

1614 idx = self._index_map[pos] 

1615 except KeyError as e: 

1616 raise IndexError("list index out of range") from e 

1617 else: 

1618 idx = len(self._value) 

1619 if idx >= 1 and self._value[idx - 1].is_whitespace(): 

1620 # The last item is a pure whitespace(\n ), insert before it 

1621 idx -= 1 

1622 _indent = self._value[idx].indent 

1623 if _indent is not None and "\n" in _indent.s: 

1624 default_indent = "\n " 

1625 indent: Whitespace | None = None 

1626 comma: Whitespace | None = Whitespace(",") if pos < length else None 

1627 if idx < len(self._value) and not self._value[idx].is_whitespace(): 

1628 # Prefer to copy the indentation from the item after 

1629 indent = self._value[idx].indent 

1630 if idx > 0: 

1631 last_item = self._value[idx - 1] 

1632 if indent is None: 

1633 indent = last_item.indent 

1634 if not isinstance(last_item.value, Null) and "\n" in default_indent: 

1635 # Copy the comma from the last item if 1) it contains a value and 

1636 # 2) the array is multiline 

1637 comma = last_item.comma 

1638 comma_in_indent = indent is not None and "," in indent.s 

1639 if ( 

1640 last_item.comma is None 

1641 and not isinstance(last_item.value, Null) 

1642 and not comma_in_indent 

1643 ): 

1644 # Add comma to the last item to separate it from the following 

1645 # items, unless the new item's indent already starts with a 

1646 # comma (comma-first style). 

1647 last_item.comma = Whitespace(",") 

1648 if indent is not None and "," in indent.s: 

1649 # Comma-first style: the item that will follow the new one brings 

1650 # its own leading comma, so the new item must not add a trailing 

1651 # comma of its own. 

1652 comma = None 

1653 if indent is None and (idx > 0 or "\n" in default_indent): 

1654 # apply default indent if it isn't the first item or the array is multiline. 

1655 indent = Whitespace(default_indent) 

1656 new_item = _ArrayItemGroup(value=it, indent=indent, comma=comma) 

1657 self._value.insert(idx, new_item) 

1658 self._reindex() 

1659 

1660 def __delitem__(self, key: int | slice) -> None: # type: ignore[override] 

1661 length = len(self) 

1662 list.__delitem__(self, key) 

1663 

1664 if isinstance(key, slice): 

1665 indices_to_remove = list( 

1666 range(key.start or 0, key.stop or length, key.step or 1) 

1667 ) 

1668 else: 

1669 indices_to_remove = [length + key if key < 0 else key] 

1670 for i in sorted(indices_to_remove, reverse=True): 

1671 try: 

1672 idx = self._index_map[i] 

1673 except KeyError as e: 

1674 if not isinstance(key, slice): 

1675 raise IndexError("list index out of range") from e 

1676 else: 

1677 group_rm = self._value[idx] 

1678 del self._value[idx] 

1679 if ( 

1680 idx == 0 

1681 and len(self._value) > 0 

1682 and (ind := self._value[idx].indent) 

1683 and "\n" not in ind.s 

1684 ): 

1685 # Remove the indentation of the first item if not newline 

1686 self._value[idx].indent = None 

1687 comma_in_indent = ( 

1688 group_rm.indent is not None and "," in group_rm.indent.s 

1689 ) 

1690 comma_in_comma = group_rm.comma is not None and "," in group_rm.comma.s 

1691 if comma_in_indent and comma_in_comma: 

1692 # Removed group had both commas. Add one to the next group. 

1693 group = self._value[idx] if len(self._value) > idx else None 

1694 if group is not None: 

1695 if group.indent is None: 

1696 group.indent = Whitespace(",") 

1697 elif "," not in group.indent.s: 

1698 # Insert the comma after the newline 

1699 try: 

1700 newline_index = group.indent.s.index("\n") 

1701 group.indent._s = ( 

1702 group.indent.s[: newline_index + 1] 

1703 + "," 

1704 + group.indent.s[newline_index + 1 :] 

1705 ) 

1706 except ValueError: 

1707 group.indent._s = "," + group.indent.s 

1708 elif not comma_in_indent and not comma_in_comma: 

1709 # Removed group had no commas. Remove the next comma found. 

1710 for j in range(idx, len(self._value)): 

1711 group = self._value[j] 

1712 if group.indent is not None and "," in group.indent.s: 

1713 group.indent._s = group.indent.s.replace(",", "", 1) 

1714 break 

1715 if group_rm.indent is not None and "\n" in group_rm.indent.s: 

1716 # Restore the removed group's newline onto the next group 

1717 # if the next group does not have a newline. 

1718 # i.e. the two were on the same line 

1719 group = self._value[idx] if len(self._value) > idx else None 

1720 if group is not None and ( 

1721 group.indent is None or "\n" not in group.indent.s 

1722 ): 

1723 group.indent = group_rm.indent 

1724 

1725 if len(self._value) > 0: 

1726 v = self._value[-1] 

1727 if not v.is_whitespace(): 

1728 # remove the comma of the last item 

1729 v.comma = None 

1730 

1731 self._reindex() 

1732 

1733 def _getstate(self, protocol: int = 3) -> tuple[list[Item], Trivia, bool]: 

1734 return list(self._iter_items()), self._trivia, self._multiline 

1735 

1736 

1737class AbstractTable(Item, _CustomDict): # type: ignore[type-arg] 

1738 """Common behaviour of both :class:`Table` and :class:`InlineTable`""" 

1739 

1740 def __init__(self, value: container.Container, trivia: Trivia): 

1741 Item.__init__(self, trivia) 

1742 

1743 self._value = value 

1744 

1745 for k, v in self._value.body: 

1746 if k is not None: 

1747 dict.__setitem__(self, k.key, v) 

1748 

1749 def unwrap(self) -> dict[str, Any]: 

1750 unwrapped = {} 

1751 for k, v in self.items(): 

1752 if isinstance(k, Key): 

1753 k = k.key 

1754 if hasattr(v, "unwrap"): 

1755 v = v.unwrap() 

1756 unwrapped[k] = v 

1757 

1758 return unwrapped 

1759 

1760 @property 

1761 def value(self) -> container.Container: 

1762 return self._value 

1763 

1764 @overload 

1765 def append(self: AT, key: None, value: Comment | Whitespace) -> AT: ... 

1766 

1767 @overload 

1768 def append(self: AT, key: Key | str, value: Any) -> AT: ... 

1769 

1770 def append(self: AT, key: Key | str | None, value: Any) -> AT: 

1771 raise NotImplementedError 

1772 

1773 @overload 

1774 def add(self: AT, key: Comment | Whitespace) -> AT: ... 

1775 

1776 @overload 

1777 def add(self: AT, key: Key | str, value: Any = ...) -> AT: ... 

1778 

1779 def add( 

1780 self: AT, key: Key | str | Comment | Whitespace, value: Any | None = None 

1781 ) -> AT: 

1782 if value is None: 

1783 if not isinstance(key, (Comment, Whitespace)): 

1784 msg = "Non comment/whitespace items must have an associated key" 

1785 raise ValueError(msg) 

1786 

1787 return self.append(None, key) 

1788 

1789 if isinstance(key, (Comment, Whitespace)): 

1790 raise ValueError("Comment/Whitespace keys must not have a value") 

1791 

1792 return self.append(key, value) 

1793 

1794 def remove(self: AT, key: Key | str) -> AT: 

1795 self._value.remove(key) 

1796 

1797 if isinstance(key, Key): 

1798 key = key.key 

1799 

1800 if key is not None: 

1801 dict.__delitem__(self, key) 

1802 

1803 return self 

1804 

1805 def item(self, key: Key | str) -> Item | OutOfOrderTableProxy: 

1806 return self._value.item(key) 

1807 

1808 def setdefault(self, key: Key | str, default: Any) -> Any: # type: ignore[override] 

1809 super().setdefault(key, default) 

1810 return self[key] 

1811 

1812 def __str__(self) -> str: 

1813 return str(self.value) 

1814 

1815 def copy(self: AT) -> AT: 

1816 return copy.copy(self) 

1817 

1818 def __repr__(self) -> str: 

1819 return repr(self.value) 

1820 

1821 def __iter__(self) -> Iterator[str]: 

1822 return iter(self._value) 

1823 

1824 def __len__(self) -> int: 

1825 return len(self._value) 

1826 

1827 def __delitem__(self, key: Key | str) -> None: # type: ignore[override] 

1828 self.remove(key) 

1829 

1830 def __getitem__(self, key: Key | str) -> Any: # type: ignore[override] 

1831 return self._value[key] 

1832 

1833 def __contains__(self, key: object) -> bool: 

1834 # Native membership test. The inherited ``MutableMapping.__contains__`` 

1835 # resolves the value via ``__getitem__`` (``self._value[key]``) -- and 

1836 # builds a ``NonExistentKey`` on every absent key -- only to discard it. 

1837 # Delegate straight to the inner container instead: its ``__contains__`` 

1838 # answers from ``_map`` while still building the ``OutOfOrderTableProxy`` 

1839 # for an out-of-order entry, so validation runs exactly as before. 

1840 return key in self._value 

1841 

1842 def __setitem__(self, key: Key | str, value: Any) -> None: # type: ignore[override] 

1843 if not isinstance(value, Item): 

1844 value = item(value, _parent=self) 

1845 

1846 is_replace = key in self 

1847 self._value[key] = value 

1848 

1849 if key is not None: 

1850 dict.__setitem__(self, key, value) 

1851 

1852 if is_replace: 

1853 return 

1854 m = re.match("(?s)^[^ ]*([ ]+).*$", self._trivia.indent) 

1855 if not m: 

1856 return 

1857 

1858 indent = m.group(1) 

1859 

1860 if not isinstance(value, Whitespace): 

1861 m = re.match("(?s)^([^ ]*)(.*)$", value.trivia.indent) 

1862 if not m: 

1863 value.trivia.indent = indent 

1864 else: 

1865 value.trivia.indent = m.group(1) + indent + m.group(2) 

1866 

1867 

1868class Table(AbstractTable): 

1869 """ 

1870 A table literal. 

1871 """ 

1872 

1873 def __init__( 

1874 self, 

1875 value: container.Container, 

1876 trivia: Trivia, 

1877 is_aot_element: bool, 

1878 is_super_table: bool | None = None, 

1879 name: str | None = None, 

1880 display_name: str | None = None, 

1881 ) -> None: 

1882 super().__init__(value, trivia) 

1883 

1884 self.name = name 

1885 self.display_name = display_name 

1886 self._is_aot_element = is_aot_element 

1887 self._is_super_table = is_super_table 

1888 

1889 @property 

1890 def discriminant(self) -> int: 

1891 return 9 

1892 

1893 def __copy__(self) -> Table: 

1894 return type(self)( 

1895 self._value.copy(), 

1896 self._trivia.copy(), 

1897 self._is_aot_element, 

1898 self._is_super_table, 

1899 self.name, 

1900 self.display_name, 

1901 ) 

1902 

1903 def append(self, key: Key | str | None, _item: Any) -> Table: 

1904 """ 

1905 Appends a (key, item) to the table. 

1906 """ 

1907 if not isinstance(_item, Item): 

1908 _item = item(_item, _parent=self) 

1909 

1910 self._value.append(key, _item) 

1911 

1912 if isinstance(key, Key): 

1913 key = next(iter(key)).key 

1914 _item = self._value[key] 

1915 

1916 if key is not None: 

1917 dict.__setitem__(self, key, _item) 

1918 

1919 m = re.match(r"(?s)^[^ ]*([ ]+).*$", self._trivia.indent) 

1920 if not m: 

1921 return self 

1922 

1923 indent = m.group(1) 

1924 

1925 if not isinstance(_item, Whitespace): 

1926 m = re.match("(?s)^([^ ]*)(.*)$", _item.trivia.indent) 

1927 if not m: 

1928 _item.trivia.indent = indent 

1929 else: 

1930 _item.trivia.indent = m.group(1) + indent + m.group(2) 

1931 

1932 return self 

1933 

1934 def raw_append(self, key: Key | str | None, _item: Any) -> Table: 

1935 """Similar to :meth:`append` but does not copy indentation.""" 

1936 if not isinstance(_item, Item): 

1937 _item = item(_item) 

1938 

1939 self._value.append(key, _item, validate=False) 

1940 

1941 if isinstance(key, Key): 

1942 key = next(iter(key)).key 

1943 _item = self._value[key] 

1944 

1945 if key is not None: 

1946 dict.__setitem__(self, key, _item) 

1947 

1948 return self 

1949 

1950 def is_aot_element(self) -> bool: 

1951 """True if the table is the direct child of an AOT element.""" 

1952 return self._is_aot_element 

1953 

1954 def is_super_table(self) -> bool: 

1955 """A super table is the intermediate parent of a nested table as in [a.b.c]. 

1956 If true, it won't appear in the TOML representation.""" 

1957 if self._is_super_table is not None: 

1958 return self._is_super_table 

1959 if not self: 

1960 return False 

1961 # If the table has children and all children are tables, then it is a super table. 

1962 for k, child in self.items(): 

1963 if not isinstance(k, Key): 

1964 k = SingleKey(k) 

1965 index = self.value._map[k] 

1966 if isinstance(index, tuple): 

1967 return False 

1968 real_key = self.value.body[index][0] 

1969 if ( 

1970 not isinstance(child, (Table, AoT)) 

1971 or real_key is None 

1972 or real_key.is_dotted() 

1973 ): 

1974 return False 

1975 return True 

1976 

1977 def as_string(self) -> str: 

1978 return self._value.as_string() 

1979 

1980 # Helpers 

1981 

1982 def indent(self, indent: int) -> Table: 

1983 """Indent the table with given number of spaces.""" 

1984 super().indent(indent) 

1985 

1986 m = re.match("(?s)^[^ ]*([ ]+).*$", self._trivia.indent) 

1987 if not m: 

1988 indent_str = "" 

1989 else: 

1990 indent_str = m.group(1) 

1991 

1992 for _, item in self._value.body: 

1993 if not isinstance(item, Whitespace): 

1994 item.trivia.indent = indent_str + item.trivia.indent 

1995 

1996 return self 

1997 

1998 def invalidate_display_name(self) -> None: 

1999 """Call ``invalidate_display_name`` on the contained tables""" 

2000 self.display_name = None 

2001 

2002 for child in self.values(): 

2003 if hasattr(child, "invalidate_display_name"): 

2004 child.invalidate_display_name() 

2005 

2006 def _getstate( 

2007 self, protocol: int = 3 

2008 ) -> tuple[container.Container, Trivia, bool, bool | None, str | None, str | None]: 

2009 return ( 

2010 self._value, 

2011 self._trivia, 

2012 self._is_aot_element, 

2013 self._is_super_table, 

2014 self.name, 

2015 self.display_name, 

2016 ) 

2017 

2018 

2019class InlineTable(AbstractTable): 

2020 """ 

2021 An inline table literal. 

2022 """ 

2023 

2024 def __init__( 

2025 self, value: container.Container, trivia: Trivia, new: bool = False 

2026 ) -> None: 

2027 super().__init__(value, trivia) 

2028 

2029 self._new = new 

2030 

2031 @property 

2032 def discriminant(self) -> int: 

2033 return 10 

2034 

2035 def append(self, key: Key | str | None, _item: Any) -> InlineTable: 

2036 """ 

2037 Appends a (key, item) to the table. 

2038 """ 

2039 if not isinstance(_item, Item): 

2040 _item = item(_item, _parent=self) 

2041 

2042 if not isinstance(_item, (Whitespace, Comment)): 

2043 if not _item.trivia.indent and len(self._value) > 0 and not self._new: 

2044 _item.trivia.indent = " " 

2045 if _item.trivia.comment: 

2046 _item.trivia.comment = "" 

2047 

2048 self._value.append(key, _item) 

2049 

2050 if isinstance(key, Key): 

2051 key = key.key 

2052 

2053 if key is not None: 

2054 dict.__setitem__(self, key, _item) 

2055 

2056 return self 

2057 

2058 def as_string(self) -> str: 

2059 buf = "{" 

2060 emitted_key = False 

2061 needs_separator = False 

2062 has_explicit_commas = any( 

2063 k is None and isinstance(v, Whitespace) and "," in v.s 

2064 for k, v in self._value.body 

2065 ) 

2066 last_item_idx = next( 

2067 ( 

2068 i 

2069 for i in range(len(self._value.body) - 1, -1, -1) 

2070 if self._value.body[i][0] is not None 

2071 ), 

2072 None, 

2073 ) 

2074 pending_separator = False 

2075 for i, (k, v) in enumerate(self._value.body): 

2076 if k is None: 

2077 if isinstance(v, Whitespace) and "," in v.s: 

2078 if not emitted_key: 

2079 buf += v.as_string().replace(",", "", 1) 

2080 continue 

2081 

2082 has_following_null = any( 

2083 isinstance(next_v, Null) 

2084 for _, next_v in self._value.body[i + 1 :] 

2085 ) 

2086 has_following_key = any( 

2087 next_k is not None for next_k, _ in self._value.body[i + 1 :] 

2088 ) 

2089 if has_following_null and not has_following_key: 

2090 buf += v.as_string().replace(",", "", 1) 

2091 continue 

2092 

2093 if pending_separator: 

2094 # A previous key was removed, leaving this comma as a 

2095 # duplicate of an already emitted separator. Drop it to 

2096 # avoid producing an invalid ``, ,`` sequence. 

2097 buf += v.as_string().replace(",", "", 1) 

2098 continue 

2099 

2100 pending_separator = True 

2101 

2102 if i == len(self._value.body) - 1: 

2103 if self._new: 

2104 buf = buf.rstrip(", ") 

2105 elif not has_explicit_commas or "," in v.as_string(): 

2106 buf = buf.rstrip(",") 

2107 

2108 v_string = v.as_string() 

2109 buf += v_string 

2110 if "," in v_string: 

2111 needs_separator = False 

2112 

2113 continue 

2114 

2115 if has_explicit_commas and needs_separator: 

2116 stripped = buf.rstrip() 

2117 buf = f"{stripped},{buf[len(stripped) :]}" 

2118 needs_separator = False 

2119 

2120 v_trivia_trail = v.trivia.trail.replace("\n", "") 

2121 if k.is_dotted() and isinstance(v, Table): 

2122 # A table materialized from a dotted key renders one 

2123 # `prefix.child = value` pair per child, so that keys added 

2124 # after parsing stay attached to the dotted prefix. 

2125 rendered = ", ".join(self._render_dotted(k, v)) 

2126 else: 

2127 rendered = ( 

2128 f"{k.as_string() + ('.' if k.is_dotted() else '')}" 

2129 f"{k.sep}" 

2130 f"{v.as_string()}" 

2131 ) 

2132 buf += f"{v.trivia.indent}{rendered}{v.trivia.comment}{v_trivia_trail}" 

2133 emitted_key = True 

2134 pending_separator = False 

2135 if has_explicit_commas: 

2136 needs_separator = True 

2137 

2138 if ( 

2139 not has_explicit_commas 

2140 and last_item_idx is not None 

2141 and i < last_item_idx 

2142 ): 

2143 buf += "," 

2144 if self._new: 

2145 buf += " " 

2146 

2147 buf += "}" 

2148 

2149 return buf 

2150 

2151 def _render_dotted(self, key: Key, table: Table) -> list[str]: 

2152 """Render a table materialized from a dotted key as a list of 

2153 ``prefix.child = value`` strings, recursing into nested dotted 

2154 children.""" 

2155 prefix = f"{key.as_string()}.{key.sep}" 

2156 parts = [] 

2157 for k, v in table.value.body: 

2158 if k is None: 

2159 continue 

2160 if isinstance(v, Table): 

2161 parts.extend(f"{prefix}{sub}" for sub in self._render_dotted(k, v)) 

2162 else: 

2163 trail = v.trivia.trail.replace("\n", "") 

2164 parts.append( 

2165 f"{prefix}{v.trivia.indent}{k.as_string()}{k.sep}{v.as_string()}" 

2166 f"{v.trivia.comment_ws}{v.trivia.comment}{trail}" 

2167 ) 

2168 return parts 

2169 

2170 def __setitem__(self, key: Key | str, value: Any) -> None: # type: ignore[override] 

2171 if hasattr(value, "trivia") and value.trivia.comment: 

2172 value.trivia.comment = "" 

2173 super().__setitem__(key, value) 

2174 

2175 def __copy__(self) -> InlineTable: 

2176 return type(self)(self._value.copy(), self._trivia.copy(), self._new) 

2177 

2178 def _getstate(self, protocol: int = 3) -> tuple[container.Container, Trivia]: 

2179 return (self._value, self._trivia) 

2180 

2181 

2182class String(str, Item): # type: ignore[misc] 

2183 """ 

2184 A string literal. 

2185 """ 

2186 

2187 def __new__( 

2188 cls, t: StringType, value: str, original: str, trivia: Trivia 

2189 ) -> String: 

2190 return super().__new__(cls, value) 

2191 

2192 def __init__(self, t: StringType, _: str, original: str, trivia: Trivia) -> None: 

2193 super().__init__(trivia) 

2194 

2195 self._t = t 

2196 self._original = original 

2197 

2198 def unwrap(self) -> str: 

2199 return str(self) 

2200 

2201 @property 

2202 def discriminant(self) -> int: 

2203 return 11 

2204 

2205 @property 

2206 def value(self) -> str: 

2207 return self 

2208 

2209 def as_string(self) -> str: 

2210 return f"{self._t.value}{decode(self._original)}{self._t.value}" 

2211 

2212 @property 

2213 def type(self) -> StringType: 

2214 return self._t 

2215 

2216 def __add__(self, other: str) -> String: 

2217 result = super().__add__(other) 

2218 original = self._original + getattr(other, "_original", other) 

2219 

2220 return self._new(result, original) 

2221 

2222 def _new(self, result: str, original: str) -> String: 

2223 return String(self._t, result, original, self._trivia) 

2224 

2225 def _getstate(self, protocol: int = 3) -> tuple[StringType, str, str, Trivia]: 

2226 return self._t, str(self), self._original, self._trivia 

2227 

2228 @classmethod 

2229 def from_raw( 

2230 cls, value: str, type_: StringType = StringType.SLB, escape: bool = True 

2231 ) -> String: 

2232 value = decode(value) 

2233 

2234 invalid = type_.invalid_sequences 

2235 if any(c in value for c in invalid): 

2236 raise InvalidStringError(value, invalid, type_.value) 

2237 

2238 escaped = type_.escaped_sequences 

2239 string_value = escape_string(value, escaped) if escape and escaped else value 

2240 

2241 return cls(type_, decode(value), string_value, Trivia()) 

2242 

2243 

2244class AoT(Item, _CustomList): # type: ignore[type-arg] 

2245 """ 

2246 An array of table literal 

2247 """ 

2248 

2249 def __init__( 

2250 self, body: list[Table], name: str | None = None, parsed: bool = False 

2251 ) -> None: 

2252 self.name = name 

2253 self._body: list[Table] = [] 

2254 self._parsed = parsed 

2255 

2256 super().__init__(Trivia(trail="")) 

2257 

2258 for table in body: 

2259 self.append(table) 

2260 

2261 def unwrap(self) -> list[dict[str, Any]]: 

2262 unwrapped = [] 

2263 for t in self._body: 

2264 if hasattr(t, "unwrap"): 

2265 unwrapped.append(t.unwrap()) 

2266 else: 

2267 unwrapped.append(t) 

2268 return unwrapped 

2269 

2270 @property 

2271 def body(self) -> list[Table]: 

2272 return self._body 

2273 

2274 @property 

2275 def discriminant(self) -> int: 

2276 return 12 

2277 

2278 @property 

2279 def value(self) -> list[dict[str, Any]]: 

2280 return [v.value for v in self._body] 

2281 

2282 def __len__(self) -> int: 

2283 return len(self._body) 

2284 

2285 @overload # type: ignore[override] 

2286 def __getitem__(self, key: slice) -> list[Table]: ... 

2287 

2288 @overload 

2289 def __getitem__(self, key: int) -> Table: ... 

2290 

2291 def __getitem__(self, key: int | slice) -> Table | list[Table]: 

2292 return self._body[key] 

2293 

2294 def __setitem__(self, key: slice | int, value: Any) -> None: # type: ignore[override] 

2295 self._body[key] = item(value, _parent=self) 

2296 

2297 def __delitem__(self, key: slice | int) -> None: # type: ignore[override] 

2298 del self._body[key] 

2299 list.__delitem__(self, key) 

2300 

2301 def insert(self, index: int, value: dict[str, Any]) -> None: # type: ignore[override] 

2302 value = item(value, _parent=self) 

2303 if not isinstance(value, Table): 

2304 raise ValueError(f"Unsupported insert value type: {type(value)}") 

2305 length = len(self) 

2306 if index < 0: 

2307 index += length 

2308 if index < 0: 

2309 index = 0 

2310 elif index >= length: 

2311 index = length 

2312 m = re.match("(?s)^[^ ]*([ ]+).*$", self._trivia.indent) 

2313 if m: 

2314 indent = m.group(1) 

2315 

2316 m = re.match("(?s)^([^ ]*)(.*)$", value.trivia.indent) 

2317 if not m: 

2318 value.trivia.indent = indent 

2319 else: 

2320 value.trivia.indent = m.group(1) + indent + m.group(2) 

2321 prev_table = self._body[index - 1] if 0 < index and length else None 

2322 next_table = self._body[index + 1] if index < length - 1 else None 

2323 if not self._parsed: 

2324 if prev_table and "\n" not in value.trivia.indent: 

2325 value.trivia.indent = "\n" + value.trivia.indent 

2326 if next_table and "\n" not in next_table.trivia.indent: 

2327 next_table.trivia.indent = "\n" + next_table.trivia.indent 

2328 self._body.insert(index, value) 

2329 list.insert(self, index, value) 

2330 

2331 def invalidate_display_name(self) -> None: 

2332 """Call ``invalidate_display_name`` on the contained tables""" 

2333 for child in self: 

2334 if hasattr(child, "invalidate_display_name"): 

2335 child.invalidate_display_name() 

2336 

2337 def as_string(self) -> str: 

2338 b = "" 

2339 for table in self._body: 

2340 b += table.as_string() 

2341 

2342 return b 

2343 

2344 def __repr__(self) -> str: 

2345 return f"<AoT {self.value}>" 

2346 

2347 def _getstate(self, protocol: int = 3) -> tuple[list[Table], str | None, bool]: 

2348 return self._body, self.name, self._parsed 

2349 

2350 

2351class Null(Item): 

2352 """ 

2353 A null item. 

2354 """ 

2355 

2356 def __init__(self) -> None: 

2357 super().__init__(Trivia(trail="")) 

2358 

2359 def unwrap(self) -> None: 

2360 return None 

2361 

2362 @property 

2363 def discriminant(self) -> int: 

2364 return -1 

2365 

2366 @property 

2367 def value(self) -> None: 

2368 return None 

2369 

2370 def as_string(self) -> str: 

2371 return "" 

2372 

2373 def _getstate(self, protocol: int = 3) -> tuple[()]: 

2374 return ()