Coverage for gws-app/gws/spec/generator/main.py: 77%

100 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 12:46 +0200

1import os 

2 

3from .. import core 

4from . import base, configref, manifest, normalizer, parser, extractor, strings, typescript, util 

5 

6Error = base.Error 

7 

8 

9def generate_and_write(root_dir='', out_dir='', manifest_path='', debug=False): 

10 """Generate the specs and write them to disk.""" 

11 

12 base.log.set_level('DEBUG' if debug else 'INFO') 

13 

14 gen = _run_generator(root_dir, out_dir, manifest_path, debug) 

15 

16 to_path(f'{gen.outDir}/specs.json', gen.specData) 

17 

18 util.write_file(f'{gen.outDir}/gws.generated.ts', gen.typescript) 

19 

20 util.write_file(f'{gen.outDir}/configref.en.md', gen.configRef['en']) 

21 util.write_file(f'{gen.outDir}/configref.de.md', gen.configRef['de']) 

22 

23 

24def generate(manifest_path='') -> core.SpecData: 

25 """Generate the specs and return them as a data object.""" 

26 

27 gen = _run_generator(manifest_path=manifest_path) 

28 return gen.specData 

29 

30 

31def to_path(path: str, specs: core.SpecData): 

32 """Write the specs to a JSON file.""" 

33 

34 util.write_json( 

35 path, 

36 { 

37 'meta': specs.meta, 

38 'chunks': specs.chunks, 

39 'serverTypes': specs.serverTypes, 

40 'strings': specs.strings, 

41 }, 

42 ) 

43 return path 

44 

45 

46def from_path(path: str) -> core.SpecData: 

47 """Load the specs from a JSON file.""" 

48 

49 d = util.read_json(path) 

50 s = core.SpecData() 

51 s.meta = d['meta'] 

52 s.chunks = [core.Chunk(**c) for c in d['chunks']] 

53 s.serverTypes = [core.make_type(t) for t in d['serverTypes']] 

54 s.strings = d['strings'] 

55 return s 

56 

57 

58## 

59 

60def _run_generator(root_dir='', out_dir='', manifest_path='', debug=False): 

61 gen = base.Generator() 

62 gen.rootDir = root_dir or base.v.APP_DIR 

63 gen.outDir = out_dir 

64 gen.selfDir = base.v.SELF_DIR 

65 gen.debug = debug 

66 gen.manifestPath = manifest_path 

67 

68 _init_generator(gen) 

69 gen.dump('000_init') 

70 

71 parser.parse(gen) 

72 gen.dump('001_parsed') 

73 

74 normalizer.normalize(gen) 

75 gen.dump('002_normalized') 

76 

77 extractor.extract(gen) 

78 gen.dump('003_extracted') 

79 

80 gen.typescript = typescript.create(gen) 

81 gen.strings = strings.collect(gen) 

82 

83 gen.configRef['en'] = configref.create(gen, 'en') 

84 gen.configRef['de'] = configref.create(gen, 'de') 

85 

86 gen.specData = core.SpecData() 

87 gen.specData.meta = gen.meta 

88 gen.specData.chunks = gen.chunks 

89 gen.specData.serverTypes = gen.serverTypes 

90 gen.specData.strings = gen.strings 

91 

92 return gen 

93 

94 

95def _init_generator(gen: base.Generator): 

96 gen.meta = { 

97 'version': util.read_file(gen.rootDir + '/VERSION').strip(), 

98 'manifestPath': None, 

99 'manifest': None, 

100 } 

101 

102 def _chunk(name, source_dir, bundle_dir): 

103 cc = core.Chunk() 

104 cc.name = name 

105 cc.sourceDir = source_dir 

106 cc.bundleDir = bundle_dir 

107 cc.paths = {kind: [] for _, kind in base.v.FILE_KINDS} 

108 cc.exclude = [] 

109 return cc 

110 

111 gen.chunks = [] 

112 

113 for name, path in base.v.SYSTEM_CHUNKS: 

114 cc = _chunk( 

115 name, 

116 gen.rootDir + path, 

117 base.v.APP_DIR, 

118 ) 

119 gen.chunks.append(cc) 

120 

121 manifest_plugins = None 

122 

123 if gen.manifestPath: 

124 try: 

125 base.log.debug(f'loading manifest {gen.manifestPath!r}') 

126 gen.meta['manifestPath'] = gen.manifestPath 

127 gen.meta['manifest'] = manifest.from_path(gen.manifestPath) 

128 except Exception as exc: 

129 raise base.GeneratorError(f'error loading manifest {gen.manifestPath!r}') from exc 

130 manifest_plugins = gen.meta['manifest'].get('plugins') 

131 

132 plugin_dict = {} 

133 

134 # our plugins 

135 for path in util.find_dirs(gen.rootDir + base.v.PLUGIN_DIR): 

136 name = os.path.basename(path) 

137 cc = _chunk( 

138 base.v.PLUGIN_PREFIX + '.' + name, 

139 path, 

140 path, 

141 ) 

142 plugin_dict[cc.name] = cc 

143 

144 # manifest plugins 

145 for p in manifest_plugins or []: 

146 path = p.get('path') 

147 name = p.get('name') or os.path.basename(path) 

148 if not os.path.isdir(path): 

149 raise base.GeneratorError(f'error loading plugin {name!r}: directory {path!r} not found') 

150 cc = _chunk( 

151 base.v.PLUGIN_PREFIX + '.' + name, 

152 path, 

153 path, 

154 ) 

155 plugin_dict[cc.name] = cc 

156 

157 gen.chunks.extend(plugin_dict.values()) 

158 

159 for chunk in gen.chunks: 

160 if not os.path.isdir(chunk.sourceDir): 

161 continue 

162 

163 excl = base.v.EXCLUDE_PATHS + (chunk.exclude or []) 

164 

165 for path in util.find_files(chunk.sourceDir): 

166 if any(x in path for x in excl): 

167 continue 

168 for pattern, kind in base.v.FILE_KINDS: 

169 if path.endswith(pattern): 

170 chunk.paths[kind].append(path) 

171 break 

172 

173 root_cc = _chunk( 

174 'gws', 

175 gen.rootDir + '/gws', 

176 gen.rootDir + '/gws', 

177 ) 

178 root_cc.paths['python'] = [gen.rootDir + '/gws/__init__.py'] 

179 gen.chunks.insert(0, root_cc)