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

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 Meta(type): 

24 def __getattr__(cls, item): 

25 return '??' + item 

26 

27 class T(metaclass=Meta): 

28 CLASS_NAME = module + '.' + name 

29 

30 def __init__(self, *args): 

31 pass 

32 

33 def __setstate__(self, state): 

34 if not isinstance(state, dict): 

35 state = {'?': state} 

36 self.STATE = state 

37 

38 def __getattr__(self, item): 

39 return '?' + item 

40 

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

42 pass 

43 

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

45 pass 

46 

47 def _unpickle(self, *args): 

48 pass 

49 

50 return T 

51 

52 

53class ConfigPrinter: 

54 def __init__(self, path, mode): 

55 self.path = path 

56 self.mode = mode 

57 self.obj_list = [] 

58 self.hash_map = {} 

59 

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

66 

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

73 

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) 

86 

87 def transform_object(self, obj): 

88 h = hash(obj) 

89 if h in self.hash_map: 

90 return self.hash_map[h] 

91 

92 idx = len(self.obj_list) 

93 self.obj_list.append('DUMMY_VALUE') 

94 

95 cls = obj.CLASS_NAME 

96 if isinstance(obj, type): 

97 cls = 'CLASS.' + cls 

98 

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

100 if not isinstance(state, dict): 

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

102 

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

104 if state.get('uid'): 

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

106 self.hash_map[h] = ref 

107 

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

109 d['$'] = ref 

110 self.obj_list[idx] = d 

111 

112 return ref 

113 

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

123 

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

125 

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) 

133 

134 

135 def sort_obj_list(self): 

136 weights = { 

137 '$.gws.Root': 0, 

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

139 '$.gws.core': 2, 

140 

141 '$.gws.base': 20, 

142 '$.gws.config': 30, 

143 '$.gws.ext': 40, 

144 '$.gws.gis': 50, 

145 '$.gws.lib': 60, 

146 '$.gws.server': 70, 

147 

148 '$.gws.plugin': 80, 

149 '$.gws.test': 90, 

150 '$.gws.spec': 999, 

151 } 

152 

153 def skey(obj): 

154 ref = obj['$'] 

155 p = ref.split(':') 

156 

157 cls = p[0] 

158 try: 

159 idx = int(p[1]) 

160 except: 

161 idx = p[1] 

162 

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

164 if cls.startswith(k): 

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

166 

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

168 

169 self.obj_list.sort(key=skey) 

170 

171 DEBUG = False 

172 

173 def html_resources(self): 

174 cdir = os.path.dirname(__file__) 

175 

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 ] 

182 

183 rs = [] 

184 

185 def read(path): 

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

187 return fp.read() 

188 

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

202 

203 return '\n'.join(rs) 

204 

205 

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

209 

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

222 </div> 

223 </div> 

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

225 </div> 

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

227 </div> 

228 

229 {res} 

230  

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

232 {obj_list} 

233 </script> 

234 </body> 

235 </html> 

236 """ 

237 

238 def render_json(self): 

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

240 

241 

242if __name__ == '__main__': 

243 main()