Coverage for gws-app/gws/spec/core.py: 100%
192 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"""Core data structures and constants for the specs."""
3from typing import TypeAlias, Any
4import os
7class Error(Exception):
8 """Base class for spec errors."""
10 pass
13class GeneratorError(Error):
14 """Raised when the spec generator fails."""
16 pass
19class ReadError(Error):
20 """Raised when a value does not match its spec type.
22 The arguments are the message and the offending value. With verbose errors,
23 a ``gws.ConfigErrorInfo`` object is added as the third argument.
24 """
26 pass
29class LoadError(Error):
30 """Raised when a class cannot be loaded from its module."""
32 pass
35class c:
36 """Type kinds, the values of ``Type.c``."""
38 ATOM = 'ATOM'
39 """Atomic, one of the built-in types."""
40 CLASS = 'CLASS'
41 """Class, a user-defined type."""
42 CALLABLE = 'CALLABLE'
43 """Callable, a callable argument."""
44 CONSTANT = 'CONSTANT'
45 """Constant type."""
46 DICT = 'DICT'
47 """Generic dictionary type."""
48 ENUM = 'ENUM'
49 """Enum type."""
50 EXPR = 'EXPR'
51 """Compile-time expression type."""
52 FUNCTION = 'FUNCTION'
53 """Function, a callable type."""
54 LIST = 'LIST'
55 """Generic list type."""
56 LITERAL = 'LITERAL'
57 """Literal type."""
58 METHOD = 'METHOD'
59 """Method, a callable type with a specific signature."""
60 MODULE = 'MODULE'
61 """Module type."""
62 NONE = 'NONE'
63 """None type."""
64 OPTIONAL = 'OPTIONAL'
65 """Optional, a type that can be None."""
66 PROPERTY = 'PROPERTY'
67 """Property, a type that is a property of a class."""
68 SET = 'SET'
69 """Generic set type."""
70 TUPLE = 'TUPLE'
71 """Generic tuple type."""
72 TYPE = 'TYPE'
73 """Type alias."""
74 UNION = 'UNION'
75 """Union, a type that can be one of several types."""
76 UNDEFINED = 'UNDEFINED'
77 """Undefined, a type that is not defined."""
78 VARIANT = 'VARIANT'
79 """Variant, a type that can be one of several types with a tag."""
81 EXT = 'EXT'
82 """Extension, a ``gws.ext`` alias."""
83 COMMAND = 'COMMAND'
84 """Command, a method decorated as ``gws.ext.command``."""
87TypeKind: TypeAlias = str
88"""Type kind, one of the constants in ``c``."""
89TypeUid: TypeAlias = str
90"""Type unique identifier, a string that identifies the type."""
93class Type:
94 """Spec type record, describes a single type, property, method or module.
96 Which fields are populated depends on the type kind ``c``.
97 """
99 c: TypeKind
100 """Type kind, one of the constants in ``c``."""
101 uid: TypeUid
102 """Type unique identifier, a string that identifies the type."""
104 extName: str = ''
105 """``gws.ext`` name of an extension type, variant or command method, if any."""
107 title: str = ''
108 """Documentation title, filled from the strings by ``get_config_types``."""
109 doc: str = ''
110 """Documentation string for the type."""
111 ident: str = ''
112 """Source code identifier (class, property or method name), used in the documentation."""
113 name: str = ''
114 """Qualified name of a named type, e.g. ``gws.base.layer.core.Config``."""
115 pos: str = ''
116 """Source code position of the definition, as ``path:line``."""
118 modName: str = ''
119 """Name of the module that defines this type."""
120 modPath: str = ''
121 """Path to the module that defines this type."""
123 tArg: TypeUid = ''
124 """For ``METHOD`` types, type uid of the last (request) argument."""
125 tItem: TypeUid = ''
126 """For ``LIST`` and ``SET`` types, type uid of the item."""
127 tKey: TypeUid = ''
128 """For ``DICT`` types, type uid of the key."""
129 tModule: TypeUid = ''
130 """Type uid of the type's module."""
131 tOwner: TypeUid = ''
132 """For ``PROPERTY`` and ``METHOD`` types, type uid of the owning class."""
133 tReturn: TypeUid = ''
134 """For ``METHOD`` types, type uid of the return value."""
135 tTarget: TypeUid = ''
136 """For ``TYPE``, ``EXT`` and ``OPTIONAL`` types, type uid of the target type."""
137 tValue: TypeUid = ''
138 """For ``PROPERTY`` types, type uid of the value; for ``DICT`` types, type uid of the dict values."""
140 tArgs: list[TypeUid] = []
141 """For ``METHOD`` types, type uids of the arguments."""
142 tItems: list[TypeUid] = []
143 """For ``UNION``, ``TUPLE`` and ``CALLABLE`` types, type uids of the items."""
144 tSupers: list[TypeUid] = []
145 """For ``CLASS`` types, type uids of the base classes."""
146 tMembers: dict[str, TypeUid] = {}
147 """For ``VARIANT`` types, member type uids keyed by the ``type`` tag."""
148 tProperties: dict[str, TypeUid] = {}
149 """For ``CLASS`` types, property type uids keyed by property name, including inherited properties."""
151 defaultValue: Any = None
152 """Default value for a property."""
153 defaultExpression: Any = None
154 """Unevaluated default of a property (a constant or enum reference), evaluated by the normalizer."""
155 hasDefault: bool = False
156 """True if the type has a default value."""
157 constValue: Any = None
158 """Constant value for a constant type."""
160 enumDocs: dict = {}
161 """For ``ENUM`` types, member docstrings keyed by member name."""
162 enumValues: dict = {}
163 """For ``ENUM`` types, member values keyed by member name."""
165 literalValues: list = []
166 """For ``LITERAL`` types, the allowed values."""
168 isConfig: bool = False
169 """True if the type is reachable from the application ``Config``."""
172def make_type(args: dict):
173 """Create a ``Type`` object.
175 Args:
176 args: Attribute values for the type.
178 Returns:
179 A new ``Type`` object.
180 """
182 typ = Type()
183 vars(typ).update(args)
184 return typ
187class Chunk:
188 """Source code chunk, the core packages or a plugin with their source files."""
190 name: str
191 """Name of the chunk."""
192 sourceDir: str
193 """Source directory of the chunk."""
194 bundleDir: str
195 """Directory where the client bundle of the chunk is stored."""
196 paths: dict[str, list[str]]
197 """Source file paths, grouped by file kind (``python``, ``ts``, ``css``, ``theme``, ``strings``)."""
198 exclude: list[str]
199 """Path fragments to exclude from the chunk."""
202class SpecData:
203 """Specs data, produced by the generator and loaded by the runtime."""
205 meta: dict
206 """Build-time metadata: ``version``, ``manifestPath`` and ``manifest``."""
207 chunks: list[Chunk]
208 """Source code chunks of the application and its plugins."""
209 serverTypes: list[Type]
210 """Types used by the server: configuration, request and response types, ext objects and command methods."""
211 strings: dict[str, dict[str, str]]
212 """Documentation strings keyed by language code and type uid."""
215class v:
216 """Constants for the spec generator and runtime."""
218 APP_NAME = 'gws'
219 """Application package name."""
220 EXT_PREFIX = APP_NAME + '.ext'
221 """Prefix of all extension names."""
222 EXT_DECL_PREFIX = EXT_PREFIX + '.new.'
223 """Prefix of ``gws.ext.new`` declarations in the sources."""
224 EXT_CONFIG_PREFIX = EXT_PREFIX + '.config.'
225 """Prefix of extension config names."""
226 EXT_PROPS_PREFIX = EXT_PREFIX + '.props.'
227 """Prefix of extension props names."""
228 EXT_OBJECT_PREFIX = EXT_PREFIX + '.object.'
229 """Prefix of extension object names."""
230 EXT_COMMAND_PREFIX = EXT_PREFIX + '.command.'
231 """Prefix of command names."""
233 EXT_COMMAND_API_PREFIX = EXT_COMMAND_PREFIX + 'api.'
234 """Prefix of API command names."""
235 EXT_COMMAND_GET_PREFIX = EXT_COMMAND_PREFIX + 'get.'
236 """Prefix of web GET command names."""
237 EXT_COMMAND_CLI_PREFIX = EXT_COMMAND_PREFIX + 'cli.'
238 """Prefix of CLI command names."""
240 EXT_OBJECT_CLASS = 'Object'
241 """Default object class name in a ``gws.ext.new`` declaration."""
242 EXT_CONFIG_CLASS = 'Config'
243 """Default config class name in a ``gws.ext.new`` declaration."""
244 EXT_PROPS_CLASS = 'Props'
245 """Default props class name in a ``gws.ext.new`` declaration."""
247 CLIENT_NAME = 'gc'
248 """Name of the client chunk."""
249 VARIANT_TAG = 'type'
250 """Name of the property that selects the member of a variant."""
251 DEFAULT_VARIANT_TAG = 'default'
252 """Variant member used when the tag property is missing."""
254 ATOMS = ['any', 'bool', 'bytes', 'float', 'int', 'str']
255 """Names of atomic types."""
257 BUILTINS = ATOMS + ['type', 'object', 'Exception', 'dict', 'list', 'set', 'tuple']
258 """Built-in names, registered as ``ATOM`` types."""
260 BUILTIN_TYPES = [
261 'Any',
262 'Callable',
263 'ContextManager',
264 'Dict',
265 'Enum',
266 'Iterable',
267 'Iterator',
268 'List',
269 'Literal',
270 'Optional',
271 'Protocol',
272 'Set',
273 'Tuple',
274 'TypeAlias',
275 'Union',
276 # imported in TYPE_CHECKING
277 'datetime.datetime',
278 'osgeo',
279 'sqlalchemy',
280 # vendor libs
281 'gws.lib.vendor',
282 'gws.lib.sa',
283 ]
284 """Names from ``typing`` and foreign modules that are treated as built-in."""
286 # those star-imported in gws/__init__.py
287 GLOBAL_MODULES = [
288 APP_NAME + '.core.const',
289 APP_NAME + '.core.util',
290 ]
291 """Modules whose names are available directly as ``gws.<Name>``."""
293 DEFAULT_EXT_SUPERS = {
294 'config': APP_NAME + '.core.types.ConfigWithAccess',
295 'props': APP_NAME + '.core.types.Props',
296 }
297 """Default base classes for synthesized ext config and props classes."""
299 # prefix for gws.plugin class names
300 PLUGIN_PREFIX = APP_NAME + '.plugin'
302 # inline comment symbol
303 INLINE_COMMENT_SYMBOL = '#:'
305 # where we are
306 SELF_DIR = os.path.dirname(__file__)
308 # path to `/repository-root/app`
309 APP_DIR = os.path.abspath(SELF_DIR + '/../..')
311 EXCLUDE_PATHS = ['___', '/vendor/', 'test', 'core/ext', '__pycache__']
312 """Path fragments of source files the generator skips."""
314 FILE_KINDS = [
315 ['.py', 'python'],
316 ['/index.ts', 'ts'],
317 ['/index.tsx', 'ts'],
318 ['/index.css.js', 'css'],
319 ['.theme.css.js', 'theme'],
320 ['/strings.ini', 'strings'],
321 ]
322 """Source file suffixes and the file kinds they map to."""
324 PLUGIN_DIR = '/gws/plugin'
325 """Directory of the built-in plugins, relative to the app directory."""
327 SYSTEM_CHUNKS = [
328 [CLIENT_NAME, f'/js/src/{CLIENT_NAME}'],
329 [f'{APP_NAME}.core', '/gws/core'],
330 [f'{APP_NAME}.base', '/gws/base'],
331 [f'{APP_NAME}.gis', '/gws/gis'],
332 [f'{APP_NAME}.lib', '/gws/lib'],
333 [f'{APP_NAME}.server', '/gws/server'],
334 [f'{APP_NAME}.helper', '/gws/helper'],
335 ]
336 """Names and source directories of the system chunks."""