• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

domdfcoding / domdf_python_tools / 14915795784

08 May 2025 08:45PM UTC coverage: 97.313%. Remained the same
14915795784

push

github

web-flow
Updated files with 'repo_helper'. (#134)

Co-authored-by: repo-helper[bot] <74742576+repo-helper[bot]@users.noreply.github.com>

2137 of 2196 relevant lines covered (97.31%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.76
/domdf_python_tools/pretty_print.py
1
#!/usr/bin/env python
2
# cython: language_level=3
3
#
4
#  utils.py
5
"""
6
Functions and classes for pretty printing.
7

8
.. versionadded:: 0.10.0
9
"""
10
#
11
#  Copyright © 2020 Dominic Davis-Foster <dominic@davis-foster.co.uk>
12
#
13
#  Permission is hereby granted, free of charge, to any person obtaining a copy
14
#  of this software and associated documentation files (the "Software"), to deal
15
#  in the Software without restriction, including without limitation the rights
16
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
#  copies of the Software, and to permit persons to whom the Software is
18
#  furnished to do so, subject to the following conditions:
19
#
20
#  The above copyright notice and this permission notice shall be included in all
21
#  copies or substantial portions of the Software.
22
#
23
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
27
#  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
28
#  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
29
#  OR OTHER DEALINGS IN THE SOFTWARE.
30
#
31
#  Based on CPython.
32
#  Licensed under the Python Software Foundation License Version 2.
33
#  Copyright © 2001-2020 Python Software Foundation. All rights reserved.
34
#  Copyright © 2000 BeOpen.com. All rights reserved.
35
#  Copyright © 1995-2000 Corporation for National Research Initiatives. All rights reserved.
36
#  Copyright © 1991-1995 Stichting Mathematisch Centrum. All rights reserved.
37
#
38

39
# stdlib
40
import sys
1✔
41
from io import StringIO
1✔
42
from typing import IO, Any, Callable, Iterator, MutableMapping, Optional, Tuple, Type, TypeVar
1✔
43

44
try:  # pragma: no cover
45

46
        # 3rd party
47
        from pprint36 import PrettyPrinter
48
        from pprint36._pprint import _safe_key  # type: ignore
49

50
        supports_sort_dicts = True
51

52
except ImportError:
1✔
53

54
        # stdlib
55
        from pprint import PrettyPrinter, _safe_key  # type: ignore
1✔
56

57
        supports_sort_dicts = sys.version_info >= (3, 8)
1✔
58

59
__all__ = ["FancyPrinter", "simple_repr"]
1✔
60

61
_T = TypeVar("_T", bound=Type)
1✔
62

63

64
class FancyPrinter(PrettyPrinter):
1✔
65
        """
66
        Subclass of :class:`~.pprint.PrettyPrinter` with different formatting.
67

68
        :param indent: Number of spaces to indent for each level of nesting.
69
        :param width: Attempted maximum number of columns in the output.
70
        :param depth: The maximum depth to print out nested structures.
71
        :param stream: The desired output stream.  If omitted (or :py:obj:`False`), the standard
72
                output stream available at construction will be used.
73
        :param compact: If :py:obj:`True`, several items will be combined in one line.
74
        :param sort_dicts: If :py:obj:`True`, dict keys are sorted.
75
                Only takes effect on Python 3.8 and later,
76
                or if `pprint36 <https://pypi.org/project/pprint36/>`_ is installed.
77
        """
78

79
        def __init__(
1✔
80
                        self,
81
                        indent: int = 1,
82
                        width: int = 80,
83
                        depth: Optional[int] = None,
84
                        stream: Optional[IO[str]] = None,
85
                        *,
86
                        compact: bool = False,
87
                        sort_dicts: bool = True,
88
                        ):
89
                if supports_sort_dicts:
1✔
90
                        super().__init__(
1✔
91
                                        indent=indent,
92
                                        width=width,
93
                                        depth=depth,
94
                                        stream=stream,
95
                                        compact=compact,
96
                                        sort_dicts=sort_dicts,
97
                                        )
98
                else:
99
                        super().__init__(
1✔
100
                                        indent=indent,
101
                                        width=width,
102
                                        depth=depth,
103
                                        stream=stream,
104
                                        compact=compact,
105
                                        )
106

107
        _dispatch: MutableMapping[Callable, Callable]
1✔
108
        _indent_per_level: int
1✔
109
        _format_items: Callable[[PrettyPrinter, Any, Any, Any, Any, Any, Any], None]
1✔
110
        _dispatch = dict(PrettyPrinter._dispatch)  # type: ignore
1✔
111

112
        def _make_open(self, char: str, indent: int, obj):
1✔
113
                if self._indent_per_level > 1:
1✔
114
                        the_indent = ' ' * (indent + 1)
1✔
115
                else:
116
                        the_indent = ' ' * (indent + self._indent_per_level)
1✔
117

118
                if obj and not self._compact:  # type: ignore
1✔
119
                        return f"{char}\n{the_indent}"
1✔
120
                else:
121
                        return char
1✔
122

123
        def _make_close(self, char: str, indent: int, obj):
1✔
124
                if obj and not self._compact:  # type: ignore
1✔
125
                        return f",\n{' ' * (indent + self._indent_per_level)}{char}"
1✔
126
                else:
127
                        return char
1✔
128

129
        def _pprint_dict(
1✔
130
                        self,
131
                        object,  # noqa: A002  # pylint: disable=redefined-builtin
132
                        stream,
133
                        indent,
134
                        allowance,
135
                        context,
136
                        level,
137
                        ):
138
                obj = object
1✔
139
                write = stream.write
1✔
140
                write(self._make_open('{', indent, obj))
1✔
141

142
                if self._indent_per_level > 1:
1✔
143
                        write((self._indent_per_level - 1) * ' ')
1✔
144

145
                if obj:
1✔
146
                        self._format_dict_items(  # type: ignore
1✔
147
                                        obj.items(),
148
                                        stream,
149
                                        indent,
150
                                        allowance + 1,
151
                                        context,
152
                                        level,
153
                                        )
154

155
                write(self._make_close('}', indent, obj))
1✔
156

157
        _dispatch[dict.__repr__] = _pprint_dict
1✔
158

159
        def _pprint_list(self, obj, stream, indent, allowance, context, level):
1✔
160
                stream.write(self._make_open('[', indent, obj))
1✔
161
                self._format_items(obj, stream, indent, allowance + 1, context, level)
1✔
162
                stream.write(self._make_close(']', indent, obj))
1✔
163

164
        _dispatch[list.__repr__] = _pprint_list
1✔
165

166
        def _pprint_tuple(self, obj, stream, indent, allowance, context, level):
1✔
167
                stream.write(self._make_open('(', indent, obj))
1✔
168
                endchar = ",)" if len(obj) == 1 else self._make_close(')', indent, obj)
1✔
169
                self._format_items(obj, stream, indent, allowance + len(endchar), context, level)
1✔
170
                stream.write(endchar)
1✔
171

172
        _dispatch[tuple.__repr__] = _pprint_tuple
1✔
173

174
        def _pprint_set(self, obj, stream, indent, allowance, context, level):
1✔
175
                if not obj:
1✔
176
                        stream.write(repr(obj))
×
177
                        return
×
178

179
                typ = obj.__class__
1✔
180
                if typ is set:
1✔
181
                        stream.write(self._make_open('{', indent, obj))
1✔
182
                        endchar = self._make_close('}', indent, obj)
1✔
183
                else:
184
                        stream.write(typ.__name__ + f"({{\n{' ' * (indent + self._indent_per_level + len(typ.__name__) + 1)}")
1✔
185
                        endchar = f",\n{' ' * (indent + self._indent_per_level + len(typ.__name__) + 1)}}})"
1✔
186
                        indent += len(typ.__name__) + 1
1✔
187

188
                obj = sorted(obj, key=_safe_key)
1✔
189
                self._format_items(obj, stream, indent, allowance + len(endchar), context, level)
1✔
190
                stream.write(endchar)
1✔
191

192
        _dispatch[set.__repr__] = _pprint_set
1✔
193
        _dispatch[frozenset.__repr__] = _pprint_set
1✔
194

195

196
class Attributes:
1✔
197

198
        def __init__(self, obj: object, *attributes: str):
1✔
199
                self.obj = obj
1✔
200
                self.attributes = attributes
1✔
201

202
        def __iter__(self) -> Iterator[Tuple[str, Any]]:
1✔
203
                for attr in self.attributes:
1✔
204
                        yield attr, getattr(self.obj, attr)
1✔
205

206
        def __len__(self) -> int:
1✔
207
                return len(self.attributes)
1✔
208

209
        def __repr__(self) -> str:
1✔
210
                return f"Attributes{self.attributes}"
×
211

212

213
class ReprPrettyPrinter(FancyPrinter):
1✔
214

215
        _dispatch = dict(FancyPrinter._dispatch)
1✔
216

217
        def format_attributes(self, obj: Attributes):
1✔
218
                stream = StringIO()
1✔
219
                context = {}
1✔
220

221
                context[id(obj)] = 1
1✔
222

223
                stream.write(f"(\n{self._indent_per_level * ' '}")
1✔
224

225
                if self._indent_per_level > 1:
1✔
226
                        stream.write((self._indent_per_level - 1) * ' ')
×
227

228
                if obj:
1✔
229
                        self._format_attribute_items(list(obj), stream, 0, 0 + 1, context, 1)
1✔
230
                stream.write(f"\n{self._indent_per_level * ' '})")
1✔
231

232
                del context[id(obj)]
1✔
233
                return stream.getvalue()
1✔
234

235
        def _format_attribute_items(self, items, stream, indent, allowance, context, level):
1✔
236

237
                write = stream.write
1✔
238
                indent += self._indent_per_level
1✔
239
                delimnl = ",\n" + ' ' * indent
1✔
240
                last_index = len(items) - 1
1✔
241

242
                for i, (key, ent) in enumerate(items):
1✔
243
                        last = i == last_index
1✔
244
                        write(key)
1✔
245
                        write('=')
1✔
246
                        self._format(  # type: ignore
1✔
247
                                ent,
248
                                stream,
249
                                indent + len(key) + 2,
250
                                allowance if last else 1,
251
                                context,
252
                                level,
253
                                )
254

255
                        if not last:
1✔
256
                                write(delimnl)
1✔
257

258

259
_default_formatter = ReprPrettyPrinter()
1✔
260

261

262
def simple_repr(*attributes: str, show_module: bool = False, **kwargs):
1✔
263
        r"""
264
        Adds a simple ``__repr__`` method to the decorated class.
265

266
        :param attributes: The attributes to include in the ``__repr__``.
267
        :param show_module: Whether to show the name of the module in the ``__repr__``.
268
        :param \*\*kwargs: Keyword arguments passed on to :class:`pprint.PrettyPrinter`.
269
        """
270

271
        def deco(obj: _T) -> _T:
1✔
272

273
                def __repr__(self) -> str:
1✔
274
                        if kwargs:
1✔
275
                                formatter = ReprPrettyPrinter(**kwargs)
1✔
276
                        else:
277
                                formatter = _default_formatter
×
278

279
                        class_name = f"{type(self).__module__}.{type(self).__name__}" if show_module else type(self).__name__
1✔
280

281
                        return f"{class_name}{formatter.format_attributes(Attributes(self, *attributes))}"
1✔
282

283
                __repr__.__doc__ = f"Return a string representation of the :class:`~{obj.__module__}.{obj.__name__}`."
1✔
284
                __repr__.__name__ = "__repr__"
1✔
285
                __repr__.__module__ = obj.__module__
1✔
286
                __repr__.__qualname__ = f"{obj.__module__}.__repr__"
1✔
287
                obj.__repr__ = __repr__  # type: ignore
1✔
288

289
                return obj
1✔
290

291
        return deco
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc