Coverage for gws-app/gws/spec/generator/extractor.py: 96%

90 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""Select the types the server needs at run time.""" 

2 

3from . import base 

4from .base import Type 

5 

6 

7def extract(gen: base.Generator): 

8 """Extract the server types into ``gen.serverTypes``. 

9 

10 The server needs: 

11 

12 - all ``gws.ext.object`` classes, but not their properties, 

13 - all ``gws.ext.config`` and ``gws.ext.props`` classes and their properties, recursively, 

14 - command methods (``gws.ext.command``), their owners and their arguments, recursively. 

15 

16 Types reachable from the application ``Config`` are marked with ``isConfig``. 

17 The result is sorted by uid. 

18 

19 Args: 

20 gen: Generator state. 

21 

22 Raises: 

23 ``GeneratorError``: If a referenced type is unknown or cannot be extracted. 

24 """ 

25 

26 p = _Extractor(gen) 

27 p.run() 

28 

29 

30class _Extractor: 

31 """Walks the types from a queue of start types and collects them.""" 

32 

33 out: dict[str, Type] = {} 

34 """Extracted types, keyed by uid.""" 

35 queue: list[str] = [] 

36 """Uids of the types to process.""" 

37 

38 def __init__(self, gen: base.Generator): 

39 self.gen = gen 

40 

41 def run(self): 

42 """Extract the server types and store them in the generator.""" 

43 

44 self.out = {} 

45 self.queue = [] 

46 self.extract() 

47 self.gen.serverTypes = [self.out[uid] for uid in sorted(self.out)] 

48 

49 def add(self, typ: Type, **kwargs): 

50 """Add a type to the output, setting its module name and path. 

51 

52 Args: 

53 typ: Type to add. 

54 **kwargs: Additional attributes to set, e.g. ``isConfig``. 

55 """ 

56 

57 kwargs.setdefault('isConfig', False) 

58 

59 mod = self.gen.get_type(typ.tModule) 

60 if mod: 

61 kwargs['modName'] = mod.name 

62 kwargs['modPath'] = mod.modPath 

63 

64 vars(typ).update(kwargs) 

65 self.out[typ.uid] = typ 

66 

67 def extract(self): 

68 """Extract the config types, the application object and all ext types.""" 

69 

70 # config-related types 

71 self.queue = ['gws.base.application.core.Config'] 

72 self.extract_all(isConfig=True) 

73 

74 # application objects, including methods and their args/rets 

75 self.queue = ['gws.base.application.core.Object'] 

76 self.extract_all() 

77 

78 # ext objects 

79 self.queue = list(set(typ.uid for typ in self.gen.typeDict.values() if typ.extName)) 

80 self.extract_all() 

81 

82 def extract_all(self, **kwargs): 

83 """Process the queue until it is empty. 

84 

85 Args: 

86 **kwargs: Attributes to set on each extracted type. 

87 """ 

88 

89 while self.queue: 

90 self.extract_one(**kwargs) 

91 

92 def extract_one(self, **kwargs): 

93 """Process the next type in the queue and enqueue the types it refers to. 

94 

95 Args: 

96 **kwargs: Attributes to set on the extracted type. 

97 

98 Raises: 

99 ``GeneratorError``: If the type is unknown or of a kind that cannot be extracted. 

100 """ 

101 

102 typ = self.gen.require_type(self.queue.pop(0)) 

103 

104 if typ.uid in self.out or typ.c == base.c.ATOM: 

105 return 

106 

107 if typ.c == base.c.METHOD and typ.extName.startswith(base.v.EXT_COMMAND_PREFIX): 

108 self.add(typ, **kwargs) 

109 self.queue.append(typ.tOwner) 

110 self.queue.append(typ.tArg) 

111 return 

112 

113 if typ.c == base.c.CLASS and typ.extName.startswith(base.v.EXT_OBJECT_PREFIX): 

114 self.add(typ, **kwargs) 

115 return 

116 

117 if typ.c == base.c.CLASS: 

118 self.add(typ, **kwargs) 

119 self.queue.extend(typ.tProperties.values()) 

120 return 

121 

122 if typ.c == base.c.DICT: 

123 self.add(typ, **kwargs) 

124 self.queue.append(typ.tKey) 

125 self.queue.append(typ.tValue) 

126 return 

127 

128 if typ.c in {base.c.LIST, base.c.SET}: 

129 self.add(typ, **kwargs) 

130 self.queue.append(typ.tItem) 

131 return 

132 

133 if typ.c in {base.c.OPTIONAL, base.c.TYPE}: 

134 self.add(typ, **kwargs) 

135 self.queue.append(typ.tTarget) 

136 return 

137 

138 if typ.c in {base.c.TUPLE, base.c.UNION}: 

139 self.add(typ, **kwargs) 

140 self.queue.extend(typ.tItems) 

141 return 

142 

143 if typ.c == base.c.VARIANT: 

144 self.add(typ, **kwargs) 

145 self.queue.extend(typ.tMembers.values()) 

146 return 

147 

148 if typ.c == base.c.PROPERTY: 

149 self.add(typ, **kwargs) 

150 self.queue.append(typ.tValue) 

151 self.queue.append(typ.tOwner) 

152 return 

153 

154 if typ.c == base.c.CLASS: 

155 self.add(typ, **kwargs) 

156 self.queue.extend(typ.tProperties.values()) 

157 return 

158 

159 if typ.c == base.c.ENUM: 

160 self.add(typ, **kwargs) 

161 return 

162 

163 if typ.c == base.c.LITERAL: 

164 self.add(typ, **kwargs) 

165 return 

166 

167 if typ.c == base.c.EXT: 

168 return 

169 

170 raise base.GeneratorError(f'unbound object {typ.c}: {typ.uid!r} in {typ.pos}')