Coverage for gws-app/gws/lib/misc/tree_viewer/__init__.py: 0%
141 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
1"""Take a pickled GWS tree and output a browsable HTML page."""
3import builtins
4import json
5import os
6import pickle
7import sys
10def main():
11 cp = ConfigPrinter(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else 'json')
12 out = cp.run()
13 print(out)
16##
18class Unpickler(pickle.Unpickler):
19 def find_class(self, module, name):
20 if module == 'builtins':
21 return getattr(builtins, name)
23 class T:
24 CLASS_NAME = module + '.' + name
26 def __init__(self, *args):
27 pass
29 def __setstate__(self, state):
30 if not isinstance(state, dict):
31 state = {'?': state}
32 self.STATE = state
34 def __getattr__(self, item):
35 return '?' + item
37 def __call__(self, *args, **kwargs):
38 pass
40 def __setitem__(self, *args, **kwargs):
41 pass
43 def _unpickle(self, *args):
44 pass
46 return T
49class ConfigPrinter:
50 def __init__(self, path, mode):
51 self.path = path
52 self.mode = mode
53 self.obj_list = []
54 self.hash_map = {}
56 def run(self):
57 self.load_pickle()
58 if self.mode == 'html':
59 return self.render_html()
60 if self.mode == 'json':
61 return self.render_json()
63 def load_pickle(self):
64 with open(self.path, 'rb') as fp:
65 tree = Unpickler(fp).load()
66 self.transform(tree)
67 self.sort_obj_list()
68 self.make_refs()
70 def transform(self, obj):
71 if obj is None:
72 return obj
73 if isinstance(obj, (str, int, float, bool)):
74 return obj
75 if isinstance(obj, (list, tuple, set)):
76 return [self.transform(x) for x in obj]
77 if isinstance(obj, dict):
78 return {str(k): self.transform(v) for k, v in obj.items()}
79 if hasattr(obj, 'CLASS_NAME'):
80 return self.transform_object(obj)
81 return 'UNKNOWN: ' + repr(obj)
83 def transform_object(self, obj):
84 h = hash(obj)
85 if h in self.hash_map:
86 return self.hash_map[h]
88 idx = len(self.obj_list)
89 self.obj_list.append('DUMMY_VALUE')
91 cls = obj.CLASS_NAME
92 if isinstance(obj, type):
93 cls = 'CLASS.' + cls
95 state = getattr(obj, 'STATE', {})
96 if not isinstance(state, dict):
97 state = {'state': repr(state)}
99 ref = '$.' + cls + ':' + str(idx)
100 if state.get('uid'):
101 ref += ':uid=' + str(state['uid'])
102 self.hash_map[h] = ref
104 d = {str(k): self.transform(v) for k, v in state.items()}
105 d['$'] = ref
106 self.obj_list[idx] = d
108 return ref
110 def make_refs(self):
111 def flat(val):
112 if isinstance(val, str):
113 return [val]
114 if isinstance(val, (list, tuple, set)):
115 return [v for e in val for v in flat(e)]
116 if isinstance(val, dict):
117 return [v for e in val.values() for v in flat(e)]
118 return []
120 ref_map = {d['$']: d for d in self.obj_list}
122 for d in self.obj_list:
123 for prop, v in d.items():
124 if prop == '$' or prop.startswith('~'):
125 continue
126 for v2 in flat(v):
127 if v2 in ref_map and v2 != d['$']:
128 ref_map[v2].setdefault('~references', []).append(d['$'] + ' @' + prop)
131 def sort_obj_list(self):
132 weights = {
133 '$.gws.Root': 0,
134 '$.gws.base.application.core': 1,
135 '$.gws.core': 2,
137 '$.gws.base': 20,
138 '$.gws.config': 30,
139 '$.gws.ext': 40,
140 '$.gws.gis': 50,
141 '$.gws.lib': 60,
142 '$.gws.server': 70,
144 '$.gws.plugin': 80,
145 '$.gws.test': 90,
146 '$.gws.spec': 999,
147 }
149 def skey(obj):
150 ref = obj['$']
151 p = ref.split(':')
153 cls = p[0]
154 try:
155 idx = int(p[1])
156 except:
157 idx = p[1]
159 for k, w in weights.items():
160 if cls.startswith(k):
161 return w, cls, idx, ref.lower()
163 return 100, cls, idx, ref.lower()
165 self.obj_list.sort(key=skey)
167 DEBUG = False
169 def html_resources(self):
170 cdir = os.path.dirname(__file__)
172 paths = [
173 os.path.abspath(cdir + '/../../vendor/jvv/jvv.js'),
174 os.path.abspath(cdir + '/../../vendor/jvv/jvv.css'),
175 os.path.abspath(cdir + '/tree.css'),
176 os.path.abspath(cdir + '/tree.js'),
177 ]
179 rs = []
181 def read(path):
182 with open(path, 'rt') as fp:
183 return fp.read()
185 if self.DEBUG:
186 for p in paths:
187 p = p.split('lib')[-1]
188 if p.endswith('.js'):
189 rs.append(f'<script src="{p}"></script>')
190 if p.endswith('.css'):
191 rs.append(f'<link rel="stylesheet" href="{p}">')
192 else:
193 for p in paths:
194 if p.endswith('.js'):
195 rs.append(f'<script>\n' + read(p) + '</script>')
196 if p.endswith('.css'):
197 rs.append(f'<style>\n' + read(p) + '</style>')
199 return '\n'.join(rs)
202 def render_html(self):
203 obj_list = json.dumps(self.obj_list, indent=4, sort_keys=True).replace('</script>', '<\\/script>')
204 res = self.html_resources()
206 return f"""<!doctype html>
207 <html>
208 <head>
209 <meta charset="utf-8">
210 </head>
211 <body>
212 <div id="content">
213 <div id="sidebar">
214 <div id="sidebar-top">
215 <div id="sidebar-search-box">
216 <input>
217 <button>×</button>
218 </div>
219 </div>
220 <div id="sidebar-body"></div>
221 </div>
222 <div id="main"></div>
223 </div>
225 {res}
227 <script id="OBJ_LIST" type="text/plain" defer>
228 {obj_list}
229 </script>
230 </body>
231 </html>
232 """
234 def render_json(self):
235 return json.dumps(self.obj_list, indent=4, sort_keys=True)
238if __name__ == '__main__':
239 main()