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 22:59 +0200

1"""Take a pickled GWS tree and output a browsable HTML page.""" 

2 

3import builtins 

4import json 

5import os 

6import pickle 

7import sys 

8 

9 

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) 

14 

15 

16## 

17 

18class Unpickler(pickle.Unpickler): 

19 def find_class(self, module, name): 

20 if module == 'builtins': 

21 return getattr(builtins, name) 

22 

23 class T: 

24 CLASS_NAME = module + '.' + name 

25 

26 def __init__(self, *args): 

27 pass 

28 

29 def __setstate__(self, state): 

30 if not isinstance(state, dict): 

31 state = {'?': state} 

32 self.STATE = state 

33 

34 def __getattr__(self, item): 

35 return '?' + item 

36 

37 def __call__(self, *args, **kwargs): 

38 pass 

39 

40 def __setitem__(self, *args, **kwargs): 

41 pass 

42 

43 def _unpickle(self, *args): 

44 pass 

45 

46 return T 

47 

48 

49class ConfigPrinter: 

50 def __init__(self, path, mode): 

51 self.path = path 

52 self.mode = mode 

53 self.obj_list = [] 

54 self.hash_map = {} 

55 

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() 

62 

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() 

69 

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) 

82 

83 def transform_object(self, obj): 

84 h = hash(obj) 

85 if h in self.hash_map: 

86 return self.hash_map[h] 

87 

88 idx = len(self.obj_list) 

89 self.obj_list.append('DUMMY_VALUE') 

90 

91 cls = obj.CLASS_NAME 

92 if isinstance(obj, type): 

93 cls = 'CLASS.' + cls 

94 

95 state = getattr(obj, 'STATE', {}) 

96 if not isinstance(state, dict): 

97 state = {'state': repr(state)} 

98 

99 ref = '$.' + cls + ':' + str(idx) 

100 if state.get('uid'): 

101 ref += ':uid=' + str(state['uid']) 

102 self.hash_map[h] = ref 

103 

104 d = {str(k): self.transform(v) for k, v in state.items()} 

105 d['$'] = ref 

106 self.obj_list[idx] = d 

107 

108 return ref 

109 

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 [] 

119 

120 ref_map = {d['$']: d for d in self.obj_list} 

121 

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) 

129 

130 

131 def sort_obj_list(self): 

132 weights = { 

133 '$.gws.Root': 0, 

134 '$.gws.base.application.core': 1, 

135 '$.gws.core': 2, 

136 

137 '$.gws.base': 20, 

138 '$.gws.config': 30, 

139 '$.gws.ext': 40, 

140 '$.gws.gis': 50, 

141 '$.gws.lib': 60, 

142 '$.gws.server': 70, 

143 

144 '$.gws.plugin': 80, 

145 '$.gws.test': 90, 

146 '$.gws.spec': 999, 

147 } 

148 

149 def skey(obj): 

150 ref = obj['$'] 

151 p = ref.split(':') 

152 

153 cls = p[0] 

154 try: 

155 idx = int(p[1]) 

156 except: 

157 idx = p[1] 

158 

159 for k, w in weights.items(): 

160 if cls.startswith(k): 

161 return w, cls, idx, ref.lower() 

162 

163 return 100, cls, idx, ref.lower() 

164 

165 self.obj_list.sort(key=skey) 

166 

167 DEBUG = False 

168 

169 def html_resources(self): 

170 cdir = os.path.dirname(__file__) 

171 

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 ] 

178 

179 rs = [] 

180 

181 def read(path): 

182 with open(path, 'rt') as fp: 

183 return fp.read() 

184 

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>') 

198 

199 return '\n'.join(rs) 

200 

201 

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() 

205 

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>&times;</button> 

218 </div> 

219 </div> 

220 <div id="sidebar-body"></div> 

221 </div> 

222 <div id="main"></div> 

223 </div> 

224 

225 {res} 

226  

227 <script id="OBJ_LIST" type="text/plain" defer> 

228 {obj_list} 

229 </script> 

230 </body> 

231 </html> 

232 """ 

233 

234 def render_json(self): 

235 return json.dumps(self.obj_list, indent=4, sort_keys=True) 

236 

237 

238if __name__ == '__main__': 

239 main()