Coverage for gws-app/gws/lib/misc/tree_viewer/__init__.py: 0%
144 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 12:46 +0200
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 12:46 +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 Meta(type):
24 def __getattr__(cls, item):
25 return '??' + item
27 class T(metaclass=Meta):
28 CLASS_NAME = module + '.' + name
30 def __init__(self, *args):
31 pass
33 def __setstate__(self, state):
34 if not isinstance(state, dict):
35 state = {'?': state}
36 self.STATE = state
38 def __getattr__(self, item):
39 return '?' + item
41 def __call__(self, *args, **kwargs):
42 pass
44 def __setitem__(self, *args, **kwargs):
45 pass
47 def _unpickle(self, *args):
48 pass
50 return T
53class ConfigPrinter:
54 def __init__(self, path, mode):
55 self.path = path
56 self.mode = mode
57 self.obj_list = []
58 self.hash_map = {}
60 def run(self):
61 self.load_pickle()
62 if self.mode == 'html':
63 return self.render_html()
64 if self.mode == 'json':
65 return self.render_json()
67 def load_pickle(self):
68 with open(self.path, 'rb') as fp:
69 tree = Unpickler(fp).load()
70 self.transform(tree)
71 self.sort_obj_list()
72 self.make_refs()
74 def transform(self, obj):
75 if obj is None:
76 return obj
77 if isinstance(obj, (str, int, float, bool)):
78 return obj
79 if isinstance(obj, (list, tuple, set)):
80 return [self.transform(x) for x in obj]
81 if isinstance(obj, dict):
82 return {str(k): self.transform(v) for k, v in obj.items()}
83 if hasattr(obj, 'CLASS_NAME'):
84 return self.transform_object(obj)
85 return 'UNKNOWN: ' + repr(obj)
87 def transform_object(self, obj):
88 h = hash(obj)
89 if h in self.hash_map:
90 return self.hash_map[h]
92 idx = len(self.obj_list)
93 self.obj_list.append('DUMMY_VALUE')
95 cls = obj.CLASS_NAME
96 if isinstance(obj, type):
97 cls = 'CLASS.' + cls
99 state = getattr(obj, 'STATE', {})
100 if not isinstance(state, dict):
101 state = {'state': repr(state)}
103 ref = '$.' + cls + ':' + str(idx)
104 if state.get('uid'):
105 ref += ':uid=' + str(state['uid'])
106 self.hash_map[h] = ref
108 d = {str(k): self.transform(v) for k, v in state.items()}
109 d['$'] = ref
110 self.obj_list[idx] = d
112 return ref
114 def make_refs(self):
115 def flat(val):
116 if isinstance(val, str):
117 return [val]
118 if isinstance(val, (list, tuple, set)):
119 return [v for e in val for v in flat(e)]
120 if isinstance(val, dict):
121 return [v for e in val.values() for v in flat(e)]
122 return []
124 ref_map = {d['$']: d for d in self.obj_list}
126 for d in self.obj_list:
127 for prop, v in d.items():
128 if prop == '$' or prop.startswith('~'):
129 continue
130 for v2 in flat(v):
131 if v2 in ref_map and v2 != d['$']:
132 ref_map[v2].setdefault('~references', []).append(d['$'] + ' @' + prop)
135 def sort_obj_list(self):
136 weights = {
137 '$.gws.Root': 0,
138 '$.gws.base.application.core': 1,
139 '$.gws.core': 2,
141 '$.gws.base': 20,
142 '$.gws.config': 30,
143 '$.gws.ext': 40,
144 '$.gws.gis': 50,
145 '$.gws.lib': 60,
146 '$.gws.server': 70,
148 '$.gws.plugin': 80,
149 '$.gws.test': 90,
150 '$.gws.spec': 999,
151 }
153 def skey(obj):
154 ref = obj['$']
155 p = ref.split(':')
157 cls = p[0]
158 try:
159 idx = int(p[1])
160 except:
161 idx = p[1]
163 for k, w in weights.items():
164 if cls.startswith(k):
165 return w, cls, idx, ref.lower()
167 return 100, cls, idx, ref.lower()
169 self.obj_list.sort(key=skey)
171 DEBUG = False
173 def html_resources(self):
174 cdir = os.path.dirname(__file__)
176 paths = [
177 os.path.abspath(cdir + '/../../vendor/jvv/jvv.js'),
178 os.path.abspath(cdir + '/../../vendor/jvv/jvv.css'),
179 os.path.abspath(cdir + '/tree.css'),
180 os.path.abspath(cdir + '/tree.js'),
181 ]
183 rs = []
185 def read(path):
186 with open(path, 'rt') as fp:
187 return fp.read()
189 if self.DEBUG:
190 for p in paths:
191 p = p.split('lib')[-1]
192 if p.endswith('.js'):
193 rs.append(f'<script src="{p}"></script>')
194 if p.endswith('.css'):
195 rs.append(f'<link rel="stylesheet" href="{p}">')
196 else:
197 for p in paths:
198 if p.endswith('.js'):
199 rs.append(f'<script>\n' + read(p) + '</script>')
200 if p.endswith('.css'):
201 rs.append(f'<style>\n' + read(p) + '</style>')
203 return '\n'.join(rs)
206 def render_html(self):
207 obj_list = json.dumps(self.obj_list, indent=4, sort_keys=True).replace('</script>', '<\\/script>')
208 res = self.html_resources()
210 return f"""<!doctype html>
211 <html>
212 <head>
213 <meta charset="utf-8">
214 </head>
215 <body>
216 <div id="content">
217 <div id="sidebar">
218 <div id="sidebar-top">
219 <div id="sidebar-search-box">
220 <input>
221 <button>×</button>
222 </div>
223 </div>
224 <div id="sidebar-body"></div>
225 </div>
226 <div id="main"></div>
227 </div>
229 {res}
231 <script id="OBJ_LIST" type="text/plain" defer>
232 {obj_list}
233 </script>
234 </body>
235 </html>
236 """
238 def render_json(self):
239 return json.dumps(self.obj_list, indent=4, sort_keys=True)
242if __name__ == '__main__':
243 main()