jk_prettyprintobj 0.2024.2.18__py3-none-any.whl → 0.2024.10.20__py3-none-any.whl
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.
- jk_prettyprintobj/DumperSettings.py +51 -0
- jk_prettyprintobj/RawValue.py +47 -0
- jk_prettyprintobj/_ByteChunker.py +152 -0
- jk_prettyprintobj/_ConverterFunctions.py +116 -0
- jk_prettyprintobj/__init__.py +32 -2
- jk_prettyprintobj/dumper.py +126 -246
- {jk_prettyprintobj-0.2024.2.18.dist-info → jk_prettyprintobj-0.2024.10.20.dist-info}/METADATA +1 -1
- jk_prettyprintobj-0.2024.10.20.dist-info/RECORD +11 -0
- jk_prettyprintobj-0.2024.2.18.dist-info/RECORD +0 -7
- {jk_prettyprintobj-0.2024.2.18.dist-info → jk_prettyprintobj-0.2024.10.20.dist-info}/WHEEL +0 -0
@@ -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
|
+
|
jk_prettyprintobj/__init__.py
CHANGED
@@ -2,14 +2,44 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
__author__ = "Jürgen Knauth"
|
5
|
-
__version__ = "0.2024.
|
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
|
+
)
|
6
15
|
|
7
16
|
|
8
17
|
|
9
|
-
from .dumper import DumpMixin, Dumper, DumpCtx, DEFAULT_DUMPER_SETTINGS, RawValue
|
10
18
|
|
11
19
|
|
20
|
+
from .DumperSettings import DumperSettings
|
21
|
+
from .RawValue import RawValue
|
22
|
+
from .dumper import DumpMixin, Dumper, DumpCtx, DEFAULT_DUMPER_SETTINGS
|
12
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
|
+
#
|
13
43
|
|
14
44
|
|
15
45
|
|
jk_prettyprintobj/dumper.py
CHANGED
@@ -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
|
-
|
230
|
-
"
|
231
|
-
"
|
232
|
-
"
|
233
|
-
"
|
234
|
-
"
|
235
|
-
"
|
236
|
-
"
|
237
|
-
"
|
238
|
-
"
|
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,
|
242
|
-
"float_round7": ((float, int),
|
243
|
-
"float_round6": ((float, int),
|
244
|
-
"float_round5": ((float, int),
|
245
|
-
"float_round4": ((float, int),
|
246
|
-
"float_round3": ((float, int),
|
247
|
-
"float_round2": ((float, int),
|
248
|
-
"float_round1": ((float, int),
|
249
|
-
"int_hex": (int,
|
250
|
-
"int_bit": (int,
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
465
|
+
if processorName not in _PRIMITIVE_POST_PROCESSORS:
|
631
466
|
raise Exception("No such postprocessor: " + repr(processorName))
|
632
|
-
postProcessorTypeCompatibility, postProcessor =
|
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
|
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 =
|
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
|
|
{jk_prettyprintobj-0.2024.2.18.dist-info → jk_prettyprintobj-0.2024.10.20.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: jk_prettyprintobj
|
3
|
-
Version: 0.2024.
|
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,11 @@
|
|
1
|
+
jk_prettyprintobj/DumperSettings.py,sha256=hPWXl-QwW8VCgF4Z8_vmTuzbRxJywGBxgY_X7Q9q5iE,2094
|
2
|
+
jk_prettyprintobj/RawValue.py,sha256=N-iBdP4MqBlO5RYSzLhQOFUb3J0qneKjnKL6beo6-MI,1944
|
3
|
+
jk_prettyprintobj/_Bits.py,sha256=EVAk9bMNpnuv_re8hi5vk4znK2xYHsceR07RG_0f4x0,805
|
4
|
+
jk_prettyprintobj/_ByteChunker.py,sha256=Q3BUM87TsuS5-_HorEDoaXIYwAxbyG1vsLgW9H64dUc,5031
|
5
|
+
jk_prettyprintobj/_ConverterFunctions.py,sha256=c31r8SZB4JXy8uohwXimBZR8Gn0rSfYs2jGhr9a5eJw,3301
|
6
|
+
jk_prettyprintobj/_Hex.py,sha256=RsqEIp8N4ms48aDCYIV1TLQxexd0TnxwRmnx91Y0MQg,515
|
7
|
+
jk_prettyprintobj/__init__.py,sha256=Lm-biGzrzeWnsRU0wcCfNDrU15Z0eJHFp_b6Nh4QoPw,677
|
8
|
+
jk_prettyprintobj/dumper.py,sha256=Z1wGB1qa-dPHbu_C799bLTf1e9EkFdumrGDUHCxpI3I,18727
|
9
|
+
jk_prettyprintobj-0.2024.10.20.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
10
|
+
jk_prettyprintobj-0.2024.10.20.dist-info/METADATA,sha256=iDuW49l_yZmpMJ2XsjGLf0bhRsh6xML-nV9tNsu5HG4,8317
|
11
|
+
jk_prettyprintobj-0.2024.10.20.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
jk_prettyprintobj/_Bits.py,sha256=EVAk9bMNpnuv_re8hi5vk4znK2xYHsceR07RG_0f4x0,805
|
2
|
-
jk_prettyprintobj/_Hex.py,sha256=RsqEIp8N4ms48aDCYIV1TLQxexd0TnxwRmnx91Y0MQg,515
|
3
|
-
jk_prettyprintobj/__init__.py,sha256=a0raxboYf240pXJUtBjWbnvO7NFSnIrzqBfGdU1tZxQ,153
|
4
|
-
jk_prettyprintobj/dumper.py,sha256=fA-jTweGAR25gpLna7Y16R7BSsvNolNwqvyEr0Po-Q4,20909
|
5
|
-
jk_prettyprintobj-0.2024.2.18.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
6
|
-
jk_prettyprintobj-0.2024.2.18.dist-info/METADATA,sha256=8Acm9i54Kf1ipBkDXKYfBG--0i9TWUecdIsv23VNPYY,8316
|
7
|
-
jk_prettyprintobj-0.2024.2.18.dist-info/RECORD,,
|
File without changes
|