Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/assertion/truncate.py: 22%
49 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
1"""Utilities for truncating assertion output.
3Current default behaviour is to truncate assertion explanations at
4~8 terminal lines, unless running in "-vv" mode or running on CI.
5"""
6from typing import List
7from typing import Optional
9from _pytest.assertion import util
10from _pytest.nodes import Item
13DEFAULT_MAX_LINES = 8
14DEFAULT_MAX_CHARS = 8 * 80
15USAGE_MSG = "use '-vv' to show"
18def truncate_if_required(
19 explanation: List[str], item: Item, max_length: Optional[int] = None
20) -> List[str]:
21 """Truncate this assertion explanation if the given test item is eligible."""
22 if _should_truncate_item(item):
23 return _truncate_explanation(explanation)
24 return explanation
27def _should_truncate_item(item: Item) -> bool:
28 """Whether or not this test item is eligible for truncation."""
29 verbose = item.config.option.verbose
30 return verbose < 2 and not util.running_on_ci()
33def _truncate_explanation(
34 input_lines: List[str],
35 max_lines: Optional[int] = None,
36 max_chars: Optional[int] = None,
37) -> List[str]:
38 """Truncate given list of strings that makes up the assertion explanation.
40 Truncates to either 8 lines, or 640 characters - whichever the input reaches
41 first. The remaining lines will be replaced by a usage message.
42 """
44 if max_lines is None:
45 max_lines = DEFAULT_MAX_LINES
46 if max_chars is None:
47 max_chars = DEFAULT_MAX_CHARS
49 # Check if truncation required
50 input_char_count = len("".join(input_lines))
51 if len(input_lines) <= max_lines and input_char_count <= max_chars:
52 return input_lines
54 # Truncate first to max_lines, and then truncate to max_chars if max_chars
55 # is exceeded.
56 truncated_explanation = input_lines[:max_lines]
57 truncated_explanation = _truncate_by_char_count(truncated_explanation, max_chars)
59 # Add ellipsis to final line
60 truncated_explanation[-1] = truncated_explanation[-1] + "..."
62 # Append useful message to explanation
63 truncated_line_count = len(input_lines) - len(truncated_explanation)
64 truncated_line_count += 1 # Account for the part-truncated final line
65 msg = "...Full output truncated"
66 if truncated_line_count == 1:
67 msg += f" ({truncated_line_count} line hidden)"
68 else:
69 msg += f" ({truncated_line_count} lines hidden)"
70 msg += f", {USAGE_MSG}"
71 truncated_explanation.extend(["", str(msg)])
72 return truncated_explanation
75def _truncate_by_char_count(input_lines: List[str], max_chars: int) -> List[str]:
76 # Check if truncation required
77 if len("".join(input_lines)) <= max_chars:
78 return input_lines
80 # Find point at which input length exceeds total allowed length
81 iterated_char_count = 0
82 for iterated_index, input_line in enumerate(input_lines):
83 if iterated_char_count + len(input_line) > max_chars:
84 break
85 iterated_char_count += len(input_line)
87 # Create truncated explanation with modified final line
88 truncated_result = input_lines[:iterated_index]
89 final_line = input_lines[iterated_index]
90 if final_line:
91 final_line_truncate_point = max_chars - iterated_char_count
92 final_line = final_line[:final_line_truncate_point]
93 truncated_result.append(final_line)
94 return truncated_result