jk_prettyprintobj 0.2024.2.18__tar.gz → 0.2024.10.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jk_prettyprintobj
3
- Version: 0.2024.2.18
3
+ Version: 0.2024.10.20
4
4
  Summary: This python module provides a mixin for creating pretty debugging output for objects. This is especially useful for semi-complex data structures.
5
5
  Keywords: pretty-print,debugging,debug
6
6
  Author-email: Jürgen Knauth <pubsrc@binary-overflow.de>
@@ -0,0 +1,51 @@
1
+
2
+
3
+
4
+
5
+ #
6
+ # Holds settings about how to dump data.
7
+ #
8
+ class DumperSettings(object):
9
+
10
+ ################################################################################################################################
11
+ ## Constants
12
+ ################################################################################################################################
13
+
14
+ ################################################################################################################################
15
+ ## Constructor
16
+ ################################################################################################################################
17
+
18
+ #
19
+ # Constructor method.
20
+ #
21
+ def __init__(self) -> None:
22
+ self.showPrimitivesWithType = False
23
+ self.showDictKeysWithType = False
24
+ self.showComplexStructsWithType = False
25
+ self.compactSequenceLengthLimit = 8
26
+ self.compactSequenceItemLengthLimit = 50
27
+ self.bytesLineSize = 32
28
+ self.compactBytesLinesLengthLimit = 32
29
+ #
30
+
31
+ ################################################################################################################################
32
+ ## Public Properties
33
+ ################################################################################################################################
34
+
35
+ ################################################################################################################################
36
+ ## Helper Methods
37
+ ################################################################################################################################
38
+
39
+ ################################################################################################################################
40
+ ## Public Methods
41
+ ################################################################################################################################
42
+
43
+ ################################################################################################################################
44
+ ## Public Static Methods
45
+ ################################################################################################################################
46
+
47
+ #
48
+
49
+
50
+
51
+
@@ -0,0 +1,47 @@
1
+
2
+
3
+
4
+ #
5
+ # Represents a value that should be written directly without any automatic formatting.
6
+ #
7
+ class RawValue(object):
8
+
9
+ ################################################################################################################################
10
+ ## Constants
11
+ ################################################################################################################################
12
+
13
+ ################################################################################################################################
14
+ ## Constructor
15
+ ################################################################################################################################
16
+
17
+ #
18
+ # Constructor method.
19
+ #
20
+ def __init__(self, text:str) -> None:
21
+ assert isinstance(text, str)
22
+ assert text
23
+
24
+ self.text = text
25
+ #
26
+
27
+ ################################################################################################################################
28
+ ## Public Properties
29
+ ################################################################################################################################
30
+
31
+ ################################################################################################################################
32
+ ## Helper Methods
33
+ ################################################################################################################################
34
+
35
+ ################################################################################################################################
36
+ ## Public Methods
37
+ ################################################################################################################################
38
+
39
+ ################################################################################################################################
40
+ ## Public Static Methods
41
+ ################################################################################################################################
42
+
43
+ #
44
+
45
+
46
+
47
+
@@ -0,0 +1,152 @@
1
+
2
+
3
+ import codecs
4
+ import math
5
+ import typing
6
+
7
+ from .DumperSettings import DumperSettings
8
+
9
+
10
+
11
+
12
+
13
+ class _ByteChunker(object):
14
+
15
+ ################################################################################################################################
16
+ ## Constants
17
+ ################################################################################################################################
18
+
19
+ ################################################################################################################################
20
+ ## Constructor
21
+ ################################################################################################################################
22
+
23
+ ################################################################################################################################
24
+ ## Public Properties
25
+ ################################################################################################################################
26
+
27
+ ################################################################################################################################
28
+ ## Helper Methods
29
+ ################################################################################################################################
30
+
31
+ @staticmethod
32
+ def _x_byteChunkToHex(data:bytes) -> str:
33
+ hexSpanLength = 16 # == 8 bytes for a span
34
+
35
+ s = codecs.encode(data, "hex").decode("ascii")
36
+ chunks = [ s[i:i+hexSpanLength] for i in range(0, len(s), hexSpanLength) ]
37
+ return " ".join(chunks)
38
+ #
39
+
40
+ @staticmethod
41
+ def _x_byteChunkToASCII(data:bytes) -> str:
42
+ spanLength = 8 # == 8 bytes for a span
43
+
44
+ ret = []
45
+ for i, b in enumerate(data):
46
+ if (i % spanLength) == 0:
47
+ ret.append(" ")
48
+ if 32 <= b <= 127:
49
+ ret.append(chr(b))
50
+ else:
51
+ ret.append(".")
52
+
53
+ return "".join(ret)
54
+ #
55
+
56
+ ################################################################################################################################
57
+ ## Public Methods
58
+ ################################################################################################################################
59
+
60
+ ################################################################################################################################
61
+ ## Public Static Methods
62
+ ################################################################################################################################
63
+
64
+ #
65
+ # Creates data for output.
66
+ #
67
+ # @param DumperSettings settings The settings object that contains <c>settings.compactBytesLinesLengthLimit</c>
68
+ # @param bytes data The full data block to process
69
+ # @param str? processorName Understands "shorten" to provide shortened output,
70
+ # respecting <c>settings.compactBytesLinesLengthLimit</c> in that cae
71
+ # @return tuple<str,str,str>[] Returns a generator that provides tuples of the following values:
72
+ # * str offset part
73
+ # * str hex data part
74
+ # * str ascii data part
75
+ #
76
+ @staticmethod
77
+ def chunkWithOfs(settings:DumperSettings, data:bytes, processorName:str = None) -> typing.Generator[typing.Tuple[str,str,str],typing.Tuple[str,str,str],typing.Tuple[str,str,str]]:
78
+ assert isinstance(settings, DumperSettings)
79
+ assert isinstance(data, bytes)
80
+ chunkSize = settings.bytesLineSize
81
+ assert isinstance(chunkSize, int)
82
+ assert chunkSize > 0
83
+ hexStrPadded = chunkSize*2 + math.ceil(chunkSize / 8) - 1
84
+ if processorName is not None:
85
+ assert isinstance(processorName, str)
86
+
87
+ # ----
88
+
89
+ nTotalLength = len(data)
90
+ nTotalLines = math.ceil(nTotalLength / chunkSize)
91
+ formatStrFragment = None
92
+ formatStrFragmentEllipsis = None
93
+ if nTotalLength <= 256*256:
94
+ formatStrFragment = "{:04x}"
95
+ formatStrFragmentEllipsis = "... "
96
+ elif nTotalLength <= 256*256*256:
97
+ formatStrFragment = "{:06x}"
98
+ formatStrFragmentEllipsis = "... "
99
+ else:
100
+ formatStrFragment = "{:08x}"
101
+ formatStrFragmentEllipsis = "... "
102
+
103
+ # ----
104
+
105
+ skipFrom = -1
106
+ skipTo = -1
107
+ if processorName:
108
+ if processorName == "shorten":
109
+ skipFrom = settings.compactBytesLinesLengthLimit
110
+ skipTo = nTotalLines - 4
111
+ if skipFrom >= skipTo:
112
+ skipFrom = -1
113
+ skipTo = -1
114
+ else:
115
+ raise Exception("No such postprocessor: " + repr(processorName))
116
+
117
+ # ----
118
+
119
+ if skipFrom < 0:
120
+ # direct loop, no addtional if statements
121
+ iFrom = 0
122
+ iTo = chunkSize
123
+ while iFrom < len(data):
124
+ chunk = data[iFrom:iTo]
125
+ yield formatStrFragment.format(iFrom), _ByteChunker._x_byteChunkToHex(chunk).ljust(hexStrPadded), _ByteChunker._x_byteChunkToASCII(chunk)
126
+ iFrom = iTo
127
+ iTo += chunkSize
128
+ else:
129
+ # loop with if statements
130
+ lineNo = 0
131
+ iFrom = 0
132
+ iTo = chunkSize
133
+ while iFrom < len(data):
134
+ chunk = data[iFrom:iTo]
135
+ if skipFrom <= lineNo <= skipTo:
136
+ if skipFrom == lineNo:
137
+ yield formatStrFragmentEllipsis, "...".ljust(hexStrPadded), "..."
138
+ else:
139
+ yield formatStrFragment.format(iFrom), _ByteChunker._x_byteChunkToHex(chunk).ljust(hexStrPadded), _ByteChunker._x_byteChunkToASCII(chunk)
140
+ iFrom = iTo
141
+ iTo += chunkSize
142
+ lineNo += 1
143
+ #
144
+
145
+ #
146
+
147
+
148
+
149
+
150
+
151
+
152
+
@@ -0,0 +1,116 @@
1
+
2
+
3
+ import typing
4
+
5
+ from ._Hex import _Hex
6
+ from ._Bits import _Bits
7
+
8
+
9
+
10
+
11
+
12
+ class _ConverterFunctions(object):
13
+
14
+ ################################################################################################################################
15
+ ## Constants
16
+ ################################################################################################################################
17
+
18
+ ################################################################################################################################
19
+ ## Constructor
20
+ ################################################################################################################################
21
+
22
+ ################################################################################################################################
23
+ ## Public Properties
24
+ ################################################################################################################################
25
+
26
+ ################################################################################################################################
27
+ ## Helper Methods
28
+ ################################################################################################################################
29
+
30
+ ################################################################################################################################
31
+ ## Public Methods
32
+ ################################################################################################################################
33
+
34
+ ################################################################################################################################
35
+ ## Public Static Methods
36
+ ################################################################################################################################
37
+
38
+ @staticmethod
39
+ def str_shortenText(text:str) -> str:
40
+ if len(text) > 40:
41
+ return text[:40] + "..."
42
+ else:
43
+ return text
44
+ #
45
+
46
+ @staticmethod
47
+ def float_roundTo7FractionDigits(data:typing.Union[int,float]) -> float:
48
+ return round(data, 7)
49
+ #
50
+
51
+ @staticmethod
52
+ def float_roundTo6FractionDigits(data:typing.Union[int,float]) -> float:
53
+ return round(data, 6)
54
+ #
55
+
56
+ @staticmethod
57
+ def float_roundTo5FractionDigits(data:typing.Union[int,float]) -> float:
58
+ return round(data, 5)
59
+ #
60
+
61
+ @staticmethod
62
+ def float_roundTo4FractionDigits(data:typing.Union[int,float]) -> float:
63
+ return round(data, 4)
64
+ #
65
+
66
+ @staticmethod
67
+ def float_roundTo3FractionDigits(data:typing.Union[int,float]) -> float:
68
+ return round(data, 3)
69
+ #
70
+
71
+ @staticmethod
72
+ def float_roundTo2FractionDigits(data:typing.Union[int,float]) -> float:
73
+ return round(data, 2)
74
+ #
75
+
76
+ @staticmethod
77
+ def float_roundTo1FractionDigits(data:typing.Union[int,float]) -> float:
78
+ return round(data, 1)
79
+ #
80
+
81
+ @staticmethod
82
+ def int_toHex(data:int) -> typing.Union[int,str]:
83
+ if data < 0:
84
+ return data
85
+ return _Hex(data)
86
+ #
87
+
88
+ @staticmethod
89
+ def int_toBits(data:int) -> typing.Union[int,str]:
90
+ if data < 0:
91
+ return data
92
+ return _Bits(data)
93
+ #
94
+
95
+ @staticmethod
96
+ def byteChunker(data:bytes, chunkSize:int) -> typing.Generator[bytes,bytes,bytes]:
97
+ assert isinstance(data, bytes)
98
+ assert isinstance(chunkSize, int)
99
+ assert chunkSize > 0
100
+
101
+ iFrom = 0
102
+ iTo = chunkSize
103
+ while iFrom < len(data):
104
+ yield data[iFrom:iTo]
105
+ iFrom = iTo
106
+ iTo += chunkSize
107
+ #
108
+
109
+ #
110
+
111
+
112
+
113
+
114
+
115
+
116
+
@@ -0,0 +1,46 @@
1
+
2
+
3
+
4
+ __author__ = "Jürgen Knauth"
5
+ __version__ = "0.2024.10.20"
6
+ __all__ = (
7
+ "DumperSettings",
8
+ "RawValue",
9
+ "DumpMixin",
10
+ "Dumper",
11
+ "DumpCtx",
12
+ "DEFAULT_DUMPER_SETTINGS",
13
+ "pprint",
14
+ )
15
+
16
+
17
+
18
+
19
+
20
+ from .DumperSettings import DumperSettings
21
+ from .RawValue import RawValue
22
+ from .dumper import DumpMixin, Dumper, DumpCtx, DEFAULT_DUMPER_SETTINGS
23
+
24
+ from builtins import print as _print
25
+
26
+
27
+
28
+
29
+ #
30
+ # Print any value in a human readable way.
31
+ #
32
+ def pprint(something, printFunc = None):
33
+ if printFunc is None:
34
+ printFunc = _print
35
+ else:
36
+ assert callable(printFunc)
37
+
38
+ dumper = Dumper()
39
+ with dumper.createContext(None, "") as dumper2:
40
+ dumper2._dumpX("", something)
41
+ dumper.print(printFunc)
42
+ #
43
+
44
+
45
+
46
+
@@ -7,247 +7,51 @@ import math
7
7
  import codecs
8
8
  #import datetime
9
9
 
10
+ from .RawValue import RawValue
11
+ from .DumperSettings import DumperSettings
10
12
  from ._Hex import _Hex
11
13
  from ._Bits import _Bits
14
+ from ._ConverterFunctions import _ConverterFunctions as _CF
15
+ from ._ByteChunker import _ByteChunker
12
16
 
13
17
 
14
18
 
15
- ################################################################################################################################
16
- ################################################################################################################################
17
- ##
18
- ## Dumpable objects should use the following import:
19
- ##
20
- ## import jk_prettyprintobj
21
- ##
22
- ## Dumpable objects should then be defined like this:
23
- ##
24
- ## class FooBar(SomeBaseClassA,SomeMixinB,jk_prettyprintobj.DumpMixin)
25
- ## ....
26
- ## #
27
- ##
28
- ## Dumpable objects should then implement one of these two methods:
29
- ##
30
- ## def _dump(self, ctx:jk_prettyprintobj.DumpCtx):
31
- ## ctx.dumpVar(...)
32
- ## #
33
- ##
34
- ## or:
35
- ##
36
- ## def _dumpVarNames(self) -> typing.List[str]:
37
- ## return [
38
- ## "....",
39
- ## ]
40
- ## #
41
- ##
42
- ################################################################################################################################
43
- ################################################################################################################################
44
-
45
-
46
19
 
47
20
 
48
21
 
49
22
 
50
23
 
51
-
52
- class DumperSettings(object):
53
-
54
- def __init__(self):
55
- self.showPrimitivesWithType = False
56
- self.showDictKeysWithType = False
57
- self.showComplexStructsWithType = False
58
- self.compactSequenceLengthLimit = 8
59
- self.compactSequenceItemLengthLimit = 50
60
- self.bytesLineSize = 32
61
- self.compactBytesLinesLengthLimit = 32
62
- #
63
-
64
- #
65
-
66
24
  DEFAULT_DUMPER_SETTINGS = DumperSettings()
67
25
 
68
26
 
69
27
 
70
28
 
71
- def _str_shortenText(text:str) -> str:
72
- if len(text) > 40:
73
- return text[:40] + "..."
74
- else:
75
- return text
76
- #
77
-
78
- def _float_roundTo7FractionDigits(data:typing.Union[int,float]) -> float:
79
- return round(data, 7)
80
- #
81
-
82
- def _float_roundTo6FractionDigits(data:typing.Union[int,float]) -> float:
83
- return round(data, 6)
84
- #
85
-
86
- def _float_roundTo5FractionDigits(data:typing.Union[int,float]) -> float:
87
- return round(data, 5)
88
- #
89
-
90
- def _float_roundTo4FractionDigits(data:typing.Union[int,float]) -> float:
91
- return round(data, 4)
92
- #
93
-
94
- def _float_roundTo3FractionDigits(data:typing.Union[int,float]) -> float:
95
- return round(data, 3)
96
- #
97
-
98
- def _float_roundTo2FractionDigits(data:typing.Union[int,float]) -> float:
99
- return round(data, 2)
100
- #
101
-
102
- def _float_roundTo1FractionDigits(data:typing.Union[int,float]) -> float:
103
- return round(data, 1)
104
- #
105
-
106
- def _int_toHex(data:int) -> typing.Union[int,str]:
107
- if data < 0:
108
- return data
109
- return _Hex(data)
110
- #
111
-
112
- def _int_toBits(data:int) -> typing.Union[int,str]:
113
- if data < 0:
114
- return data
115
- return _Bits(data)
116
- #
117
-
118
- def _byteChunker(data:bytes, chunkSize:int) -> typing.Sequence[bytes]:
119
- assert isinstance(data, bytes)
120
- assert isinstance(chunkSize, int)
121
- assert chunkSize > 0
122
-
123
- iFrom = 0
124
- iTo = chunkSize
125
- while iFrom < len(data):
126
- yield data[iFrom:iTo]
127
- iFrom = iTo
128
- iTo += chunkSize
129
- #
130
-
131
- def _x_byteChunkToHex(data:bytes) -> str:
132
- hexSpanLength = 16 # == 8 bytes for a span
133
-
134
- s = codecs.encode(data, "hex").decode("ascii")
135
- chunks = [ s[i:i+hexSpanLength] for i in range(0, len(s), hexSpanLength) ]
136
- return " ".join(chunks)
137
- #
138
-
139
- def _x_byteChunkToASCII(data:bytes) -> str:
140
- spanLength = 8 # == 8 bytes for a span
141
-
142
- ret = []
143
- for i, b in enumerate(data):
144
- if (i % spanLength) == 0:
145
- ret.append(" ")
146
- if 32 <= b <= 127:
147
- ret.append(chr(b))
148
- else:
149
- ret.append(".")
150
-
151
- return "".join(ret)
152
- #
153
-
154
- #
155
- # Returns chunks of the specified data.
156
- #
157
- def _byteChunkerWithOfs(settings:DumperSettings, data:bytes, processorName:str = None) -> typing.Sequence[typing.Tuple[str,str,str]]:
158
- assert isinstance(settings, DumperSettings)
159
- assert isinstance(data, bytes)
160
- chunkSize = settings.bytesLineSize
161
- assert isinstance(chunkSize, int)
162
- assert chunkSize > 0
163
- hexStrPadded = chunkSize*2 + math.ceil(chunkSize / 8) - 1
164
- if processorName is not None:
165
- assert isinstance(processorName, str)
166
-
167
- # ----
168
-
169
- nTotalLength = len(data)
170
- nTotalLines = math.ceil(nTotalLength / chunkSize)
171
- formatStrFragment = None
172
- formatStrFragmentEllipsis = None
173
- if nTotalLength <= 256*256:
174
- formatStrFragment = "{:04x}"
175
- formatStrFragmentEllipsis = "... "
176
- elif nTotalLength <= 256*256*256:
177
- formatStrFragment = "{:06x}"
178
- formatStrFragmentEllipsis = "... "
179
- else:
180
- formatStrFragment = "{:08x}"
181
- formatStrFragmentEllipsis = "... "
182
-
183
- # ----
184
-
185
- skipFrom = -1
186
- skipTo = -1
187
- if processorName:
188
- if processorName == "shorten":
189
- skipFrom = settings.compactBytesLinesLengthLimit
190
- skipTo = nTotalLines - 4
191
- if skipFrom >= skipTo:
192
- skipFrom = -1
193
- skipTo = -1
194
- else:
195
- raise Exception("No such postprocessor: " + repr(processorName))
196
-
197
- # ----
198
-
199
- if skipFrom < 0:
200
- # direct loop, no addtional if statements
201
- iFrom = 0
202
- iTo = chunkSize
203
- while iFrom < len(data):
204
- chunk = data[iFrom:iTo]
205
- yield formatStrFragment.format(iFrom), _x_byteChunkToHex(chunk).ljust(hexStrPadded), _x_byteChunkToASCII(chunk)
206
- iFrom = iTo
207
- iTo += chunkSize
208
- else:
209
- # loop with if statements
210
- lineNo = 0
211
- iFrom = 0
212
- iTo = chunkSize
213
- while iFrom < len(data):
214
- chunk = data[iFrom:iTo]
215
- if skipFrom <= lineNo <= skipTo:
216
- if skipFrom == lineNo:
217
- yield formatStrFragmentEllipsis, "...".ljust(hexStrPadded), "..."
218
- else:
219
- yield formatStrFragment.format(iFrom), _x_byteChunkToHex(chunk).ljust(hexStrPadded), _x_byteChunkToASCII(chunk)
220
- iFrom = iTo
221
- iTo += chunkSize
222
- lineNo += 1
223
- #
224
-
225
-
226
29
 
227
30
 
31
+ _PRIMITIVE_POST_PROCESSORS = {
32
+ # map associating processor name to tuple consisting of type (or type list) and callable
228
33
 
229
- POST_PROCESSORS = {
230
- "shorten": (str, _str_shortenText),
231
- "hex": (int, _int_toHex),
232
- "bit": (int, _int_toBits),
233
- "round1": ((float, int), _float_roundTo1FractionDigits),
234
- "round2": ((float, int), _float_roundTo2FractionDigits),
235
- "round3": ((float, int), _float_roundTo3FractionDigits),
236
- "round4": ((float, int), _float_roundTo4FractionDigits),
237
- "round5": ((float, int), _float_roundTo5FractionDigits),
238
- "round6": ((float, int), _float_roundTo6FractionDigits),
239
- "round7": ((float, int), _float_roundTo7FractionDigits),
34
+ "shorten": (str, _CF.str_shortenText),
35
+ "hex": (int, _CF.int_toHex),
36
+ "bit": (int, _CF.int_toBits),
37
+ "round1": ((float, int), _CF.float_roundTo1FractionDigits),
38
+ "round2": ((float, int), _CF.float_roundTo2FractionDigits),
39
+ "round3": ((float, int), _CF.float_roundTo3FractionDigits),
40
+ "round4": ((float, int), _CF.float_roundTo4FractionDigits),
41
+ "round5": ((float, int), _CF.float_roundTo5FractionDigits),
42
+ "round6": ((float, int), _CF.float_roundTo6FractionDigits),
43
+ "round7": ((float, int), _CF.float_roundTo7FractionDigits),
240
44
 
241
- "str_shorten": (str, _str_shortenText),
242
- "float_round7": ((float, int), _float_roundTo7FractionDigits),
243
- "float_round6": ((float, int), _float_roundTo6FractionDigits),
244
- "float_round5": ((float, int), _float_roundTo5FractionDigits),
245
- "float_round4": ((float, int), _float_roundTo4FractionDigits),
246
- "float_round3": ((float, int), _float_roundTo3FractionDigits),
247
- "float_round2": ((float, int), _float_roundTo2FractionDigits),
248
- "float_round1": ((float, int), _float_roundTo1FractionDigits),
249
- "int_hex": (int, _int_toHex),
250
- "int_bit": (int, _int_toBits),
45
+ "str_shorten": (str, _CF.str_shortenText),
46
+ "float_round7": ((float, int), _CF.float_roundTo7FractionDigits),
47
+ "float_round6": ((float, int), _CF.float_roundTo6FractionDigits),
48
+ "float_round5": ((float, int), _CF.float_roundTo5FractionDigits),
49
+ "float_round4": ((float, int), _CF.float_roundTo4FractionDigits),
50
+ "float_round3": ((float, int), _CF.float_roundTo3FractionDigits),
51
+ "float_round2": ((float, int), _CF.float_roundTo2FractionDigits),
52
+ "float_round1": ((float, int), _CF.float_roundTo1FractionDigits),
53
+ "int_hex": (int, _CF.int_toHex),
54
+ "int_bit": (int, _CF.int_toBits),
251
55
  }
252
56
 
253
57
 
@@ -257,23 +61,17 @@ class _Omitted:
257
61
  pass
258
62
  #
259
63
 
260
- class RawValue:
261
-
262
- def __init__(self, text:str) -> None:
263
- self.text = text
264
- #
265
-
266
- #
267
-
268
64
  _OMITTED = _Omitted()
269
65
 
270
66
 
271
67
 
272
68
 
273
69
 
70
+ DumpCtx = typing.NewType("DumpCtx", object)
71
+
274
72
  class DumpCtx(object):
275
73
 
276
- _TYPE_MAP = {} # type -> function
74
+ _TYPE_MAP:typing.Dict[type,typing.Callable[[DumpCtx,str,typing.Any,typing.Union[str,None]],None]] = {} # type -> function
277
75
 
278
76
  def __init__(self, s:DumperSettings, outputLines:list, exitAppend:str, prefix:str):
279
77
  self.__s = s
@@ -294,7 +92,7 @@ class DumpCtx(object):
294
92
  #
295
93
 
296
94
  def dumpVarRaw(self, varName:str, value:RawValue) -> None:
297
- self._dumpX(varName + " = ", value.text)
95
+ self._dumpX(varName + " = ", value)
298
96
  #
299
97
 
300
98
  #
@@ -339,9 +137,13 @@ class DumpCtx(object):
339
137
  ################################################################################################################################
340
138
 
341
139
  #
342
- # This method outputs a value (recursively).
140
+ # This method outputs a value (recursively). It is the main dump method.
343
141
  # To achieve this this method analyses the data type of the specified value and invokes individual type processing methods if available.
344
142
  #
143
+ # @param str extraPrefix (required) A prefix to use
144
+ # @param any value (optional) The value to print
145
+ # @param str? processorName (optional) A value output processor
146
+ #
345
147
  def _dumpX(self, extraPrefix:str, value, processorName:str = None):
346
148
  if value is None:
347
149
  self._dumpPrimitive(extraPrefix, None, processorName)
@@ -369,7 +171,7 @@ class DumpCtx(object):
369
171
  self._dumpObj(extraPrefix, value, processorName)
370
172
  return
371
173
 
372
- # is it derived from on of our types?
174
+ # is it derived from one of our types?
373
175
 
374
176
  for storedT, m in DumpCtx._TYPE_MAP.items():
375
177
  if isinstance(value, storedT):
@@ -401,7 +203,10 @@ class DumpCtx(object):
401
203
  #
402
204
  def _dumpObj(self, extraPrefix:str, value:object, processorName:str = None):
403
205
  if processorName == "shorten":
404
- self.outputLines.append(self.prefix + extraPrefix + "<" + value.__class__.__name__ + "(...)>")
206
+ if hasattr(value, "_dumpShort"):
207
+ value._dumpShort(ctx2)
208
+ else:
209
+ self.outputLines.append(self.prefix + extraPrefix + "<" + value.__class__.__name__ + "(...)>")
405
210
 
406
211
  else:
407
212
  self.outputLines.append(self.prefix + extraPrefix + "<" + value.__class__.__name__ + "(")
@@ -421,6 +226,9 @@ class DumpCtx(object):
421
226
  #
422
227
  # Dump the specified dictionary.
423
228
  #
229
+ # @param str processorName (optional) The processor name. This name is passed to recursive calls of _dumpX() so that it is applied
230
+ # to every value. Additionally if "shorten" is specified the dictionary itself will be shortened.
231
+ #
424
232
  def _dumpDict(self, extraPrefix:str, value:dict, processorName:str = None):
425
233
  e = (type(value).__name__ + ":") if self.__s.showComplexStructsWithType else ""
426
234
 
@@ -428,11 +236,17 @@ class DumpCtx(object):
428
236
 
429
237
  ctx = DumpCtx(self.__s, self.outputLines, None, self.prefix + "\t")
430
238
  with ctx as ctx2:
239
+ i = 0
431
240
  for k, v in value.items():
241
+ if processorName == "shorten":
242
+ if i >= 3:
243
+ ctx2._dumpRawValue("", RawValue("..."))
244
+ break
432
245
  if processorName == "omitValues":
433
246
  v = _OMITTED
434
247
  ctx2._dumpX(self._dictKeyToStr(k) + " : ", v)
435
248
  self.outputLines[-1] += ","
249
+ i += 1
436
250
 
437
251
  self.outputLines.append(self.prefix + "}")
438
252
  #
@@ -454,20 +268,30 @@ class DumpCtx(object):
454
268
  #
455
269
  # Dump the specified list.
456
270
  #
271
+ # @param str processorName (optional) The processor name. This name is passed to recursive calls of _dumpX() so that it is applied
272
+ # to every value. Additionally if "shorten" is specified the list itself will be shortened.
273
+ #
457
274
  def _dumpList(self, extraPrefix:str, value:list, processorName:str = None):
458
275
  e = (type(value).__name__ + ":") if self.__s.showComplexStructsWithType else ""
459
276
 
460
277
  if self._canCompactSequence(value):
461
- self.outputLines.append(self.prefix + extraPrefix + e + "[ " + self._compactSequence(value, processorName) + " ]")
278
+ rawText = self._compactSequence(value, processorName)
279
+ self.outputLines.append(self.prefix + extraPrefix + e + "[ " + rawText + " ]")
462
280
 
463
281
  else:
464
282
  self.outputLines.append(self.prefix + extraPrefix + e + "[")
465
283
 
466
284
  ctx = DumpCtx(self.__s, self.outputLines, None, self.prefix + "\t")
467
285
  with ctx as ctx2:
286
+ i = 0
468
287
  for vItem in value:
288
+ if processorName == "shorten":
289
+ if i >= 3:
290
+ ctx2._dumpRawValue("", RawValue("..."))
291
+ break
469
292
  ctx2._dumpX("", vItem, processorName)
470
293
  self.outputLines[-1] += ","
294
+ i += 1
471
295
 
472
296
  self.outputLines.append(self.prefix + "]")
473
297
  #
@@ -484,7 +308,7 @@ class DumpCtx(object):
484
308
  else:
485
309
  self.outputLines.append(self.prefix + extraPrefix + e + "<")
486
310
 
487
- for sOfs, chunk, sAscii in _byteChunkerWithOfs(self.__s, value, processorName):
311
+ for sOfs, chunk, sAscii in _ByteChunker.chunkWithOfs(self.__s, value, processorName):
488
312
  self.outputLines.append(self.prefix + "\t" + sOfs + " " + chunk + " " + sAscii)
489
313
 
490
314
  if len(value) == 1:
@@ -498,20 +322,30 @@ class DumpCtx(object):
498
322
  #
499
323
  # Dump the specified tuple.
500
324
  #
325
+ # @param str processorName (optional) The processor name. This name is passed to recursive calls of _dumpX() so that it is applied
326
+ # to every value. Additionally if "shorten" is specified the list itself will be shortened.
327
+ #
501
328
  def _dumpTuple(self, extraPrefix:str, value:set, processorName:str = None):
502
329
  e = (type(value).__name__ + ":") if self.__s.showComplexStructsWithType else ""
503
330
 
504
331
  if self._canCompactSequence(value):
505
- self.outputLines.append(self.prefix + extraPrefix + e + "( " + self._compactSequence(value, processorName) + " )")
332
+ rawText = self._compactSequence(value, processorName)
333
+ self.outputLines.append(self.prefix + extraPrefix + e + "( " + rawText + " )")
506
334
 
507
335
  else:
508
336
  self.outputLines.append(self.prefix + extraPrefix + e + "(")
509
337
 
510
338
  ctx = DumpCtx(self.__s, self.outputLines, None, self.prefix + "\t")
511
339
  with ctx as ctx2:
340
+ i = 0
512
341
  for vItem in value:
342
+ if processorName == "shorten":
343
+ if i >= 3:
344
+ ctx2._dumpRawValue("", RawValue("..."))
345
+ break
513
346
  ctx2._dumpX("", vItem, processorName)
514
347
  self.outputLines[-1] += ","
348
+ i += 1
515
349
 
516
350
  self.outputLines.append(self.prefix + ")")
517
351
  #
@@ -591,6 +425,7 @@ class DumpCtx(object):
591
425
  return True
592
426
  #
593
427
 
428
+ # TODO: support "shorten" to shorten the list
594
429
  def _compactSequence(self, someSequence, processorName:str = None) -> str:
595
430
  ret = []
596
431
  for v in someSequence:
@@ -627,9 +462,9 @@ class DumpCtx(object):
627
462
  else:
628
463
  # process value before converting it to str
629
464
  if processorName:
630
- if processorName not in POST_PROCESSORS:
465
+ if processorName not in _PRIMITIVE_POST_PROCESSORS:
631
466
  raise Exception("No such postprocessor: " + repr(processorName))
632
- postProcessorTypeCompatibility, postProcessor = POST_PROCESSORS[processorName]
467
+ postProcessorTypeCompatibility, postProcessor = _PRIMITIVE_POST_PROCESSORS[processorName]
633
468
  if isinstance(value, postProcessorTypeCompatibility):
634
469
  value = postProcessor(value)
635
470
 
@@ -730,28 +565,73 @@ class Dumper(object):
730
565
 
731
566
 
732
567
 
568
+ ################################################################################################################################
569
+ ################################################################################################################################
570
+ """
571
+
572
+ Dumpable objects should be defined like this:
573
+
574
+ class FooBar(SomeBaseClassA,SomeMixinB,jk_prettyprintobj.DumpMixin)
575
+ ....
576
+ #
577
+
578
+ Dumpable objects should then implement one of these two methods:
579
+
580
+ def _dump(self, ctx:jk_prettyprintobj.DumpCtx):
581
+ ctx.dumpVar(...)
582
+ #
583
+
584
+ or:
585
+
586
+ def _dumpVarNames(self) -> typing.List[str]:
587
+ return [
588
+ "....",
589
+ ]
590
+ #
591
+
592
+ and maybe additionally:
593
+
594
+ def _dumpShort(self, ctx:jk_prettyprintobj.DumpCtx):
595
+ ctx.dumpVar(...)
596
+ #
597
+
598
+ """
599
+ ################################################################################################################################
600
+ ################################################################################################################################
601
+
602
+
603
+
604
+
733
605
  class DumpMixin:
734
606
 
735
607
  __slots__ = tuple()
736
608
 
737
- def dump(self, prefix:str = None, printFunc = None) -> None:
609
+ def __dump(self, prefix:str = None) -> Dumper:
738
610
  dumper = Dumper()
739
611
  with dumper.createContext(self, prefix) as dumper2:
740
612
  if not dumper2._isDumpableObj(self):
741
613
  raise Exception("Improper object encountered for prettyprinting: " + self.__class__.__name__ + " - Either implement _dump(ctx:DumpCtx) or _dumpVarNames()!")
742
614
  dumper2._dumpObj("", self)
615
+ return dumper
616
+ #
617
+
618
+ def dump(self, prefix:str = None, printFunc = None) -> None:
619
+ dumper = self.__dump(prefix)
743
620
  dumper.print(printFunc)
744
621
  #
745
622
 
746
623
  def dumpToStr(self, prefix:str = None) -> str:
747
- dumper = Dumper()
748
- with dumper.createContext(self, prefix) as dumper2:
749
- if not dumper2._isDumpableObj(self):
750
- raise Exception("Improper object encountered for prettyprinting: " + self.__class__.__name__ + " - Either implement _dump(ctx:DumpCtx) or _dumpVarNames()!")
751
- dumper2._dumpObj("", self)
624
+ dumper = self.__dump(prefix)
752
625
  return dumper.toStr()
753
626
  #
754
627
 
628
+ def dumpToFile(self, filePath:str) -> None:
629
+ dumper = self.__dump()
630
+ assert isinstance(filePath, str)
631
+ with open(filePath, "w", newline="\n", encoding="UTF-8") as fout:
632
+ fout.write(dumper.toStr())
633
+ #
634
+
755
635
  #
756
636
 
757
637
 
@@ -1,16 +0,0 @@
1
-
2
-
3
-
4
- __author__ = "Jürgen Knauth"
5
- __version__ = "0.2024.2.18"
6
-
7
-
8
-
9
- from .dumper import DumpMixin, Dumper, DumpCtx, DEFAULT_DUMPER_SETTINGS, RawValue
10
-
11
-
12
-
13
-
14
-
15
-
16
-