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

1357 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 "\n" in comment or "\r" in comment: 

504 raise ValueError("Comment cannot contain line breaks") 

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

506 comment = "# " + comment 

507 

508 self._trivia.comment_ws = " " 

509 self._trivia.comment = comment 

510 

511 return self 

512 

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

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

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

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

517 else: 

518 self._trivia.indent = " " * indent 

519 

520 return self 

521 

522 def is_boolean(self) -> bool: 

523 return isinstance(self, Bool) 

524 

525 def is_table(self) -> bool: 

526 return isinstance(self, Table) 

527 

528 def is_inline_table(self) -> bool: 

529 return isinstance(self, InlineTable) 

530 

531 def is_aot(self) -> bool: 

532 return isinstance(self, AoT) 

533 

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

535 return (self._trivia,) 

536 

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

538 return self.__reduce_ex__(2) 

539 

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

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

542 

543 

544class Whitespace(Item): 

545 """ 

546 A whitespace literal. 

547 """ 

548 

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

550 self._s = s 

551 self._fixed = fixed 

552 

553 @property 

554 def s(self) -> str: 

555 return self._s 

556 

557 @property 

558 def value(self) -> str: 

559 """The wrapped string of the whitespace""" 

560 return self._s 

561 

562 @property 

563 def trivia(self) -> Trivia: 

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

565 

566 @property 

567 def discriminant(self) -> int: 

568 return 0 

569 

570 def is_fixed(self) -> bool: 

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

572 return self._fixed 

573 

574 def as_string(self) -> str: 

575 return self._s 

576 

577 def __repr__(self) -> str: 

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

579 

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

581 return self._s, self._fixed 

582 

583 

584class Comment(Item): 

585 """ 

586 A comment literal. 

587 """ 

588 

589 @property 

590 def discriminant(self) -> int: 

591 return 1 

592 

593 def as_string(self) -> str: 

594 return ( 

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

596 ) 

597 

598 def __str__(self) -> str: 

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

600 

601 

602class Integer(Item, _CustomInt): 

603 """ 

604 An integer literal. 

605 """ 

606 

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

608 return int.__new__(cls, value) 

609 

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

611 super().__init__(trivia) 

612 self._original = value 

613 self._raw = raw 

614 self._sign = False 

615 

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

617 self._sign = True 

618 

619 def unwrap(self) -> int: 

620 return self._original 

621 

622 __int__ = unwrap 

623 

624 def __hash__(self) -> int: 

625 return hash(self.unwrap()) 

626 

627 @property 

628 def discriminant(self) -> int: 

629 return 2 

630 

631 @property 

632 def value(self) -> int: 

633 """The wrapped integer value""" 

634 return self 

635 

636 def as_string(self) -> str: 

637 return self._raw 

638 

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

640 raw = str(result) 

641 if self._sign and result >= 0: 

642 raw = f"+{raw}" 

643 

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

645 

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

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

648 

649 # int methods — explicit typed wrappers 

650 def __abs__(self) -> Integer: 

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

652 

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

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

655 if result is NotImplemented: 

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

657 return self._new(result) 

658 

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

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

661 if result is NotImplemented: 

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

663 return self._new(result) 

664 

665 def __ceil__(self) -> Integer: 

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

667 

668 __eq__ = int.__eq__ 

669 

670 def __floor__(self) -> Integer: 

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

672 

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

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

675 if result is NotImplemented: 

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

677 return self._new(result) 

678 

679 def __invert__(self) -> Integer: 

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

681 

682 __le__ = int.__le__ 

683 

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

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

686 if result is NotImplemented: 

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

688 return self._new(result) 

689 

690 __lt__ = int.__lt__ 

691 

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

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

694 if result is NotImplemented: 

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

696 return self._new(result) 

697 

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

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

700 if result is NotImplemented: 

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

702 return self._new(result) 

703 

704 def __neg__(self) -> Integer: 

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

706 

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

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

709 if result is NotImplemented: 

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

711 return self._new(result) 

712 

713 def __pos__(self) -> Integer: 

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

715 

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

717 result = ( 

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

719 ) 

720 return self._new(result) 

721 

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

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

724 if result is NotImplemented: 

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

726 return self._new(result) 

727 

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

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

730 if result is NotImplemented: 

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

732 return self._new(result) 

733 

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

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

736 if result is NotImplemented: 

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

738 return self._new(result) 

739 

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

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

742 if result is NotImplemented: 

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

744 return self._new(result) 

745 

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

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

748 if result is NotImplemented: 

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

750 return self._new(result) 

751 

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

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

754 if result is NotImplemented: 

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

756 return self._new(result) 

757 

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

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

760 if result is NotImplemented: 

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

762 return self._new(result) 

763 

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

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

766 

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

768 result = ( 

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

770 ) 

771 return self._new(result) 

772 

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

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

775 if result is NotImplemented: 

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

777 return self._new(result) 

778 

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

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

781 if result is NotImplemented: 

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

783 return self._new(result) 

784 

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

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

787 if result is NotImplemented: 

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

789 return self._new(result) 

790 

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

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

793 if result is NotImplemented: 

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

795 return self._new(result) 

796 

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

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

799 if result is NotImplemented: 

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

801 return self._new(result) 

802 

803 def __trunc__(self) -> Integer: 

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

805 

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

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

808 if result is NotImplemented: 

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

810 return self._new(result) 

811 

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

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

814 if result is NotImplemented: 

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

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

817 

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

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

820 if result is NotImplemented: 

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

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

823 

824 

825class Float(Item, _CustomFloat): 

826 """ 

827 A float literal. 

828 """ 

829 

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

831 return float.__new__(cls, value) 

832 

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

834 super().__init__(trivia) 

835 self._original = value 

836 self._raw = raw 

837 self._sign = False 

838 

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

840 self._sign = True 

841 

842 def unwrap(self) -> float: 

843 return self._original 

844 

845 __float__ = unwrap 

846 

847 def __hash__(self) -> int: 

848 return hash(self.unwrap()) 

849 

850 @property 

851 def discriminant(self) -> int: 

852 return 3 

853 

854 @property 

855 def value(self) -> float: 

856 """The wrapped float value""" 

857 return self 

858 

859 def as_string(self) -> str: 

860 return self._raw 

861 

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

863 raw = str(result) 

864 

865 if self._sign and result >= 0: 

866 raw = f"+{raw}" 

867 

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

869 

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

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

872 

873 # float methods — explicit typed wrappers 

874 def __abs__(self) -> Float: 

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

876 

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

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

879 if result is NotImplemented: 

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

881 return self._new(result) 

882 

883 __eq__ = float.__eq__ 

884 

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

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

887 if result is NotImplemented: 

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

889 return self._new(result) 

890 

891 __le__ = float.__le__ 

892 __lt__ = float.__lt__ 

893 

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

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

896 if result is NotImplemented: 

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

898 return self._new(result) 

899 

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

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

902 if result is NotImplemented: 

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

904 return self._new(result) 

905 

906 def __neg__(self) -> Float: 

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

908 

909 def __pos__(self) -> Float: 

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

911 

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

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

914 if result is NotImplemented: 

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

916 return self._new(result) 

917 

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

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

920 if result is NotImplemented: 

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

922 return self._new(result) 

923 

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

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

926 if result is NotImplemented: 

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

928 return self._new(result) 

929 

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

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

932 if result is NotImplemented: 

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

934 return self._new(result) 

935 

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

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

938 if result is NotImplemented: 

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

940 return self._new(result) 

941 

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

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

944 

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

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

947 if result is NotImplemented: 

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

949 return self._new(result) 

950 

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

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

953 if result is NotImplemented: 

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

955 return self._new(result) 

956 

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

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

959 if result is NotImplemented: 

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

961 return self._new(result) 

962 

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

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

965 if result is NotImplemented: 

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

967 return self._new(result) 

968 

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

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

971 if result is NotImplemented: 

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

973 return self._new(result) 

974 

975 __trunc__ = float.__trunc__ 

976 __ceil__ = float.__ceil__ 

977 __floor__ = float.__floor__ 

978 

979 

980class Bool(Item): 

981 """ 

982 A boolean literal. 

983 """ 

984 

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

986 super().__init__(trivia) 

987 

988 self._value = bool(t) 

989 

990 def unwrap(self) -> bool: 

991 return bool(self) 

992 

993 @property 

994 def discriminant(self) -> int: 

995 return 4 

996 

997 @property 

998 def value(self) -> bool: 

999 """The wrapped boolean value""" 

1000 return self._value 

1001 

1002 def as_string(self) -> str: 

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

1004 

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

1006 return self._value, self._trivia 

1007 

1008 def __bool__(self) -> bool: 

1009 return self._value 

1010 

1011 __nonzero__ = __bool__ 

1012 

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

1014 if not isinstance(other, bool): 

1015 return NotImplemented 

1016 

1017 return other == self._value 

1018 

1019 def __hash__(self) -> int: 

1020 return hash(self._value) 

1021 

1022 def __repr__(self) -> str: 

1023 return repr(self._value) 

1024 

1025 

1026class DateTime(Item, datetime): 

1027 """ 

1028 A datetime literal. 

1029 """ 

1030 

1031 def __new__( 

1032 cls, 

1033 year: int, 

1034 month: int, 

1035 day: int, 

1036 hour: int, 

1037 minute: int, 

1038 second: int, 

1039 microsecond: int, 

1040 tzinfo: tzinfo | None, 

1041 trivia: Trivia | None = None, 

1042 raw: str | None = None, 

1043 **kwargs: object, 

1044 ) -> DateTime: 

1045 return datetime.__new__( 

1046 cls, 

1047 year, 

1048 month, 

1049 day, 

1050 hour, 

1051 minute, 

1052 second, 

1053 microsecond, 

1054 tzinfo=tzinfo, 

1055 ) 

1056 

1057 def __init__( 

1058 self, 

1059 year: int, 

1060 month: int, 

1061 day: int, 

1062 hour: int, 

1063 minute: int, 

1064 second: int, 

1065 microsecond: int, 

1066 tzinfo: tzinfo | None, 

1067 trivia: Trivia | None = None, 

1068 raw: str | None = None, 

1069 **kwargs: object, 

1070 ) -> None: 

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

1072 

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

1074 

1075 def unwrap(self) -> datetime: 

1076 ( 

1077 year, 

1078 month, 

1079 day, 

1080 hour, 

1081 minute, 

1082 second, 

1083 microsecond, 

1084 tzinfo, 

1085 _, 

1086 _, 

1087 ) = self._getstate() 

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

1089 

1090 @property 

1091 def discriminant(self) -> int: 

1092 return 5 

1093 

1094 @property 

1095 def value(self) -> datetime: 

1096 return self 

1097 

1098 def as_string(self) -> str: 

1099 return self._raw 

1100 

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

1102 if PY38: 

1103 result = datetime( 

1104 self.year, 

1105 self.month, 

1106 self.day, 

1107 self.hour, 

1108 self.minute, 

1109 self.second, 

1110 self.microsecond, 

1111 self.tzinfo, 

1112 ).__add__(other) 

1113 else: 

1114 result = super().__add__(other) 

1115 

1116 return self._new(result) 

1117 

1118 @overload # type: ignore[override] 

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

1120 

1121 @overload 

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

1123 

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

1125 if PY38: 

1126 result = datetime( 

1127 self.year, 

1128 self.month, 

1129 self.day, 

1130 self.hour, 

1131 self.minute, 

1132 self.second, 

1133 self.microsecond, 

1134 self.tzinfo, 

1135 ).__sub__(other) 

1136 else: 

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

1138 

1139 if isinstance(result, datetime): 

1140 result = self._new(result) 

1141 

1142 return result 

1143 

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

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

1146 

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

1148 result = super().astimezone(tz) 

1149 if PY38: 

1150 return result 

1151 return self._new(result) 

1152 

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

1154 raw = result.isoformat() 

1155 

1156 return DateTime( 

1157 result.year, 

1158 result.month, 

1159 result.day, 

1160 result.hour, 

1161 result.minute, 

1162 result.second, 

1163 result.microsecond, 

1164 result.tzinfo, 

1165 self._trivia, 

1166 raw, 

1167 ) 

1168 

1169 def _getstate( 

1170 self, protocol: int = 3 

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

1172 return ( 

1173 self.year, 

1174 self.month, 

1175 self.day, 

1176 self.hour, 

1177 self.minute, 

1178 self.second, 

1179 self.microsecond, 

1180 self.tzinfo, 

1181 self._trivia, 

1182 self._raw, 

1183 ) 

1184 

1185 

1186class Date(Item, date): 

1187 """ 

1188 A date literal. 

1189 """ 

1190 

1191 def __new__( 

1192 cls, 

1193 year: int, 

1194 month: int, 

1195 day: int, 

1196 trivia: Trivia | None = None, 

1197 raw: str = "", 

1198 ) -> Date: 

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

1200 

1201 def __init__( 

1202 self, 

1203 year: int, 

1204 month: int, 

1205 day: int, 

1206 trivia: Trivia | None = None, 

1207 raw: str = "", 

1208 ) -> None: 

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

1210 

1211 self._raw = raw 

1212 

1213 def unwrap(self) -> date: 

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

1215 return date(year, month, day) 

1216 

1217 @property 

1218 def discriminant(self) -> int: 

1219 return 6 

1220 

1221 @property 

1222 def value(self) -> date: 

1223 return self 

1224 

1225 def as_string(self) -> str: 

1226 return self._raw 

1227 

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

1229 if PY38: 

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

1231 else: 

1232 result = super().__add__(other) 

1233 

1234 return self._new(result) 

1235 

1236 @overload # type: ignore[override] 

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

1238 

1239 @overload 

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

1241 

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

1243 if PY38: 

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

1245 else: 

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

1247 

1248 if isinstance(result, date): 

1249 result = self._new(result) 

1250 

1251 return result 

1252 

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

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

1255 

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

1257 raw = result.isoformat() 

1258 

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

1260 

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

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

1263 

1264 

1265class Time(Item, time): 

1266 """ 

1267 A time literal. 

1268 """ 

1269 

1270 def __new__( 

1271 cls, 

1272 hour: int, 

1273 minute: int, 

1274 second: int, 

1275 microsecond: int, 

1276 tzinfo: tzinfo | None, 

1277 trivia: Trivia | None = None, 

1278 raw: str = "", 

1279 ) -> Time: 

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

1281 

1282 def __init__( 

1283 self, 

1284 hour: int, 

1285 minute: int, 

1286 second: int, 

1287 microsecond: int, 

1288 tzinfo: tzinfo | None, 

1289 trivia: Trivia | None = None, 

1290 raw: str = "", 

1291 ) -> None: 

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

1293 

1294 self._raw = raw 

1295 

1296 def unwrap(self) -> time: 

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

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

1299 

1300 @property 

1301 def discriminant(self) -> int: 

1302 return 7 

1303 

1304 @property 

1305 def value(self) -> time: 

1306 return self 

1307 

1308 def as_string(self) -> str: 

1309 return self._raw 

1310 

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

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

1313 

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

1315 raw = result.isoformat() 

1316 

1317 return Time( 

1318 result.hour, 

1319 result.minute, 

1320 result.second, 

1321 result.microsecond, 

1322 result.tzinfo, 

1323 self._trivia, 

1324 raw, 

1325 ) 

1326 

1327 def _getstate( 

1328 self, protocol: int = 3 

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

1330 return ( 

1331 self.hour, 

1332 self.minute, 

1333 self.second, 

1334 self.microsecond, 

1335 self.tzinfo, 

1336 self._trivia, 

1337 self._raw, 

1338 ) 

1339 

1340 

1341class _ArrayItemGroup: 

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

1343 

1344 def __init__( 

1345 self, 

1346 value: Item | None = None, 

1347 indent: Whitespace | None = None, 

1348 comma: Whitespace | None = None, 

1349 comment: Comment | None = None, 

1350 ) -> None: 

1351 self.value = value 

1352 self.indent = indent 

1353 self.comma = comma 

1354 self.comment = comment 

1355 

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

1357 return ( 

1358 x 

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

1360 if x is not None 

1361 ) 

1362 

1363 def __repr__(self) -> str: 

1364 return repr(tuple(self)) 

1365 

1366 def is_whitespace(self) -> bool: 

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

1368 

1369 def __bool__(self) -> bool: 

1370 try: 

1371 next(iter(self)) 

1372 except StopIteration: 

1373 return False 

1374 return True 

1375 

1376 

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

1378 """ 

1379 An array literal 

1380 """ 

1381 

1382 def __init__( 

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

1384 ) -> None: 

1385 super().__init__(trivia) 

1386 list.__init__( 

1387 self, 

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

1389 ) 

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

1391 self._value = self._group_values(value) 

1392 self._multiline = multiline 

1393 self._reindex() 

1394 

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

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

1397 groups = [] 

1398 this_group = _ArrayItemGroup() 

1399 start_new_group = False 

1400 for item in value: 

1401 if isinstance(item, Whitespace): 

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

1403 groups.append(this_group) 

1404 this_group = _ArrayItemGroup(indent=item) 

1405 start_new_group = False 

1406 else: 

1407 if this_group.value is None: 

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

1409 this_group.value = Null() 

1410 this_group.comma = item 

1411 elif isinstance(item, Comment): 

1412 if this_group.value is None: 

1413 this_group.value = Null() 

1414 this_group.comment = item 

1415 # Comments are the last item in a group. 

1416 start_new_group = True 

1417 elif this_group.value is None: 

1418 this_group.value = item 

1419 else: 

1420 groups.append(this_group) 

1421 this_group = _ArrayItemGroup(value=item) 

1422 groups.append(this_group) 

1423 return [group for group in groups if group] 

1424 

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

1426 unwrapped = [] 

1427 for v in self: 

1428 if hasattr(v, "unwrap"): 

1429 unwrapped.append(v.unwrap()) 

1430 else: 

1431 unwrapped.append(v) 

1432 return unwrapped 

1433 

1434 @property 

1435 def discriminant(self) -> int: 

1436 return 8 

1437 

1438 @property 

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

1440 return self 

1441 

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

1443 for v in self._value: 

1444 yield from v 

1445 

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

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

1448 

1449 :Example: 

1450 

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

1452 >>> print(a.as_string()) 

1453 [1, 2, 3] 

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

1455 [ 

1456 1, 

1457 2, 

1458 3, 

1459 ] 

1460 """ 

1461 self._multiline = multiline 

1462 

1463 return self 

1464 

1465 def as_string(self) -> str: 

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

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

1468 

1469 s = "[\n" 

1470 s += "".join( 

1471 self.trivia.indent 

1472 + " " * 4 

1473 + v.value.as_string() 

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

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

1476 + "\n" 

1477 for v in self._value 

1478 if v.value is not None 

1479 ) 

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

1481 

1482 return s 

1483 

1484 def _reindex(self) -> None: 

1485 self._index_map.clear() 

1486 index = 0 

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

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

1489 continue 

1490 self._index_map[index] = i 

1491 index += 1 

1492 

1493 def add_line( 

1494 self, 

1495 *items: Any, 

1496 indent: str = " ", 

1497 comment: str | None = None, 

1498 add_comma: bool = True, 

1499 newline: bool = True, 

1500 ) -> None: 

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

1502 When add_comma is True, only accept actual values and 

1503 ", " will be added between values automatically. 

1504 

1505 :Example: 

1506 

1507 >>> a = array() 

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

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

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

1511 >>> print(a.as_string()) 

1512 [ 

1513 1, 2, 3, 

1514 4, 5, 6, 

1515 ] 

1516 """ 

1517 if comment and ("\n" in comment or "\r" in comment): 

1518 raise ValueError("Comment cannot contain line breaks") 

1519 new_values: list[Item] = [] 

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

1521 if first_indent: 

1522 new_values.append(Whitespace(first_indent)) 

1523 whitespace = "" 

1524 data_values = [] 

1525 for i, el in enumerate(items): 

1526 it = item(el, _parent=self) 

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

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

1529 if not isinstance(it, Whitespace): 

1530 if whitespace: 

1531 new_values.append(Whitespace(whitespace)) 

1532 whitespace = "" 

1533 new_values.append(it) 

1534 data_values.append(it.value) 

1535 if add_comma: 

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

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

1538 new_values.append(Whitespace(" ")) 

1539 elif "," not in it.s: 

1540 whitespace += it.s 

1541 else: 

1542 new_values.append(it) 

1543 if whitespace: 

1544 new_values.append(Whitespace(whitespace)) 

1545 if comment: 

1546 indent = " " if items else "" 

1547 new_values.append( 

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

1549 ) 

1550 list.extend(self, data_values) 

1551 if len(self._value) > 0: 

1552 last_item = self._value[-1] 

1553 last_value_item = next( 

1554 ( 

1555 v 

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

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

1558 ), 

1559 None, 

1560 ) 

1561 if last_value_item is not None: 

1562 last_value_item.comma = Whitespace(",") 

1563 if last_item.is_whitespace(): 

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

1565 else: 

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

1567 else: 

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

1569 self._reindex() 

1570 

1571 def clear(self) -> None: 

1572 """Clear the array.""" 

1573 list.clear(self) 

1574 self._index_map.clear() 

1575 self._value.clear() 

1576 

1577 def __len__(self) -> int: 

1578 return list.__len__(self) 

1579 

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

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

1582 

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

1584 return list.__getitem__(self, key) 

1585 

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

1587 it = item(value, _parent=self) 

1588 list.__setitem__(self, key, it) 

1589 if isinstance(key, slice): 

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

1591 if key < 0: 

1592 key += len(self) 

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

1594 

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

1596 it = item(value, _parent=self) 

1597 length = len(self) 

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

1599 list.insert(self, pos, it) 

1600 if pos < 0: 

1601 pos += length 

1602 if pos < 0: 

1603 pos = 0 

1604 

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

1606 default_indent = " " 

1607 if pos < length: 

1608 try: 

1609 idx = self._index_map[pos] 

1610 except KeyError as e: 

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

1612 else: 

1613 idx = len(self._value) 

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

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

1616 idx -= 1 

1617 _indent = self._value[idx].indent 

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

1619 default_indent = "\n " 

1620 indent: Whitespace | None = None 

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

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

1623 # Prefer to copy the indentation from the item after 

1624 indent = self._value[idx].indent 

1625 if idx > 0: 

1626 last_item = self._value[idx - 1] 

1627 if indent is None: 

1628 indent = last_item.indent 

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

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

1631 # 2) the array is multiline 

1632 comma = last_item.comma 

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

1634 if ( 

1635 last_item.comma is None 

1636 and not isinstance(last_item.value, Null) 

1637 and not comma_in_indent 

1638 ): 

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

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

1641 # comma (comma-first style). 

1642 last_item.comma = Whitespace(",") 

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

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

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

1646 # comma of its own. 

1647 comma = None 

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

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

1650 indent = Whitespace(default_indent) 

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

1652 self._value.insert(idx, new_item) 

1653 self._reindex() 

1654 

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

1656 length = len(self) 

1657 list.__delitem__(self, key) 

1658 

1659 if isinstance(key, slice): 

1660 indices_to_remove = list( 

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

1662 ) 

1663 else: 

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

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

1666 try: 

1667 idx = self._index_map[i] 

1668 except KeyError as e: 

1669 if not isinstance(key, slice): 

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

1671 else: 

1672 group_rm = self._value[idx] 

1673 del self._value[idx] 

1674 if ( 

1675 idx == 0 

1676 and len(self._value) > 0 

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

1678 and "\n" not in ind.s 

1679 ): 

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

1681 self._value[idx].indent = None 

1682 comma_in_indent = ( 

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

1684 ) 

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

1686 if comma_in_indent and comma_in_comma: 

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

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

1689 if group is not None: 

1690 if group.indent is None: 

1691 group.indent = Whitespace(",") 

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

1693 # Insert the comma after the newline 

1694 try: 

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

1696 group.indent._s = ( 

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

1698 + "," 

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

1700 ) 

1701 except ValueError: 

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

1703 elif not comma_in_indent and not comma_in_comma: 

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

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

1706 group = self._value[j] 

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

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

1709 break 

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

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

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

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

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

1715 if group is not None and ( 

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

1717 ): 

1718 group.indent = group_rm.indent 

1719 

1720 if len(self._value) > 0: 

1721 v = self._value[-1] 

1722 if not v.is_whitespace(): 

1723 # remove the comma of the last item 

1724 v.comma = None 

1725 

1726 self._reindex() 

1727 

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

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

1730 

1731 

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

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

1734 

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

1736 Item.__init__(self, trivia) 

1737 

1738 self._value = value 

1739 

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

1741 if k is not None: 

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

1743 

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

1745 # Delegate to the inner container's unwrap, which walks its _body 

1746 # directly instead of re-resolving every key through items()/__getitem__ 

1747 # (which rebuilds a SingleKey, and an OutOfOrderTableProxy for 

1748 # out-of-order keys, per key just to throw it away). 

1749 return self._value.unwrap() 

1750 

1751 @property 

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

1753 return self._value 

1754 

1755 @overload 

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

1757 

1758 @overload 

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

1760 

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

1762 raise NotImplementedError 

1763 

1764 @overload 

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

1766 

1767 @overload 

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

1769 

1770 def add( 

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

1772 ) -> AT: 

1773 if value is None: 

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

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

1776 raise ValueError(msg) 

1777 

1778 return self.append(None, key) 

1779 

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

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

1782 

1783 return self.append(key, value) 

1784 

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

1786 self._value.remove(key) 

1787 

1788 if isinstance(key, Key): 

1789 key = key.key 

1790 

1791 if key is not None: 

1792 dict.__delitem__(self, key) 

1793 

1794 return self 

1795 

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

1797 return self._value.item(key) 

1798 

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

1800 super().setdefault(key, default) 

1801 return self[key] 

1802 

1803 def __str__(self) -> str: 

1804 return str(self.value) 

1805 

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

1807 return copy.copy(self) 

1808 

1809 def __repr__(self) -> str: 

1810 return repr(self.value) 

1811 

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

1813 return iter(self._value) 

1814 

1815 def __len__(self) -> int: 

1816 return len(self._value) 

1817 

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

1819 self.remove(key) 

1820 

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

1822 return self._value[key] 

1823 

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

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

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

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

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

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

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

1831 return key in self._value 

1832 

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

1834 if not isinstance(value, Item): 

1835 value = item(value, _parent=self) 

1836 

1837 is_replace = key in self 

1838 self._value[key] = value 

1839 

1840 if key is not None: 

1841 dict.__setitem__(self, key, value) 

1842 

1843 if is_replace: 

1844 return 

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

1846 if not m: 

1847 return 

1848 

1849 indent = m.group(1) 

1850 

1851 if not isinstance(value, Whitespace): 

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

1853 if not m: 

1854 value.trivia.indent = indent 

1855 else: 

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

1857 

1858 

1859class Table(AbstractTable): 

1860 """ 

1861 A table literal. 

1862 """ 

1863 

1864 def __init__( 

1865 self, 

1866 value: container.Container, 

1867 trivia: Trivia, 

1868 is_aot_element: bool, 

1869 is_super_table: bool | None = None, 

1870 name: str | None = None, 

1871 display_name: str | None = None, 

1872 ) -> None: 

1873 super().__init__(value, trivia) 

1874 

1875 self.name = name 

1876 self.display_name = display_name 

1877 self._is_aot_element = is_aot_element 

1878 self._is_super_table = is_super_table 

1879 

1880 @property 

1881 def discriminant(self) -> int: 

1882 return 9 

1883 

1884 def __copy__(self) -> Table: 

1885 return type(self)( 

1886 self._value.copy(), 

1887 self._trivia.copy(), 

1888 self._is_aot_element, 

1889 self._is_super_table, 

1890 self.name, 

1891 self.display_name, 

1892 ) 

1893 

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

1895 """ 

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

1897 """ 

1898 if not isinstance(_item, Item): 

1899 _item = item(_item, _parent=self) 

1900 

1901 self._value.append(key, _item) 

1902 

1903 if isinstance(key, Key): 

1904 key = next(iter(key)).key 

1905 _item = self._value[key] 

1906 

1907 if key is not None: 

1908 dict.__setitem__(self, key, _item) 

1909 

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

1911 if not m: 

1912 return self 

1913 

1914 indent = m.group(1) 

1915 

1916 if not isinstance(_item, Whitespace): 

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

1918 if not m: 

1919 _item.trivia.indent = indent 

1920 else: 

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

1922 

1923 return self 

1924 

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

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

1927 if not isinstance(_item, Item): 

1928 _item = item(_item) 

1929 

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

1931 

1932 if isinstance(key, Key): 

1933 key = next(iter(key)).key 

1934 _item = self._value[key] 

1935 

1936 if key is not None: 

1937 dict.__setitem__(self, key, _item) 

1938 

1939 return self 

1940 

1941 def is_aot_element(self) -> bool: 

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

1943 return self._is_aot_element 

1944 

1945 def is_super_table(self) -> bool: 

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

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

1948 if self._is_super_table is not None: 

1949 return self._is_super_table 

1950 if not self: 

1951 return False 

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

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

1954 if not isinstance(k, Key): 

1955 k = SingleKey(k) 

1956 index = self.value._map[k] 

1957 if isinstance(index, tuple): 

1958 return False 

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

1960 if ( 

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

1962 or real_key is None 

1963 or real_key.is_dotted() 

1964 ): 

1965 return False 

1966 return True 

1967 

1968 def as_string(self) -> str: 

1969 return self._value.as_string() 

1970 

1971 # Helpers 

1972 

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

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

1975 super().indent(indent) 

1976 

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

1978 if not m: 

1979 indent_str = "" 

1980 else: 

1981 indent_str = m.group(1) 

1982 

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

1984 if not isinstance(item, Whitespace): 

1985 item.trivia.indent = indent_str + item.trivia.indent 

1986 

1987 return self 

1988 

1989 def invalidate_display_name(self) -> None: 

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

1991 self.display_name = None 

1992 

1993 for child in self.values(): 

1994 if hasattr(child, "invalidate_display_name"): 

1995 child.invalidate_display_name() 

1996 

1997 def _getstate( 

1998 self, protocol: int = 3 

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

2000 return ( 

2001 self._value, 

2002 self._trivia, 

2003 self._is_aot_element, 

2004 self._is_super_table, 

2005 self.name, 

2006 self.display_name, 

2007 ) 

2008 

2009 

2010class InlineTable(AbstractTable): 

2011 """ 

2012 An inline table literal. 

2013 """ 

2014 

2015 def __init__( 

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

2017 ) -> None: 

2018 super().__init__(value, trivia) 

2019 

2020 self._new = new 

2021 

2022 @property 

2023 def discriminant(self) -> int: 

2024 return 10 

2025 

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

2027 """ 

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

2029 """ 

2030 if not isinstance(_item, Item): 

2031 _item = item(_item, _parent=self) 

2032 

2033 self._validate_child(_item) 

2034 

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

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

2037 _item.trivia.indent = " " 

2038 if _item.trivia.comment: 

2039 _item.trivia.comment = "" 

2040 

2041 self._value.append(key, _item) 

2042 

2043 if isinstance(key, Key): 

2044 key = key.key 

2045 

2046 if key is not None: 

2047 dict.__setitem__(self, key, _item) 

2048 

2049 return self 

2050 

2051 def _validate_child(self, _item: Item) -> None: 

2052 if isinstance(_item, Table): 

2053 raise ValueError("Inline tables cannot contain a table") 

2054 

2055 def as_string(self) -> str: 

2056 buf = "{" 

2057 emitted_key = False 

2058 needs_separator = False 

2059 # Single pass over the body to precompute everything the render loop 

2060 # needs, instead of rescanning the tail on every separator comma (which 

2061 # was O(n^2) on large inline tables): whether any explicit-comma 

2062 # whitespace is present, the index of the last real key, and the index 

2063 # of the last Null (deleted) element. 

2064 has_explicit_commas = False 

2065 last_item_idx = None 

2066 last_null_idx = -1 

2067 for _i, (_k, _v) in enumerate(self._value.body): 

2068 if _k is not None: 

2069 last_item_idx = _i 

2070 elif isinstance(_v, Whitespace) and "," in _v.s: 

2071 has_explicit_commas = True 

2072 if isinstance(_v, Null): 

2073 last_null_idx = _i 

2074 pending_separator = False 

2075 # Buffer position right after the last rendered value, used to place a 

2076 # deferred separator comma after the value rather than after a trailing 

2077 # comment/whitespace. 

2078 last_value_end = len(buf) 

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

2080 if k is None: 

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

2082 if not emitted_key: 

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

2084 continue 

2085 

2086 # Equivalent to scanning body[i + 1 :] for a Null / a real 

2087 # key, but O(1) using the indices precomputed above. 

2088 has_following_null = last_null_idx > i 

2089 has_following_key = last_item_idx is not None and last_item_idx > i 

2090 if has_following_null and not has_following_key: 

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

2092 continue 

2093 

2094 if pending_separator: 

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

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

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

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

2099 continue 

2100 

2101 pending_separator = True 

2102 

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

2104 if self._new: 

2105 buf = buf.rstrip(", ") 

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

2107 buf = buf.rstrip(",") 

2108 

2109 v_string = v.as_string() 

2110 buf += v_string 

2111 if "," in v_string: 

2112 needs_separator = False 

2113 

2114 continue 

2115 

2116 if has_explicit_commas and needs_separator: 

2117 # Insert the deferred separator right after the previous value, 

2118 # not after any trailing comment/whitespace -- otherwise the 

2119 # comma is swallowed by a trailing comment (see #512). 

2120 buf = f"{buf[:last_value_end]},{buf[last_value_end:]}" 

2121 needs_separator = False 

2122 

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

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

2125 # A table materialized from a dotted key renders one 

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

2127 # after parsing stay attached to the dotted prefix. 

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

2129 else: 

2130 rendered = ( 

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

2132 f"{k.sep}" 

2133 f"{v.as_string()}" 

2134 ) 

2135 buf += f"{v.trivia.indent}{rendered}" 

2136 last_value_end = len(buf) 

2137 buf += f"{v.trivia.comment}{v_trivia_trail}" 

2138 emitted_key = True 

2139 pending_separator = False 

2140 if has_explicit_commas: 

2141 needs_separator = True 

2142 

2143 if ( 

2144 not has_explicit_commas 

2145 and last_item_idx is not None 

2146 and i < last_item_idx 

2147 ): 

2148 buf += "," 

2149 if self._new: 

2150 buf += " " 

2151 

2152 buf += "}" 

2153 

2154 return buf 

2155 

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

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

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

2159 children.""" 

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

2161 parts = [] 

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

2163 if k is None: 

2164 continue 

2165 if isinstance(v, Table): 

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

2167 else: 

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

2169 parts.append( 

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

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

2172 ) 

2173 return parts 

2174 

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

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

2177 value.trivia.comment = "" 

2178 if not isinstance(value, Item): 

2179 value = item(value, _parent=self) 

2180 self._validate_child(value) 

2181 super().__setitem__(key, value) 

2182 

2183 def __copy__(self) -> InlineTable: 

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

2185 

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

2187 return (self._value, self._trivia) 

2188 

2189 

2190class String(str, Item): 

2191 """ 

2192 A string literal. 

2193 """ 

2194 

2195 def __new__( 

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

2197 ) -> String: 

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

2199 

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

2201 super().__init__(trivia) 

2202 

2203 self._t = t 

2204 self._original = original 

2205 

2206 def unwrap(self) -> str: 

2207 return str(self) 

2208 

2209 @property 

2210 def discriminant(self) -> int: 

2211 return 11 

2212 

2213 @property 

2214 def value(self) -> str: 

2215 return self 

2216 

2217 def as_string(self) -> str: 

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

2219 

2220 @property 

2221 def type(self) -> StringType: 

2222 return self._t 

2223 

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

2225 result = super().__add__(other) 

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

2227 

2228 return self._new(result, original) 

2229 

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

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

2232 

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

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

2235 

2236 @classmethod 

2237 def from_raw( 

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

2239 ) -> String: 

2240 value = decode(value) 

2241 

2242 invalid = type_.invalid_sequences 

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

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

2245 

2246 escaped = type_.escaped_sequences 

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

2248 

2249 if type_.is_multiline() and string_value[:1] in ("\n", "\r"): 

2250 # A newline immediately following the opening delimiter of a 

2251 # multiline string is trimmed by the parser, so a value that 

2252 # starts with a newline would otherwise lose it on round-trip. 

2253 # Emit an extra leading newline (the trimmed one) to preserve it. 

2254 string_value = "\n" + string_value 

2255 

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

2257 

2258 

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

2260 """ 

2261 An array of table literal 

2262 """ 

2263 

2264 def __init__( 

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

2266 ) -> None: 

2267 self.name = name 

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

2269 self._parsed = parsed 

2270 

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

2272 

2273 for table in body: 

2274 self.append(table) 

2275 

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

2277 unwrapped = [] 

2278 for t in self._body: 

2279 if hasattr(t, "unwrap"): 

2280 unwrapped.append(t.unwrap()) 

2281 else: 

2282 unwrapped.append(t) 

2283 return unwrapped 

2284 

2285 @property 

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

2287 return self._body 

2288 

2289 @property 

2290 def discriminant(self) -> int: 

2291 return 12 

2292 

2293 @property 

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

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

2296 

2297 def __len__(self) -> int: 

2298 return len(self._body) 

2299 

2300 @overload # type: ignore[override] 

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

2302 

2303 @overload 

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

2305 

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

2307 return self._body[key] 

2308 

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

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

2311 

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

2313 del self._body[key] 

2314 list.__delitem__(self, key) 

2315 

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

2317 value = item(value, _parent=self) 

2318 if not isinstance(value, Table): 

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

2320 length = len(self) 

2321 if index < 0: 

2322 index += length 

2323 if index < 0: 

2324 index = 0 

2325 elif index >= length: 

2326 index = length 

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

2328 if m: 

2329 indent = m.group(1) 

2330 

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

2332 if not m: 

2333 value.trivia.indent = indent 

2334 else: 

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

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

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

2338 if not self._parsed: 

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

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

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

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

2343 self._body.insert(index, value) 

2344 list.insert(self, index, value) 

2345 

2346 def invalidate_display_name(self) -> None: 

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

2348 for child in self: 

2349 if hasattr(child, "invalidate_display_name"): 

2350 child.invalidate_display_name() 

2351 

2352 def as_string(self) -> str: 

2353 b = "" 

2354 for table in self._body: 

2355 b += table.as_string() 

2356 

2357 return b 

2358 

2359 def __repr__(self) -> str: 

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

2361 

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

2363 return self._body, self.name, self._parsed 

2364 

2365 

2366class Null(Item): 

2367 """ 

2368 A null item. 

2369 """ 

2370 

2371 def __init__(self) -> None: 

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

2373 

2374 def unwrap(self) -> None: 

2375 return None 

2376 

2377 @property 

2378 def discriminant(self) -> int: 

2379 return -1 

2380 

2381 @property 

2382 def value(self) -> None: 

2383 return None 

2384 

2385 def as_string(self) -> str: 

2386 return "" 

2387 

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

2389 return ()