Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/hypothesis/vendor/pretty.py: 33%

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

624 statements  

1# This file is part of Hypothesis, which may be found at 

2# https://github.com/HypothesisWorks/hypothesis/ 

3# 

4# Copyright the Hypothesis Authors. 

5# Individual contributors are listed in AUTHORS.rst and the git log. 

6# 

7# This Source Code Form is subject to the terms of the Mozilla Public License, 

8# v. 2.0. If a copy of the MPL was not distributed with this file, You can 

9# obtain one at https://mozilla.org/MPL/2.0/. 

10 

11""" 

12Python advanced pretty printer. This pretty printer is intended to 

13replace the old `pprint` python module which does not allow developers 

14to provide their own pretty print callbacks. 

15This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`. 

16Example Usage 

17------------- 

18To get a string of the output use `pretty`:: 

19 from pretty import pretty 

20 string = pretty(complex_object) 

21Extending 

22--------- 

23The pretty library allows developers to add pretty printing rules for their 

24own objects. This process is straightforward. All you have to do is to 

25add a `_repr_pretty_` method to your object and call the methods on the 

26pretty printer passed:: 

27 class MyObject(object): 

28 def _repr_pretty_(self, p, cycle): 

29 ... 

30Here is an example implementation of a `_repr_pretty_` method for a list 

31subclass:: 

32 class MyList(list): 

33 def _repr_pretty_(self, p, cycle): 

34 if cycle: 

35 p.text('MyList(...)') 

36 else: 

37 with p.group(8, 'MyList([', '])'): 

38 for idx, item in enumerate(self): 

39 if idx: 

40 p.text(',') 

41 p.breakable() 

42 p.pretty(item) 

43The `cycle` parameter is `True` if pretty detected a cycle. You *have* to 

44react to that or the result is an infinite loop. `p.text()` just adds 

45non breaking text to the output, `p.breakable()` either adds a whitespace 

46or breaks here. If you pass it an argument it's used instead of the 

47default space. `p.pretty` prettyprints another object using the pretty print 

48method. 

49The first parameter to the `group` function specifies the extra indentation 

50of the next line. In this example the next item will either be on the same 

51line (if the items are short enough) or aligned with the right edge of the 

52opening bracket of `MyList`. 

53If you just want to indent something you can use the group function 

54without open / close parameters. You can also use this code:: 

55 with p.indent(2): 

56 ... 

57Inheritance diagram: 

58.. inheritance-diagram:: IPython.lib.pretty 

59 :parts: 3 

60:copyright: 2007 by Armin Ronacher. 

61 Portions (c) 2009 by Robert Kern. 

62:license: BSD License. 

63""" 

64 

65import ast 

66import datetime 

67import re 

68import struct 

69import sys 

70import types 

71import warnings 

72from collections import Counter, OrderedDict, defaultdict, deque 

73from collections.abc import Callable, Generator, Iterable, Sequence 

74from contextlib import contextmanager, suppress 

75from enum import Enum, Flag 

76from functools import partial 

77from io import StringIO, TextIOBase 

78from math import copysign, isnan 

79from typing import TYPE_CHECKING, Any, Optional, TypeAlias, TypeVar 

80 

81if TYPE_CHECKING: 

82 from hypothesis.control import BuildContext 

83 

84T = TypeVar("T") 

85PrettyPrintFunction: TypeAlias = Callable[[Any, "RepresentationPrinter", bool], None] 

86ArgLabelsT: TypeAlias = dict[str, tuple[int, int]] 

87 

88__all__ = [ 

89 "IDKey", 

90 "RepresentationPrinter", 

91 "_fixeddict_pprinter", 

92 "_tuple_pprinter", 

93 "pretty", 

94] 

95 

96 

97def _safe_getattr(obj: object, attr: str, default: Any | None = None) -> Any: 

98 """Safe version of getattr. 

99 

100 Same as getattr, but will return ``default`` on any Exception, 

101 rather than raising. 

102 

103 """ 

104 try: 

105 return getattr(obj, attr, default) 

106 except Exception: 

107 return default 

108 

109 

110def pretty(obj: object, *, cycle: bool = False) -> str: 

111 """Pretty print the object's representation.""" 

112 printer = RepresentationPrinter() 

113 printer.pretty(obj, cycle=cycle) 

114 return printer.getvalue() 

115 

116 

117class IDKey: 

118 def __init__(self, value: object): 

119 self.value = value 

120 

121 def __hash__(self) -> int: 

122 return hash((type(self), id(self.value))) 

123 

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

125 return isinstance(__o, type(self)) and id(self.value) == id(__o.value) 

126 

127 

128def _try_inline_lambda( 

129 func_name: str, 

130 args: Sequence[object], 

131 kwargs: dict[str, object], 

132 printer: "RepresentationPrinter", 

133) -> bool: 

134 """Try to inline single-use lambda arguments into the body expression. 

135 

136 Given e.g. func_name="lambda b: hashlib.sha256(b).hexdigest()" with 

137 args=(b'',), returns the printer output for "hashlib.sha256(b'').hexdigest()" 

138 by substituting the argument repr into the AST. 

139 

140 Returns True if inlining succeeded (the printer has been written to), 

141 False if inlining is not possible (parse failure, multi-use params, etc). 

142 """ 

143 try: 

144 tree = ast.parse(func_name, mode="eval") 

145 except Exception: 

146 return False 

147 lam = tree.body 

148 if not isinstance(lam, ast.Lambda): 

149 return False 

150 

151 # Build param name -> argument repr mapping, matching Python call semantics 

152 params = lam.args 

153 if params.vararg or params.kwonlyargs or params.kw_defaults or params.kwarg: 

154 return False 

155 

156 param_names = [p.arg for p in params.args] 

157 # params.defaults are right-aligned: if there are 3 params and 1 default, 

158 # params.defaults applies to the last param only. 

159 n_defaults = len(params.defaults) 

160 has_default = ( 

161 set(param_names[len(param_names) - n_defaults :]) if n_defaults else set() 

162 ) 

163 

164 # Bail if there are more positional args than parameters, or if any 

165 # kwarg doesn't match a parameter name — these can't be inlined. 

166 if len(args) > len(param_names): 

167 return False 

168 if any(k not in param_names for k in kwargs): 

169 return False 

170 

171 arg_reprs: dict[str, str] = {} 

172 for i, name in enumerate(param_names): 

173 if i < len(args): 

174 arg_reprs[name] = pretty(args[i]) 

175 elif name in kwargs: 

176 arg_reprs[name] = pretty(kwargs[name]) 

177 elif name in has_default: 

178 pass # not passed, will use its default — just skip 

179 else: 

180 return False 

181 

182 # Bail if any repr is not valid Python (e.g. "HypothesisRandom(generated data)") 

183 for repr_str in arg_reprs.values(): 

184 try: 

185 ast.parse(repr_str, mode="eval") 

186 except Exception: 

187 return False 

188 

189 use_counts = dict.fromkeys(param_names, 0) 

190 for node in ast.walk(lam.body): 

191 if isinstance(node, ast.Name) and node.id in use_counts: 

192 use_counts[node.id] += 1 

193 

194 # Bail if any parameter is used more than once (avoid duplicating expressions) 

195 if any(count > 1 for count in use_counts.values()): 

196 return False 

197 

198 # Substitute argument reprs into the body AST 

199 class _Inliner(ast.NodeTransformer): 

200 def visit_Name(self, node: ast.Name) -> ast.AST: 

201 if node.id in arg_reprs: 

202 # Parse the repr as an expression and splice it in. 

203 # Wrap in parens to preserve precedence in all contexts. 

204 replacement = ast.parse(arg_reprs[node.id], mode="eval").body 

205 return ast.copy_location(replacement, node) 

206 return node 

207 

208 new_body = _Inliner().visit(lam.body) 

209 ast.fix_missing_locations(new_body) 

210 

211 try: 

212 result = ast.unparse(new_body) 

213 except Exception: 

214 return False 

215 

216 printer.text(result) 

217 return True 

218 

219 

220class RepresentationPrinter: 

221 """Special pretty printer that has a `pretty` method that calls the pretty 

222 printer for a python object. 

223 

224 This class stores processing data on `self` so you must *never* use 

225 this class in a threaded environment. Always lock it or 

226 reinstantiate it. 

227 

228 """ 

229 

230 def __init__( 

231 self, 

232 output: TextIOBase | None = None, 

233 *, 

234 context: Optional["BuildContext"] = None, 

235 ) -> None: 

236 """Optionally pass the output stream and the current build context. 

237 

238 We use the context to represent objects constructed by strategies by showing 

239 *how* they were constructed, and add annotations showing which parts of the 

240 minimal failing example can vary without changing the test result. 

241 """ 

242 self.broken: bool = False 

243 self.output: TextIOBase = StringIO() if output is None else output 

244 self.max_width: int = 79 

245 self.max_seq_length: int = 1000 

246 self.output_width: int = 0 

247 self.buffer_width: int = 0 

248 self.buffer: deque[Breakable | Text] = deque() 

249 

250 root_group = Group(0) 

251 self.group_stack = [root_group] 

252 self.group_queue = GroupQueue(root_group) 

253 self.indentation: int = 0 

254 

255 self.stack: list[int] = [] 

256 self.singleton_pprinters: dict[int, PrettyPrintFunction] = {} 

257 self.type_pprinters: dict[type, PrettyPrintFunction] = {} 

258 self.deferred_pprinters: dict[tuple[str, str], PrettyPrintFunction] = {} 

259 # If IPython has been imported, load up their pretty-printer registry 

260 if "IPython.lib.pretty" in sys.modules: 

261 ipp = sys.modules["IPython.lib.pretty"] 

262 self.singleton_pprinters.update(ipp._singleton_pprinters) 

263 self.type_pprinters.update(ipp._type_pprinters) 

264 self.deferred_pprinters.update(ipp._deferred_type_pprinters) 

265 # If there's overlap between our pprinters and IPython's, we'll use ours. 

266 self.singleton_pprinters.update(_singleton_pprinters) 

267 self.type_pprinters.update(_type_pprinters) 

268 self.deferred_pprinters.update(_deferred_type_pprinters) 

269 

270 # for which-parts-matter, we track a mapping from the (start_idx, end_idx) 

271 # of slices into the minimal failing example; this is per-interesting_origin 

272 # but we report each separately so that's someone else's problem here. 

273 # Invocations of self.repr_call() can report the slice for each argument, 

274 # which will then be used to look up the relevant comment if any. 

275 self.known_object_printers: dict[IDKey, list[PrettyPrintFunction]] 

276 self.slice_comments: dict[tuple[int, int], str] 

277 if context is None: 

278 self.known_object_printers = defaultdict(list) 

279 self.slice_comments = {} 

280 else: 

281 self.known_object_printers = context.known_object_printers 

282 self.slice_comments = context.data.slice_comments 

283 assert all(isinstance(k, IDKey) for k in self.known_object_printers) 

284 # Track which slices we've already printed comments for, to avoid 

285 # duplicating comments when nested objects share the same slice range. 

286 self._commented_slices: set[tuple[int, int]] = set() 

287 

288 def pretty(self, obj: object, *, cycle: bool = False) -> None: 

289 """Pretty print the given object.""" 

290 

291 obj_id = id(obj) 

292 cycle = cycle or obj_id in self.stack 

293 self.stack.append(obj_id) 

294 try: 

295 with self.group(): 

296 obj_class = _safe_getattr(obj, "__class__", None) or type(obj) 

297 # First try to find registered singleton printers for the type. 

298 try: 

299 printer = self.singleton_pprinters[obj_id] 

300 except (TypeError, KeyError): 

301 pass 

302 else: 

303 return printer(obj, self, cycle) 

304 

305 # Look for the _repr_pretty_ method which allows users 

306 # to define custom pretty printing. 

307 # Some objects automatically create any requested 

308 # attribute. Try to ignore most of them by checking for 

309 # callability. 

310 pretty_method = _safe_getattr(obj, "_repr_pretty_", None) 

311 if callable(pretty_method): 

312 return pretty_method(self, cycle) 

313 

314 # Check for object-specific printers which show how this 

315 # object was constructed (a Hypothesis special feature). 

316 # This must come before type_pprinters so that sub-argument 

317 # comments are shown for tuples/dicts/etc. 

318 printers = self.known_object_printers[IDKey(obj)] 

319 if len(printers) == 1: 

320 return printers[0](obj, self, cycle) 

321 if printers: 

322 # Multiple registered functions for the same object (due to 

323 # caching, small ints, etc). Use the first if all produce 

324 # the same string; otherwise pretend none were registered. 

325 strs = set() 

326 for f in printers: 

327 p = RepresentationPrinter() 

328 f(obj, p, cycle) 

329 strs.add(p.getvalue()) 

330 if len(strs) == 1: 

331 return printers[0](obj, self, cycle) 

332 

333 # Next walk the mro and check for either: 

334 # 1) a registered printer 

335 # 2) a _repr_pretty_ method 

336 for cls in obj_class.__mro__: 

337 if cls in self.type_pprinters: 

338 # printer registered in self.type_pprinters 

339 return self.type_pprinters[cls](obj, self, cycle) 

340 else: 

341 # Check if the given class is specified in the deferred type 

342 # registry; move it to the regular type registry if so. 

343 key = ( 

344 _safe_getattr(cls, "__module__", None), 

345 _safe_getattr(cls, "__name__", None), 

346 ) 

347 if key in self.deferred_pprinters: 

348 # Move the printer over to the regular registry. 

349 printer = self.deferred_pprinters.pop(key) 

350 self.type_pprinters[cls] = printer 

351 return printer(obj, self, cycle) 

352 else: 

353 if hasattr(cls, "__attrs_attrs__"): # pragma: no cover 

354 return pprint_fields( 

355 obj, 

356 self, 

357 cycle, 

358 [at.name for at in cls.__attrs_attrs__ if at.init], 

359 ) 

360 if hasattr(cls, "__dataclass_fields__"): 

361 return pprint_fields( 

362 obj, 

363 self, 

364 cycle, 

365 [ 

366 k 

367 for k, v in cls.__dataclass_fields__.items() 

368 if v.init 

369 ], 

370 ) 

371 

372 # A user-provided repr. Find newlines and replace them with p.break_() 

373 return _repr_pprint(obj, self, cycle) 

374 finally: 

375 self.stack.pop() 

376 

377 def _break_outer_groups(self) -> None: 

378 while self.max_width < self.output_width + self.buffer_width: 

379 group = self.group_queue.deq() 

380 if not group: 

381 return 

382 while group.breakables: 

383 x = self.buffer.popleft() 

384 self.output_width = x.output(self.output, self.output_width) 

385 self.buffer_width -= x.width 

386 while self.buffer and isinstance(self.buffer[0], Text): 

387 x = self.buffer.popleft() 

388 self.output_width = x.output(self.output, self.output_width) 

389 self.buffer_width -= x.width 

390 

391 def text(self, obj: str) -> None: 

392 """Add literal text to the output.""" 

393 width = len(obj) 

394 if self.buffer: 

395 text = self.buffer[-1] 

396 if not isinstance(text, Text): 

397 text = Text() 

398 self.buffer.append(text) 

399 text.add(obj, width) 

400 self.buffer_width += width 

401 self._break_outer_groups() 

402 else: 

403 self.output.write(obj) 

404 self.output_width += width 

405 

406 def breakable(self, sep: str = " ") -> None: 

407 """Add a breakable separator to the output. 

408 

409 This does not mean that it will automatically break here. If no 

410 breaking on this position takes place the `sep` is inserted 

411 which default to one space. 

412 

413 """ 

414 width = len(sep) 

415 group = self.group_stack[-1] 

416 if group.want_break: 

417 self.flush() 

418 self.output.write("\n" + " " * self.indentation) 

419 self.output_width = self.indentation 

420 self.buffer_width = 0 

421 else: 

422 self.buffer.append(Breakable(sep, width, self)) 

423 self.buffer_width += width 

424 self._break_outer_groups() 

425 

426 def break_(self) -> None: 

427 """Explicitly insert a newline into the output, maintaining correct 

428 indentation.""" 

429 self.flush() 

430 self.output.write("\n" + " " * self.indentation) 

431 self.output_width = self.indentation 

432 self.buffer_width = 0 

433 

434 @contextmanager 

435 def indent(self, indent: int) -> Generator[None, None, None]: 

436 """`with`-statement support for indenting/dedenting.""" 

437 self.indentation += indent 

438 try: 

439 yield 

440 finally: 

441 self.indentation -= indent 

442 

443 @contextmanager 

444 def group( 

445 self, indent: int = 0, open: str = "", close: str = "" 

446 ) -> Generator[None, None, None]: 

447 """Context manager for an indented group. 

448 

449 with p.group(1, '{', '}'): 

450 

451 The first parameter specifies the indentation for the next line 

452 (usually the width of the opening text), the second and third the 

453 opening and closing delimiters. 

454 """ 

455 self.begin_group(indent=indent, open=open) 

456 try: 

457 yield 

458 finally: 

459 self.end_group(dedent=indent, close=close) 

460 

461 def begin_group(self, indent: int = 0, open: str = "") -> None: 

462 """Use the `with group(...) context manager instead. 

463 

464 The begin_group() and end_group() methods are for IPython compatibility only; 

465 see https://github.com/HypothesisWorks/hypothesis/issues/3721 for details. 

466 """ 

467 if open: 

468 self.text(open) 

469 group = Group(self.group_stack[-1].depth + 1) 

470 self.group_stack.append(group) 

471 self.group_queue.enq(group) 

472 self.indentation += indent 

473 

474 def end_group(self, dedent: int = 0, close: str = "") -> None: 

475 """See begin_group().""" 

476 self.indentation -= dedent 

477 group = self.group_stack.pop() 

478 if not group.breakables: 

479 self.group_queue.remove(group) 

480 if close: 

481 self.text(close) 

482 

483 def _enumerate(self, seq: Iterable[T]) -> Generator[tuple[int, T], None, None]: 

484 """Like enumerate, but with an upper limit on the number of items.""" 

485 for idx, x in enumerate(seq): 

486 if self.max_seq_length and idx >= self.max_seq_length: 

487 self.text(",") 

488 self.breakable() 

489 self.text("...") 

490 return 

491 yield idx, x 

492 

493 def flush(self) -> None: 

494 """Flush data that is left in the buffer.""" 

495 for data in self.buffer: 

496 self.output_width += data.output(self.output, self.output_width) 

497 self.buffer.clear() 

498 self.buffer_width = 0 

499 

500 def getvalue(self) -> str: 

501 assert isinstance(self.output, StringIO) 

502 self.flush() 

503 return self.output.getvalue() 

504 

505 def maybe_repr_known_object_as_call( 

506 self, 

507 obj: object, 

508 cycle: bool, 

509 name: str, 

510 args: Sequence[object], 

511 kwargs: dict[str, object], 

512 arg_labels: ArgLabelsT | None = None, 

513 ) -> None: 

514 # pprint this object as a call if it seems like a good idea to do so, 

515 # otherwise pprint as repr. 

516 # Rules: 

517 # 1. If there are comments, we *must* print as a call. 

518 # 2. Prefer valid syntax to invalid syntax. 

519 # 3. Prefer shorter expressions. 

520 if cycle: 

521 return self.text("<...>") 

522 # Look up comments from slice_comments if we have arg_labels 

523 comments = {} 

524 if arg_labels is not None: 

525 for key, sr in arg_labels.items(): 

526 if sr in self.slice_comments: 

527 comments[key] = self.slice_comments[sr] 

528 # If there are comments, we must use our call-style repr regardless of syntax 

529 if not comments: 

530 with suppress(Exception): 

531 # Check whether the repr is valid syntax: 

532 ast.parse(repr(obj)) 

533 # Given that the repr is valid syntax, check the call: 

534 p = RepresentationPrinter() 

535 p.stack = self.stack.copy() 

536 p.known_object_printers = self.known_object_printers 

537 p.repr_call(name, args, kwargs) 

538 # If the call is not valid syntax, use the repr 

539 if len(repr(obj)) < len(p.getvalue()): 

540 return _repr_pprint(obj, self, cycle) 

541 try: 

542 ast.parse(p.getvalue()) 

543 except Exception: 

544 return _repr_pprint(obj, self, cycle) 

545 return self.repr_call(name, args, kwargs, arg_slices=arg_labels) 

546 

547 def repr_call( 

548 self, 

549 func_name: str, 

550 args: Sequence[object], 

551 kwargs: dict[str, object], 

552 *, 

553 force_split: bool | None = None, 

554 arg_slices: ArgLabelsT | None = None, 

555 leading_comment: str | None = None, 

556 avoid_realization: bool = False, 

557 ) -> None: 

558 """Helper function to represent a function call. 

559 

560 - func_name, args, and kwargs should all be pretty obvious. 

561 - If split_lines, we'll force one-argument-per-line; otherwise we'll place 

562 calls that fit on a single line (and split otherwise). 

563 - arg_slices is a mapping from pos-idx or keyword to (start_idx, end_idx) 

564 of the Conjecture buffer, by which we can look up comments to add. 

565 """ 

566 assert isinstance(func_name, str) 

567 if func_name.startswith(("lambda:", "lambda ")): 

568 # Before wrapping the lambda in parens for a call, try to inline 

569 # arguments that are used exactly once in the body. If all args 

570 # get inlined, we can emit just the body expression with no call. 

571 # Skip inlining only when there are actual comments on arguments, 

572 # since comments need the call-style repr to attach to. 

573 has_comments = arg_slices and any( 

574 sr in self.slice_comments and sr not in self._commented_slices 

575 for sr in arg_slices.values() 

576 ) 

577 if not has_comments: 

578 inlined = _try_inline_lambda(func_name, args, kwargs, self) 

579 if inlined: 

580 return 

581 func_name = f"({func_name})" 

582 self.text(func_name) 

583 # Build list of (label, value) pairs. Labels are "arg[i]" for positional 

584 # args, or the keyword name. Skip slices already commented at a higher level. 

585 all_args = [(f"arg[{i}]", v) for i, v in enumerate(args)] 

586 all_args += list(kwargs.items()) 

587 arg_slices = arg_slices or {} 

588 comments: dict[str, tuple[str, tuple[int, int]]] = {} 

589 for label, sr in arg_slices.items(): 

590 if sr in self.slice_comments and sr not in self._commented_slices: 

591 comments[label] = (self.slice_comments[sr], sr) 

592 

593 if leading_comment or any(k in comments for k, _ in all_args): 

594 # We have to split one arg per line in order to leave comments on them. 

595 force_split = True 

596 if force_split is None: 

597 # We're OK with printing this call on a single line, but will it fit? 

598 # If not, we'd rather fall back to one-argument-per-line instead. 

599 p = RepresentationPrinter() 

600 p.stack = self.stack.copy() 

601 p.known_object_printers = self.known_object_printers 

602 p.repr_call("_" * self.output_width, args, kwargs, force_split=False) 

603 s = p.getvalue() 

604 force_split = "\n" in s 

605 

606 with self.group(indent=4, open="(", close=""): 

607 for i, (label, v) in enumerate(all_args): 

608 if force_split: 

609 if i == 0 and leading_comment: 

610 self.break_() 

611 self.text(leading_comment) 

612 self.break_() 

613 else: 

614 assert leading_comment is None # only passed by top-level report 

615 self.breakable(" " if i else "") 

616 if not label.startswith("arg["): 

617 self.text(f"{label}=") 

618 # Mark slice as commented BEFORE printing value, so nested printers skip it 

619 entry = comments.get(label) 

620 if entry: 

621 self._commented_slices.add(entry[1]) 

622 if avoid_realization: 

623 self.text("<symbolic>") 

624 else: 

625 self.pretty(v) 

626 if force_split or i + 1 < len(all_args): 

627 self.text(",") 

628 if entry: 

629 self.text(f" # {entry[0]}") 

630 if all_args and force_split: 

631 self.break_() 

632 self.text(")") # after dedent 

633 

634 

635class Printable: 

636 def output(self, stream: TextIOBase, output_width: int) -> int: # pragma: no cover 

637 raise NotImplementedError 

638 

639 

640class Text(Printable): 

641 def __init__(self) -> None: 

642 self.objs: list[str] = [] 

643 self.width: int = 0 

644 

645 def output(self, stream: TextIOBase, output_width: int) -> int: 

646 for obj in self.objs: 

647 stream.write(obj) 

648 return output_width + self.width 

649 

650 def add(self, obj: str, width: int) -> None: 

651 self.objs.append(obj) 

652 self.width += width 

653 

654 

655class Breakable(Printable): 

656 def __init__(self, seq: str, width: int, pretty: RepresentationPrinter) -> None: 

657 self.obj = seq 

658 self.width = width 

659 self.pretty = pretty 

660 self.indentation = pretty.indentation 

661 self.group = pretty.group_stack[-1] 

662 self.group.breakables.append(self) 

663 

664 def output(self, stream: TextIOBase, output_width: int) -> int: 

665 self.group.breakables.popleft() 

666 if self.group.want_break: 

667 stream.write("\n" + " " * self.indentation) 

668 return self.indentation 

669 if not self.group.breakables: 

670 self.pretty.group_queue.remove(self.group) 

671 stream.write(self.obj) 

672 return output_width + self.width 

673 

674 

675class Group(Printable): 

676 def __init__(self, depth: int) -> None: 

677 self.depth = depth 

678 self.breakables: deque[Breakable] = deque() 

679 self.want_break: bool = False 

680 

681 

682class GroupQueue: 

683 def __init__(self, *groups: Group) -> None: 

684 self.queue: list[list[Group]] = [] 

685 for group in groups: 

686 self.enq(group) 

687 

688 def enq(self, group: Group) -> None: 

689 depth = group.depth 

690 while depth > len(self.queue) - 1: 

691 self.queue.append([]) 

692 self.queue[depth].append(group) 

693 

694 def deq(self) -> Group | None: 

695 for stack in self.queue: 

696 for idx, group in enumerate(reversed(stack)): 

697 if group.breakables: 

698 del stack[idx] 

699 group.want_break = True 

700 return group 

701 for group in stack: 

702 group.want_break = True 

703 del stack[:] 

704 return None 

705 

706 def remove(self, group: Group) -> None: 

707 try: 

708 self.queue[group.depth].remove(group) 

709 except ValueError: 

710 pass 

711 

712 

713def _seq_pprinter_factory(start: str, end: str, basetype: type) -> PrettyPrintFunction: 

714 """Factory that returns a pprint function useful for sequences. 

715 

716 Used by the default pprint for tuples, dicts, and lists. 

717 """ 

718 

719 def inner( 

720 obj: tuple[object] | list[object], p: RepresentationPrinter, cycle: bool 

721 ) -> None: 

722 typ = type(obj) 

723 if ( 

724 basetype is not None 

725 and typ is not basetype 

726 and typ.__repr__ != basetype.__repr__ # type: ignore[comparison-overlap] 

727 ): 

728 # If the subclass provides its own repr, use it instead. 

729 return p.text(typ.__repr__(obj)) 

730 

731 if cycle: 

732 return p.text(start + "..." + end) 

733 step = len(start) 

734 with p.group(step, start, end): 

735 for idx, x in p._enumerate(obj): 

736 if idx: 

737 p.text(",") 

738 p.breakable() 

739 p.pretty(x) 

740 if len(obj) == 1 and type(obj) is tuple: 

741 # Special case for 1-item tuples. 

742 p.text(",") 

743 

744 return inner 

745 

746 

747def get_class_name(cls: type[object]) -> str: 

748 class_name = _safe_getattr(cls, "__qualname__", cls.__name__) 

749 assert isinstance(class_name, str) 

750 return class_name 

751 

752 

753def _set_pprinter_factory( 

754 start: str, end: str, basetype: type[object] 

755) -> PrettyPrintFunction: 

756 """Factory that returns a pprint function useful for sets and 

757 frozensets.""" 

758 

759 def inner( 

760 obj: set[Any] | frozenset[Any], 

761 p: RepresentationPrinter, 

762 cycle: bool, 

763 ) -> None: 

764 typ = type(obj) 

765 if ( 

766 basetype is not None 

767 and typ is not basetype 

768 and typ.__repr__ != basetype.__repr__ 

769 ): 

770 # If the subclass provides its own repr, use it instead. 

771 return p.text(typ.__repr__(obj)) 

772 

773 if cycle: 

774 return p.text(start + "..." + end) 

775 if not obj: 

776 # Special case. 

777 p.text(get_class_name(basetype) + "()") 

778 else: 

779 step = len(start) 

780 with p.group(step, start, end): 

781 # Like dictionary keys, try to sort the items if there aren't too many 

782 items: Iterable[object] = obj 

783 if not (p.max_seq_length and len(obj) >= p.max_seq_length): 

784 try: 

785 items = sorted(obj) 

786 except Exception: 

787 # Sometimes the items don't sort. 

788 pass 

789 for idx, x in p._enumerate(items): 

790 if idx: 

791 p.text(",") 

792 p.breakable() 

793 p.pretty(x) 

794 

795 return inner 

796 

797 

798def _dict_pprinter_factory( 

799 start: str, end: str, basetype: type[object] | None = None 

800) -> PrettyPrintFunction: 

801 """Factory that returns a pprint function used by the default pprint of 

802 dicts and dict proxies.""" 

803 

804 def inner(obj: dict[object, object], p: RepresentationPrinter, cycle: bool) -> None: 

805 typ = type(obj) 

806 if ( 

807 basetype is not None 

808 and typ is not basetype 

809 and typ.__repr__ != basetype.__repr__ 

810 ): 

811 # If the subclass provides its own repr, use it instead. 

812 return p.text(typ.__repr__(obj)) 

813 

814 if cycle: 

815 return p.text("{...}") 

816 with ( 

817 p.group(1, start, end), 

818 # If the dict contains both "" and b"" (empty string and empty bytes), we 

819 # ignore the BytesWarning raised by `python -bb` mode. We can't use 

820 # `.items()` because it might be a non-`dict` type of mapping. 

821 warnings.catch_warnings(), 

822 ): 

823 warnings.simplefilter("ignore", BytesWarning) 

824 for idx, key in p._enumerate(obj): 

825 if idx: 

826 p.text(",") 

827 p.breakable() 

828 p.pretty(key) 

829 p.text(": ") 

830 p.pretty(obj[key]) 

831 

832 inner.__name__ = f"_dict_pprinter_factory({start!r}, {end!r}, {basetype!r})" 

833 return inner 

834 

835 

836def _super_pprint(obj: Any, p: RepresentationPrinter, cycle: bool) -> None: 

837 """The pprint for the super type.""" 

838 with p.group(8, "<super: ", ">"): 

839 p.pretty(obj.__thisclass__) 

840 p.text(",") 

841 p.breakable() 

842 p.pretty(obj.__self__) 

843 

844 

845def _re_pattern_pprint(obj: re.Pattern, p: RepresentationPrinter, cycle: bool) -> None: 

846 """The pprint function for regular expression patterns.""" 

847 p.text("re.compile(") 

848 pattern = repr(obj.pattern) 

849 if pattern[:1] in "uU": # pragma: no cover 

850 pattern = pattern[1:] 

851 prefix = "ur" 

852 else: 

853 prefix = "r" 

854 pattern = prefix + pattern.replace("\\\\", "\\") 

855 p.text(pattern) 

856 if obj.flags: 

857 p.text(",") 

858 p.breakable() 

859 done_one = False 

860 for flag in ( 

861 "TEMPLATE", 

862 "IGNORECASE", 

863 "LOCALE", 

864 "MULTILINE", 

865 "DOTALL", 

866 "UNICODE", 

867 "VERBOSE", 

868 "DEBUG", 

869 ): 

870 if obj.flags & getattr(re, flag, 0): 

871 if done_one: 

872 p.text("|") 

873 p.text("re." + flag) 

874 done_one = True 

875 p.text(")") 

876 

877 

878def _type_pprint(obj: type[object], p: RepresentationPrinter, cycle: bool) -> None: 

879 """The pprint for classes and types.""" 

880 # Heap allocated types might not have the module attribute, 

881 # and others may set it to None. 

882 

883 # Checks for a __repr__ override in the metaclass 

884 # != rather than is not because pypy compatibility 

885 if type(obj).__repr__ != type.__repr__: # type: ignore[comparison-overlap] 

886 _repr_pprint(obj, p, cycle) 

887 return 

888 

889 mod = _safe_getattr(obj, "__module__", None) 

890 try: 

891 name = obj.__qualname__ 

892 except Exception: # pragma: no cover 

893 name = obj.__name__ 

894 if not isinstance(name, str): 

895 name = "<unknown type>" 

896 

897 if mod in (None, "__builtin__", "builtins", "exceptions"): 

898 p.text(name) 

899 else: 

900 p.text(mod + "." + name) 

901 

902 

903def _repr_pprint(obj: object, p: RepresentationPrinter, cycle: bool) -> None: 

904 """A pprint that just redirects to the normal repr function.""" 

905 # Find newlines and replace them with p.break_() 

906 output = repr(obj) 

907 for idx, output_line in enumerate(output.splitlines()): 

908 if idx: 

909 p.break_() 

910 p.text(output_line) 

911 

912 

913def pprint_fields( 

914 obj: object, p: RepresentationPrinter, cycle: bool, fields: Iterable[str] 

915) -> None: 

916 name = get_class_name(obj.__class__) 

917 if cycle: 

918 return p.text(f"{name}(...)") 

919 with p.group(1, name + "(", ")"): 

920 for idx, field in enumerate(fields): 

921 if idx: 

922 p.text(",") 

923 p.breakable() 

924 p.text(field) 

925 p.text("=") 

926 p.pretty(getattr(obj, field)) 

927 

928 

929def _get_slice_comment( 

930 p: RepresentationPrinter, 

931 arg_labels: ArgLabelsT, 

932 key: Any, 

933) -> tuple[str, tuple[int, int]] | None: 

934 """Look up a comment for a slice, if not already printed at a higher level.""" 

935 if (sr := arg_labels.get(key)) and sr in p.slice_comments: 

936 if sr not in p._commented_slices: 

937 return (p.slice_comments[sr], sr) 

938 return None 

939 

940 

941def _tuple_pprinter(arg_labels: ArgLabelsT) -> PrettyPrintFunction: 

942 """Pretty printer for tuples that shows sub-argument comments.""" 

943 

944 def inner(obj: tuple, p: RepresentationPrinter, cycle: bool) -> None: 

945 if cycle: 

946 return p.text("(...)") 

947 

948 get = lambda i: _get_slice_comment(p, arg_labels, f"arg[{i}]") 

949 has_comments = any(get(i) for i in range(len(obj))) 

950 

951 with p.group(indent=4, open="(", close=""): 

952 for idx, x in p._enumerate(obj): 

953 p.break_() if has_comments else (p.breakable() if idx else None) 

954 p.pretty(x) 

955 if has_comments or idx + 1 < len(obj) or len(obj) == 1: 

956 p.text(",") 

957 if entry := get(idx): 

958 p._commented_slices.add(entry[1]) 

959 p.text(f" # {entry[0]}") 

960 if has_comments and obj: 

961 p.break_() 

962 p.text(")") 

963 

964 return inner 

965 

966 

967def _fixeddict_pprinter( 

968 arg_labels: ArgLabelsT, 

969 mapping: dict[Any, Any], 

970) -> PrettyPrintFunction: 

971 """Pretty printer for fixed_dictionaries that shows sub-argument comments.""" 

972 

973 def inner(obj: dict, p: RepresentationPrinter, cycle: bool) -> None: 

974 if cycle: 

975 return p.text("{...}") 

976 

977 get = lambda k: _get_slice_comment(p, arg_labels, k) 

978 # Preserve mapping key order, then any optional keys (deduped) 

979 keys = list(dict.fromkeys(k for k in [*mapping, *obj] if k in obj)) 

980 has_comments = any(get(k) for k in keys) 

981 

982 with p.group(indent=4, open="{", close=""): 

983 for idx, key in p._enumerate(keys): 

984 p.break_() if has_comments else (p.breakable() if idx else None) 

985 p.pretty(key) 

986 p.text(": ") 

987 p.pretty(obj[key]) 

988 if has_comments or idx + 1 < len(keys): 

989 p.text(",") 

990 if entry := get(key): 

991 p._commented_slices.add(entry[1]) 

992 p.text(f" # {entry[0]}") 

993 if has_comments and obj: 

994 p.break_() 

995 p.text("}") 

996 

997 return inner 

998 

999 

1000def _function_pprint( 

1001 obj: types.FunctionType | types.BuiltinFunctionType | types.MethodType, 

1002 p: RepresentationPrinter, 

1003 cycle: bool, 

1004) -> None: 

1005 """Base pprint for all functions and builtin functions.""" 

1006 from hypothesis.internal.reflection import get_pretty_function_description 

1007 

1008 p.text(get_pretty_function_description(obj)) 

1009 

1010 

1011def _exception_pprint( 

1012 obj: BaseException, p: RepresentationPrinter, cycle: bool 

1013) -> None: 

1014 """Base pprint for all exceptions.""" 

1015 name = getattr(obj.__class__, "__qualname__", obj.__class__.__name__) 

1016 if obj.__class__.__module__ not in ("exceptions", "builtins"): 

1017 name = f"{obj.__class__.__module__}.{name}" 

1018 step = len(name) + 1 

1019 with p.group(step, name + "(", ")"): 

1020 for idx, arg in enumerate(getattr(obj, "args", ())): 

1021 if idx: 

1022 p.text(",") 

1023 p.breakable() 

1024 p.pretty(arg) 

1025 

1026 

1027def _repr_integer(obj: int, p: RepresentationPrinter, cycle: bool) -> None: 

1028 if abs(obj) < 1_000_000_000: 

1029 p.text(repr(obj)) 

1030 elif abs(obj) < 10**640: 

1031 # add underscores for integers over ten decimal digits 

1032 p.text(f"{obj:#_d}") 

1033 else: 

1034 # for very very large integers, use hex because power-of-two bases are cheaper 

1035 # https://docs.python.org/3/library/stdtypes.html#integer-string-conversion-length-limitation 

1036 p.text(f"{obj:#_x}") 

1037 

1038 

1039def _repr_float_counting_nans( 

1040 obj: float, p: RepresentationPrinter, cycle: bool 

1041) -> None: 

1042 if isnan(obj): 

1043 if struct.pack("!d", abs(obj)) != struct.pack("!d", float("nan")): 

1044 show = hex(*struct.unpack("Q", struct.pack("d", obj))) 

1045 return p.text(f"struct.unpack('d', struct.pack('Q', {show}))[0]") 

1046 elif copysign(1.0, obj) == -1.0: 

1047 return p.text("-nan") 

1048 p.text(repr(obj)) 

1049 

1050 

1051#: printers for builtin types 

1052_type_pprinters: dict[type, PrettyPrintFunction] = { 

1053 int: _repr_integer, 

1054 float: _repr_float_counting_nans, 

1055 str: _repr_pprint, 

1056 tuple: _seq_pprinter_factory("(", ")", tuple), 

1057 list: _seq_pprinter_factory("[", "]", list), 

1058 dict: _dict_pprinter_factory("{", "}", dict), 

1059 set: _set_pprinter_factory("{", "}", set), 

1060 frozenset: _set_pprinter_factory("frozenset({", "})", frozenset), 

1061 super: _super_pprint, 

1062 re.Pattern: _re_pattern_pprint, 

1063 type: _type_pprint, 

1064 types.FunctionType: _function_pprint, 

1065 types.BuiltinFunctionType: _function_pprint, 

1066 types.MethodType: _function_pprint, 

1067 datetime.datetime: _repr_pprint, 

1068 datetime.timedelta: _repr_pprint, 

1069 BaseException: _exception_pprint, 

1070 slice: _repr_pprint, 

1071 range: _repr_pprint, 

1072 bytes: _repr_pprint, 

1073} 

1074 

1075#: printers for types specified by name 

1076_deferred_type_pprinters: dict[tuple[str, str], PrettyPrintFunction] = {} 

1077 

1078 

1079def for_type_by_name( 

1080 type_module: str, type_name: str, func: PrettyPrintFunction 

1081) -> PrettyPrintFunction | None: 

1082 """Add a pretty printer for a type specified by the module and name of a 

1083 type rather than the type object itself.""" 

1084 key = (type_module, type_name) 

1085 oldfunc = _deferred_type_pprinters.get(key) 

1086 _deferred_type_pprinters[key] = func 

1087 return oldfunc 

1088 

1089 

1090#: printers for the default singletons 

1091_singleton_pprinters: dict[int, PrettyPrintFunction] = dict.fromkeys( 

1092 map(id, [None, True, False, Ellipsis, NotImplemented]), _repr_pprint 

1093) 

1094 

1095 

1096def _defaultdict_pprint( 

1097 obj: defaultdict[object, object], p: RepresentationPrinter, cycle: bool 

1098) -> None: 

1099 name = obj.__class__.__name__ 

1100 with p.group(len(name) + 1, name + "(", ")"): 

1101 if cycle: 

1102 p.text("...") 

1103 else: 

1104 p.pretty(obj.default_factory) 

1105 p.text(",") 

1106 p.breakable() 

1107 p.pretty(dict(obj)) 

1108 

1109 

1110def _ordereddict_pprint( 

1111 obj: OrderedDict[object, object], p: RepresentationPrinter, cycle: bool 

1112) -> None: 

1113 name = obj.__class__.__name__ 

1114 with p.group(len(name) + 1, name + "(", ")"): 

1115 if cycle: 

1116 p.text("...") 

1117 elif obj: 

1118 p.pretty(list(obj.items())) 

1119 

1120 

1121def _deque_pprint(obj: deque[object], p: RepresentationPrinter, cycle: bool) -> None: 

1122 name = obj.__class__.__name__ 

1123 with p.group(len(name) + 1, name + "(", ")"): 

1124 if cycle: 

1125 p.text("...") 

1126 else: 

1127 p.pretty(list(obj)) 

1128 

1129 

1130def _counter_pprint( 

1131 obj: Counter[object], p: RepresentationPrinter, cycle: bool 

1132) -> None: 

1133 name = obj.__class__.__name__ 

1134 with p.group(len(name) + 1, name + "(", ")"): 

1135 if cycle: 

1136 p.text("...") 

1137 elif obj: 

1138 p.pretty(dict(obj)) 

1139 

1140 

1141def _repr_dataframe( 

1142 obj: object, p: RepresentationPrinter, cycle: bool 

1143) -> None: # pragma: no cover 

1144 with p.indent(4): 

1145 p.break_() 

1146 _repr_pprint(obj, p, cycle) 

1147 p.break_() 

1148 

1149 

1150def _repr_enum(obj: Enum, p: RepresentationPrinter, cycle: bool) -> None: 

1151 tname = get_class_name(type(obj)) 

1152 if isinstance(obj, Flag): 

1153 p.text( 

1154 " | ".join(f"{tname}.{x.name}" for x in type(obj) if x & obj == x) 

1155 or f"{tname}({obj.value!r})" # if no matching members 

1156 ) 

1157 else: 

1158 p.text(f"{tname}.{obj.name}") 

1159 

1160 

1161class _ReprDots: 

1162 def __repr__(self) -> str: 

1163 return "..." 

1164 

1165 

1166def _repr_partial(obj: partial[Any], p: RepresentationPrinter, cycle: bool) -> None: 

1167 args, kw = obj.args, obj.keywords 

1168 if cycle: 

1169 args, kw = (_ReprDots(),), {} 

1170 p.repr_call(pretty(type(obj)), (obj.func, *args), kw) 

1171 

1172 

1173for_type_by_name("collections", "defaultdict", _defaultdict_pprint) 

1174for_type_by_name("collections", "OrderedDict", _ordereddict_pprint) 

1175for_type_by_name("ordereddict", "OrderedDict", _ordereddict_pprint) 

1176for_type_by_name("collections", "deque", _deque_pprint) 

1177for_type_by_name("collections", "Counter", _counter_pprint) 

1178for_type_by_name("pandas.core.frame", "DataFrame", _repr_dataframe) 

1179for_type_by_name("enum", "Enum", _repr_enum) 

1180for_type_by_name("functools", "partial", _repr_partial)