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

625 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, Collection, 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] 

86# Maps each argument label ("arg[i]" or a keyword) to the index of the span 

87# created by drawing that argument. 

88ArgLabelsT: TypeAlias = dict[str, int] 

89 

90__all__ = [ 

91 "IDKey", 

92 "RepresentationPrinter", 

93 "_fixeddict_pprinter", 

94 "_tuple_pprinter", 

95 "pretty", 

96] 

97 

98 

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

100 """Safe version of getattr. 

101 

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

103 rather than raising. 

104 

105 """ 

106 try: 

107 return getattr(obj, attr, default) 

108 except Exception: 

109 return default 

110 

111 

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

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

114 printer = RepresentationPrinter() 

115 printer.pretty(obj, cycle=cycle) 

116 return printer.getvalue() 

117 

118 

119class IDKey: 

120 def __init__(self, value: object): 

121 self.value = value 

122 

123 def __hash__(self) -> int: 

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

125 

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

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

128 

129 

130def _try_inline_lambda( 

131 func_name: str, 

132 args: Sequence[object], 

133 kwargs: dict[str, object], 

134 printer: "RepresentationPrinter", 

135) -> bool: 

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

137 

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

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

140 by substituting the argument repr into the AST. 

141 

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

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

144 """ 

145 try: 

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

147 except Exception: 

148 return False 

149 lam = tree.body 

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

151 return False 

152 

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

154 params = lam.args 

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

156 return False 

157 

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

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

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

161 n_defaults = len(params.defaults) 

162 has_default = ( 

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

164 ) 

165 

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

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

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

169 return False 

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

171 return False 

172 

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

174 for i, name in enumerate(param_names): 

175 if i < len(args): 

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

177 elif name in kwargs: 

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

179 elif name in has_default: 

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

181 else: 

182 return False 

183 

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

185 for repr_str in arg_reprs.values(): 

186 try: 

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

188 except Exception: 

189 return False 

190 

191 use_counts = dict.fromkeys(param_names, 0) 

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

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

194 use_counts[node.id] += 1 

195 

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

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

198 return False 

199 

200 # Substitute argument reprs into the body AST 

201 class _Inliner(ast.NodeTransformer): 

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

203 if node.id in arg_reprs: 

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

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

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

207 return ast.copy_location(replacement, node) 

208 return node 

209 

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

211 ast.fix_missing_locations(new_body) 

212 

213 try: 

214 result = ast.unparse(new_body) 

215 except Exception: 

216 return False 

217 

218 printer.text(result) 

219 return True 

220 

221 

222class RepresentationPrinter: 

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

224 printer for a python object. 

225 

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

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

228 reinstantiate it. 

229 

230 """ 

231 

232 def __init__( 

233 self, 

234 output: TextIOBase | None = None, 

235 *, 

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

237 ) -> None: 

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

239 

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

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

242 minimal failing test case can vary without changing the test result. 

243 """ 

244 self.broken: bool = False 

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

246 self.max_width: int = 79 

247 self.max_seq_length: int = 1000 

248 self.output_width: int = 0 

249 self.buffer_width: int = 0 

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

251 

252 root_group = Group(0) 

253 self.group_stack = [root_group] 

254 self.group_queue = GroupQueue(root_group) 

255 self.indentation: int = 0 

256 

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

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

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

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

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

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

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

264 self.singleton_pprinters.update(ipp._singleton_pprinters) 

265 self.type_pprinters.update(ipp._type_pprinters) 

266 self.deferred_pprinters.update(ipp._deferred_type_pprinters) 

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

268 self.singleton_pprinters.update(_singleton_pprinters) 

269 self.type_pprinters.update(_type_pprinters) 

270 self.deferred_pprinters.update(_deferred_type_pprinters) 

271 

272 # for which-parts-matter, we track comments keyed by the index of the 

273 # span each argument's draw created in the minimal failing test case; 

274 # this is per-interesting_origin but we report each separately so 

275 # that's someone else's problem here. Invocations of self.repr_call() 

276 # can report the span for each argument, which will then be used to 

277 # look up the relevant comment if any. (The None key holds the 

278 # whole-test comment, which is looked up directly, not per-argument.) 

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

280 self.span_comments: dict[int | None, str] 

281 if context is None: 

282 self.known_object_printers = defaultdict(list) 

283 self.span_comments = {} 

284 else: 

285 self.known_object_printers = context.known_object_printers 

286 self.span_comments = context.data.span_comments 

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

288 # Track which spans we've already printed comments for, to avoid 

289 # duplicating comments when nested objects share the same span. 

290 self._commented_spans: set[int] = set() 

291 

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

293 """Pretty print the given object.""" 

294 

295 obj_id = id(obj) 

296 cycle = cycle or obj_id in self.stack 

297 self.stack.append(obj_id) 

298 try: 

299 with self.group(): 

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

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

302 try: 

303 printer = self.singleton_pprinters[obj_id] 

304 except (TypeError, KeyError): 

305 pass 

306 else: 

307 return printer(obj, self, cycle) 

308 

309 # Look for the _repr_pretty_ method which allows users 

310 # to define custom pretty printing. 

311 # Some objects automatically create any requested 

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

313 # callability. 

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

315 if callable(pretty_method): 

316 return pretty_method(self, cycle) 

317 

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

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

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

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

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

323 if len(printers) == 1: 

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

325 if printers: 

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

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

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

329 strs = set() 

330 for f in printers: 

331 p = RepresentationPrinter() 

332 f(obj, p, cycle) 

333 strs.add(p.getvalue()) 

334 if len(strs) == 1: 

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

336 

337 # Next walk the mro and check for either: 

338 # 1) a registered printer 

339 # 2) a _repr_pretty_ method 

340 for cls in obj_class.__mro__: 

341 if cls in self.type_pprinters: 

342 # printer registered in self.type_pprinters 

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

344 else: 

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

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

347 key = ( 

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

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

350 ) 

351 if key in self.deferred_pprinters: 

352 # Move the printer over to the regular registry. 

353 printer = self.deferred_pprinters.pop(key) 

354 self.type_pprinters[cls] = printer 

355 return printer(obj, self, cycle) 

356 else: 

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

358 return pprint_fields( 

359 obj, 

360 self, 

361 cycle, 

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

363 ) 

364 if hasattr(cls, "__dataclass_fields__"): 

365 return pprint_fields( 

366 obj, 

367 self, 

368 cycle, 

369 [ 

370 k 

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

372 if v.init 

373 ], 

374 ) 

375 

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

377 return _repr_pprint(obj, self, cycle) 

378 finally: 

379 self.stack.pop() 

380 

381 def _break_outer_groups(self) -> None: 

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

383 group = self.group_queue.deq() 

384 if not group: 

385 return 

386 while group.breakables: 

387 x = self.buffer.popleft() 

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

389 self.buffer_width -= x.width 

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

391 x = self.buffer.popleft() 

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

393 self.buffer_width -= x.width 

394 

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

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

397 width = len(obj) 

398 if self.buffer: 

399 text = self.buffer[-1] 

400 if not isinstance(text, Text): 

401 text = Text() 

402 self.buffer.append(text) 

403 text.add(obj, width) 

404 self.buffer_width += width 

405 self._break_outer_groups() 

406 else: 

407 self.output.write(obj) 

408 self.output_width += width 

409 

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

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

412 

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

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

415 which default to one space. 

416 

417 """ 

418 width = len(sep) 

419 group = self.group_stack[-1] 

420 if group.want_break: 

421 self.flush() 

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

423 self.output_width = self.indentation 

424 self.buffer_width = 0 

425 else: 

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

427 self.buffer_width += width 

428 self._break_outer_groups() 

429 

430 def break_(self) -> None: 

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

432 indentation.""" 

433 self.flush() 

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

435 self.output_width = self.indentation 

436 self.buffer_width = 0 

437 

438 @contextmanager 

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

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

441 self.indentation += indent 

442 try: 

443 yield 

444 finally: 

445 self.indentation -= indent 

446 

447 @contextmanager 

448 def group( 

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

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

451 """Context manager for an indented group. 

452 

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

454 

455 The first parameter specifies the indentation for the next line 

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

457 opening and closing delimiters. 

458 """ 

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

460 try: 

461 yield 

462 finally: 

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

464 

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

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

467 

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

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

470 """ 

471 if open: 

472 self.text(open) 

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

474 self.group_stack.append(group) 

475 self.group_queue.enq(group) 

476 self.indentation += indent 

477 

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

479 """See begin_group().""" 

480 self.indentation -= dedent 

481 group = self.group_stack.pop() 

482 if not group.breakables: 

483 self.group_queue.remove(group) 

484 if close: 

485 self.text(close) 

486 

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

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

489 for idx, x in enumerate(seq): 

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

491 self.text(",") 

492 self.breakable() 

493 self.text("...") 

494 return 

495 yield idx, x 

496 

497 def flush(self) -> None: 

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

499 for data in self.buffer: 

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

501 self.buffer.clear() 

502 self.buffer_width = 0 

503 

504 def getvalue(self) -> str: 

505 assert isinstance(self.output, StringIO) 

506 self.flush() 

507 return self.output.getvalue() 

508 

509 def maybe_repr_known_object_as_call( 

510 self, 

511 obj: object, 

512 cycle: bool, 

513 name: str, 

514 args: Sequence[object], 

515 kwargs: dict[str, object], 

516 arg_labels: ArgLabelsT | None = None, 

517 ) -> None: 

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

519 # otherwise pprint as repr. 

520 # Rules: 

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

522 # 2. Prefer valid syntax to invalid syntax. 

523 # 3. Prefer shorter expressions. 

524 if cycle: 

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

526 # Look up comments from span_comments if we have arg_labels 

527 comments = {} 

528 if arg_labels is not None: 

529 for key, span_index in arg_labels.items(): 

530 if span_index in self.span_comments: 

531 comments[key] = self.span_comments[span_index] 

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

533 if not comments: 

534 with suppress(Exception): 

535 # Check whether the repr is valid syntax: 

536 ast.parse(repr(obj)) 

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

538 p = RepresentationPrinter() 

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

540 p.known_object_printers = self.known_object_printers 

541 p.repr_call(name, args, kwargs) 

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

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

544 return _repr_pprint(obj, self, cycle) 

545 try: 

546 ast.parse(p.getvalue()) 

547 except Exception: 

548 return _repr_pprint(obj, self, cycle) 

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

550 

551 def repr_call( 

552 self, 

553 func_name: str, 

554 args: Sequence[object], 

555 kwargs: dict[str, object], 

556 *, 

557 force_split: bool | None = None, 

558 arg_labels: ArgLabelsT | None = None, 

559 leading_comment: str | None = None, 

560 avoid_realization: bool = False, 

561 known_safe_to_repr: Collection[str] = (), 

562 ) -> None: 

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

564 

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

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

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

568 - arg_labels is a mapping from pos-idx or keyword to the index of the 

569 span created by drawing that argument, by which we can look up 

570 comments to add. 

571 """ 

572 assert isinstance(func_name, str) 

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

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

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

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

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

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

579 has_comments = arg_labels and any( 

580 span_index in self.span_comments 

581 and span_index not in self._commented_spans 

582 for span_index in arg_labels.values() 

583 ) 

584 if not has_comments: 

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

586 if inlined: 

587 return 

588 func_name = f"({func_name})" 

589 self.text(func_name) 

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

591 # args, or the keyword name. Skip spans already commented at a higher level. 

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

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

594 arg_labels = arg_labels or {} 

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

596 for label, span_index in arg_labels.items(): 

597 if ( 

598 span_index in self.span_comments 

599 and span_index not in self._commented_spans 

600 ): 

601 comments[label] = (self.span_comments[span_index], span_index) 

602 

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

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

605 force_split = True 

606 if force_split is None: 

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

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

609 p = RepresentationPrinter() 

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

611 p.known_object_printers = self.known_object_printers 

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

613 s = p.getvalue() 

614 force_split = "\n" in s 

615 

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

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

618 if force_split: 

619 if i == 0 and leading_comment: 

620 self.break_() 

621 self.text(leading_comment) 

622 self.break_() 

623 else: 

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

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

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

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

628 # Mark span as commented BEFORE printing value, so nested printers skip it 

629 entry = comments.get(label) 

630 if entry: 

631 self._commented_spans.add(entry[1]) 

632 if avoid_realization and label not in known_safe_to_repr: 

633 self.text("<symbolic>") 

634 else: 

635 self.pretty(v) 

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

637 self.text(",") 

638 if entry: 

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

640 if all_args and force_split: 

641 self.break_() 

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

643 

644 

645class Printable: 

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

647 raise NotImplementedError 

648 

649 

650class Text(Printable): 

651 def __init__(self) -> None: 

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

653 self.width: int = 0 

654 

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

656 for obj in self.objs: 

657 stream.write(obj) 

658 return output_width + self.width 

659 

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

661 self.objs.append(obj) 

662 self.width += width 

663 

664 

665class Breakable(Printable): 

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

667 self.obj = seq 

668 self.width = width 

669 self.pretty = pretty 

670 self.indentation = pretty.indentation 

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

672 self.group.breakables.append(self) 

673 

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

675 self.group.breakables.popleft() 

676 if self.group.want_break: 

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

678 return self.indentation 

679 if not self.group.breakables: 

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

681 stream.write(self.obj) 

682 return output_width + self.width 

683 

684 

685class Group(Printable): 

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

687 self.depth = depth 

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

689 self.want_break: bool = False 

690 

691 

692class GroupQueue: 

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

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

695 for group in groups: 

696 self.enq(group) 

697 

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

699 depth = group.depth 

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

701 self.queue.append([]) 

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

703 

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

705 for stack in self.queue: 

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

707 if group.breakables: 

708 del stack[idx] 

709 group.want_break = True 

710 return group 

711 for group in stack: 

712 group.want_break = True 

713 del stack[:] 

714 return None 

715 

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

717 try: 

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

719 except ValueError: 

720 pass 

721 

722 

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

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

725 

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

727 """ 

728 

729 def inner( 

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

731 ) -> None: 

732 typ = type(obj) 

733 if ( 

734 basetype is not None 

735 and typ is not basetype 

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

737 ): 

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

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

740 

741 if cycle: 

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

743 step = len(start) 

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

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

746 if idx: 

747 p.text(",") 

748 p.breakable() 

749 p.pretty(x) 

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

751 # Special case for 1-item tuples. 

752 p.text(",") 

753 

754 return inner 

755 

756 

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

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

759 assert isinstance(class_name, str) 

760 return class_name 

761 

762 

763def _set_pprinter_factory( 

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

765) -> PrettyPrintFunction: 

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

767 frozensets.""" 

768 

769 def inner( 

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

771 p: RepresentationPrinter, 

772 cycle: bool, 

773 ) -> None: 

774 typ = type(obj) 

775 if ( 

776 basetype is not None 

777 and typ is not basetype 

778 and typ.__repr__ != basetype.__repr__ 

779 ): 

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

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

782 

783 if cycle: 

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

785 if not obj: 

786 # Special case. 

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

788 else: 

789 step = len(start) 

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

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

792 items: Iterable[object] = obj 

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

794 try: 

795 items = sorted(obj) 

796 except Exception: 

797 # Sometimes the items don't sort. 

798 pass 

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

800 if idx: 

801 p.text(",") 

802 p.breakable() 

803 p.pretty(x) 

804 

805 return inner 

806 

807 

808def _dict_pprinter_factory( 

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

810) -> PrettyPrintFunction: 

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

812 dicts and dict proxies.""" 

813 

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

815 typ = type(obj) 

816 if ( 

817 basetype is not None 

818 and typ is not basetype 

819 and typ.__repr__ != basetype.__repr__ 

820 ): 

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

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

823 

824 if cycle: 

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

826 with ( 

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

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

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

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

831 warnings.catch_warnings(), 

832 ): 

833 warnings.simplefilter("ignore", BytesWarning) 

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

835 if idx: 

836 p.text(",") 

837 p.breakable() 

838 p.pretty(key) 

839 p.text(": ") 

840 p.pretty(obj[key]) 

841 

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

843 return inner 

844 

845 

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

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

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

849 p.pretty(obj.__thisclass__) 

850 p.text(",") 

851 p.breakable() 

852 p.pretty(obj.__self__) 

853 

854 

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

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

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

858 pattern = repr(obj.pattern) 

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

860 pattern = pattern[1:] 

861 prefix = "ur" 

862 else: 

863 prefix = "r" 

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

865 p.text(pattern) 

866 if obj.flags: 

867 p.text(",") 

868 p.breakable() 

869 done_one = False 

870 for flag in ( 

871 "TEMPLATE", 

872 "IGNORECASE", 

873 "LOCALE", 

874 "MULTILINE", 

875 "DOTALL", 

876 "UNICODE", 

877 "VERBOSE", 

878 "DEBUG", 

879 ): 

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

881 if done_one: 

882 p.text("|") 

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

884 done_one = True 

885 p.text(")") 

886 

887 

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

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

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

891 # and others may set it to None. 

892 

893 # Checks for a __repr__ override in the metaclass 

894 # != rather than is not because pypy compatibility 

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

896 _repr_pprint(obj, p, cycle) 

897 return 

898 

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

900 try: 

901 name = obj.__qualname__ 

902 except Exception: # pragma: no cover 

903 name = obj.__name__ 

904 if not isinstance(name, str): 

905 name = "<unknown type>" 

906 

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

908 p.text(name) 

909 else: 

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

911 

912 

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

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

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

916 output = repr(obj) 

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

918 if idx: 

919 p.break_() 

920 p.text(output_line) 

921 

922 

923def pprint_fields( 

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

925) -> None: 

926 name = get_class_name(obj.__class__) 

927 if cycle: 

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

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

930 for idx, field in enumerate(fields): 

931 if idx: 

932 p.text(",") 

933 p.breakable() 

934 p.text(field) 

935 p.text("=") 

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

937 

938 

939def _get_span_comment( 

940 p: RepresentationPrinter, 

941 arg_labels: ArgLabelsT, 

942 key: Any, 

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

944 """Look up a comment for a span, if not already printed at a higher level.""" 

945 span_index = arg_labels.get(key) 

946 if span_index is not None and span_index in p.span_comments: 

947 if span_index not in p._commented_spans: 

948 return (p.span_comments[span_index], span_index) 

949 return None 

950 

951 

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

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

954 

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

956 if cycle: 

957 return p.text("(...)") 

958 

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

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

961 

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

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

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

965 p.pretty(x) 

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

967 p.text(",") 

968 if entry := get(idx): 

969 p._commented_spans.add(entry[1]) 

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

971 if has_comments and obj: 

972 p.break_() 

973 p.text(")") 

974 

975 return inner 

976 

977 

978def _fixeddict_pprinter(arg_labels: ArgLabelsT) -> PrettyPrintFunction: 

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

980 

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

982 if cycle: 

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

984 

985 get = lambda k: _get_span_comment(p, arg_labels, k) 

986 # Print in the dict's actual (possibly permuted) iteration order. 

987 keys = list(obj) 

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

989 

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

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

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

993 p.pretty(key) 

994 p.text(": ") 

995 p.pretty(obj[key]) 

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

997 p.text(",") 

998 if entry := get(key): 

999 p._commented_spans.add(entry[1]) 

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

1001 if has_comments and obj: 

1002 p.break_() 

1003 p.text("}") 

1004 

1005 return inner 

1006 

1007 

1008def _function_pprint( 

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

1010 p: RepresentationPrinter, 

1011 cycle: bool, 

1012) -> None: 

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

1014 from hypothesis.internal.reflection import get_pretty_function_description 

1015 

1016 p.text(get_pretty_function_description(obj)) 

1017 

1018 

1019def _exception_pprint( 

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

1021) -> None: 

1022 """Base pprint for all exceptions.""" 

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

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

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

1026 step = len(name) + 1 

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

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

1029 if idx: 

1030 p.text(",") 

1031 p.breakable() 

1032 p.pretty(arg) 

1033 

1034 

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

1036 if abs(obj) < 1_000_000_000: 

1037 p.text(repr(obj)) 

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

1039 # add underscores for integers over ten decimal digits 

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

1041 else: 

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

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

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

1045 

1046 

1047def _repr_float_counting_nans( 

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

1049) -> None: 

1050 if isnan(obj): 

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

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

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

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

1055 return p.text("-nan") 

1056 p.text(repr(obj)) 

1057 

1058 

1059#: printers for builtin types 

1060_type_pprinters: dict[type, PrettyPrintFunction] = { 

1061 int: _repr_integer, 

1062 float: _repr_float_counting_nans, 

1063 str: _repr_pprint, 

1064 tuple: _seq_pprinter_factory("(", ")", tuple), 

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

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

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

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

1069 super: _super_pprint, 

1070 re.Pattern: _re_pattern_pprint, 

1071 type: _type_pprint, 

1072 types.FunctionType: _function_pprint, 

1073 types.BuiltinFunctionType: _function_pprint, 

1074 types.MethodType: _function_pprint, 

1075 datetime.datetime: _repr_pprint, 

1076 datetime.timedelta: _repr_pprint, 

1077 BaseException: _exception_pprint, 

1078 slice: _repr_pprint, 

1079 range: _repr_pprint, 

1080 bytes: _repr_pprint, 

1081} 

1082 

1083#: printers for types specified by name 

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

1085 

1086 

1087def for_type_by_name( 

1088 type_module: str, type_name: str, func: PrettyPrintFunction 

1089) -> PrettyPrintFunction | None: 

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

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

1092 key = (type_module, type_name) 

1093 oldfunc = _deferred_type_pprinters.get(key) 

1094 _deferred_type_pprinters[key] = func 

1095 return oldfunc 

1096 

1097 

1098#: printers for the default singletons 

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

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

1101) 

1102 

1103 

1104def _defaultdict_pprint( 

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

1106) -> None: 

1107 name = obj.__class__.__name__ 

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

1109 if cycle: 

1110 p.text("...") 

1111 else: 

1112 p.pretty(obj.default_factory) 

1113 p.text(",") 

1114 p.breakable() 

1115 p.pretty(dict(obj)) 

1116 

1117 

1118def _ordereddict_pprint( 

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

1120) -> None: 

1121 name = obj.__class__.__name__ 

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

1123 if cycle: 

1124 p.text("...") 

1125 elif obj: 

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

1127 

1128 

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

1130 name = obj.__class__.__name__ 

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

1132 if cycle: 

1133 p.text("...") 

1134 else: 

1135 p.pretty(list(obj)) 

1136 

1137 

1138def _counter_pprint( 

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

1140) -> None: 

1141 name = obj.__class__.__name__ 

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

1143 if cycle: 

1144 p.text("...") 

1145 elif obj: 

1146 p.pretty(dict(obj)) 

1147 

1148 

1149def _repr_dataframe( 

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

1151) -> None: # pragma: no cover 

1152 with p.indent(4): 

1153 p.break_() 

1154 _repr_pprint(obj, p, cycle) 

1155 p.break_() 

1156 

1157 

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

1159 tname = get_class_name(type(obj)) 

1160 if isinstance(obj, Flag): 

1161 p.text( 

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

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

1164 ) 

1165 else: 

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

1167 

1168 

1169class _ReprDots: 

1170 def __repr__(self) -> str: 

1171 return "..." 

1172 

1173 

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

1175 args, kw = obj.args, obj.keywords 

1176 if cycle: 

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

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

1179 

1180 

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

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

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

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

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

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

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

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