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
« 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."""
3from . import base
4from .base import Type
7def extract(gen: base.Generator):
8 """Extract the server types into ``gen.serverTypes``.
10 The server needs:
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.
16 Types reachable from the application ``Config`` are marked with ``isConfig``.
17 The result is sorted by uid.
19 Args:
20 gen: Generator state.
22 Raises:
23 ``GeneratorError``: If a referenced type is unknown or cannot be extracted.
24 """
26 p = _Extractor(gen)
27 p.run()
30class _Extractor:
31 """Walks the types from a queue of start types and collects them."""
33 out: dict[str, Type] = {}
34 """Extracted types, keyed by uid."""
35 queue: list[str] = []
36 """Uids of the types to process."""
38 def __init__(self, gen: base.Generator):
39 self.gen = gen
41 def run(self):
42 """Extract the server types and store them in the generator."""
44 self.out = {}
45 self.queue = []
46 self.extract()
47 self.gen.serverTypes = [self.out[uid] for uid in sorted(self.out)]
49 def add(self, typ: Type, **kwargs):
50 """Add a type to the output, setting its module name and path.
52 Args:
53 typ: Type to add.
54 **kwargs: Additional attributes to set, e.g. ``isConfig``.
55 """
57 kwargs.setdefault('isConfig', False)
59 mod = self.gen.get_type(typ.tModule)
60 if mod:
61 kwargs['modName'] = mod.name
62 kwargs['modPath'] = mod.modPath
64 vars(typ).update(kwargs)
65 self.out[typ.uid] = typ
67 def extract(self):
68 """Extract the config types, the application object and all ext types."""
70 # config-related types
71 self.queue = ['gws.base.application.core.Config']
72 self.extract_all(isConfig=True)
74 # application objects, including methods and their args/rets
75 self.queue = ['gws.base.application.core.Object']
76 self.extract_all()
78 # ext objects
79 self.queue = list(set(typ.uid for typ in self.gen.typeDict.values() if typ.extName))
80 self.extract_all()
82 def extract_all(self, **kwargs):
83 """Process the queue until it is empty.
85 Args:
86 **kwargs: Attributes to set on each extracted type.
87 """
89 while self.queue:
90 self.extract_one(**kwargs)
92 def extract_one(self, **kwargs):
93 """Process the next type in the queue and enqueue the types it refers to.
95 Args:
96 **kwargs: Attributes to set on the extracted type.
98 Raises:
99 ``GeneratorError``: If the type is unknown or of a kind that cannot be extracted.
100 """
102 typ = self.gen.require_type(self.queue.pop(0))
104 if typ.uid in self.out or typ.c == base.c.ATOM:
105 return
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
113 if typ.c == base.c.CLASS and typ.extName.startswith(base.v.EXT_OBJECT_PREFIX):
114 self.add(typ, **kwargs)
115 return
117 if typ.c == base.c.CLASS:
118 self.add(typ, **kwargs)
119 self.queue.extend(typ.tProperties.values())
120 return
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
128 if typ.c in {base.c.LIST, base.c.SET}:
129 self.add(typ, **kwargs)
130 self.queue.append(typ.tItem)
131 return
133 if typ.c in {base.c.OPTIONAL, base.c.TYPE}:
134 self.add(typ, **kwargs)
135 self.queue.append(typ.tTarget)
136 return
138 if typ.c in {base.c.TUPLE, base.c.UNION}:
139 self.add(typ, **kwargs)
140 self.queue.extend(typ.tItems)
141 return
143 if typ.c == base.c.VARIANT:
144 self.add(typ, **kwargs)
145 self.queue.extend(typ.tMembers.values())
146 return
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
154 if typ.c == base.c.CLASS:
155 self.add(typ, **kwargs)
156 self.queue.extend(typ.tProperties.values())
157 return
159 if typ.c == base.c.ENUM:
160 self.add(typ, **kwargs)
161 return
163 if typ.c == base.c.LITERAL:
164 self.add(typ, **kwargs)
165 return
167 if typ.c == base.c.EXT:
168 return
170 raise base.GeneratorError(f'unbound object {typ.c}: {typ.uid!r} in {typ.pos}')