Coverage for gws-app/gws/plugin/model_value/expression/__init__.py: 77%

31 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-23 20:06 +0200

1"""Expression value. 

2 

3Computes the value by evaluating a Python expression. The expression is 

4compiled at configuration time; a syntax error is a configuration error. 

5 

6The following variables are available in the expression: 

7 

8- ``app``: the Application object, 

9- ``user``: the current user, 

10- ``project``: the current project, 

11- ``feature``: the feature the value is computed for, 

12- ``mc``: the ``gws.ModelContext`` object, 

13- ``date``: the ``gws.lib.datetimex`` module. 

14 

15Additional modules can be made available with the ``imports`` option, a list 

16of module names, e.g. ``["math", "os.path"]``. Each module is available under 

17its top-level package name, so ``os.path`` is used as ``os.path.join(...)``. 

18If a module cannot be imported or the evaluation fails, the error is logged 

19and the value is None. 

20 

21Example:: 

22 

23 fields+ { 

24 name "area_ha" 

25 type "float" 

26 values+ { 

27 type "expression" 

28 expression "round(feature.get('area_m2', 0) / 10000, 2)" 

29 } 

30 } 

31""" 

32 

33from typing import Optional 

34import gws 

35import gws.base.model.value 

36import gws.lib.datetimex 

37 

38 

39@gws.ext.config.modelValue('expression') 

40class Config(gws.base.model.value.Config): 

41 """Value computed by a Python expression.""" 

42 

43 expression: str 

44 """Python expression to evaluate.""" 

45 

46 imports: Optional[list[str]] 

47 """Additional Python modules available to the expression.""" 

48 

49 

50@gws.ext.object.modelValue('expression') 

51class Object(gws.base.model.value.Object): 

52 """Expression value object.""" 

53 

54 expression: str 

55 """Python expression to evaluate.""" 

56 imports: list[str] 

57 """Names of modules imported for the expression.""" 

58 

59 def configure(self): 

60 self.expression = (self.cfg('expression') or '').strip() 

61 self.imports = self.cfg('imports') or [] 

62 try: 

63 compile(self.expression, 'expression', 'eval') 

64 except Exception as exc: 

65 raise gws.ConfigurationError(f'invalid expression: {exc!r}') from exc 

66 

67 def compute(self, field, feature, mc): 

68 context = { 

69 'app': self.root.app, 

70 'user': mc.user, 

71 'project': mc.project, 

72 'feature': feature, 

73 'mc': mc, 

74 'date': gws.lib.datetimex, 

75 } 

76 for mod in self.imports: 

77 try: 

78 context[mod.split('.')[0]] = __import__(mod) 

79 except ImportError as exc: 

80 gws.log.error(f'failed to import module {mod!r}: {exc!r}') 

81 return 

82 

83 try: 

84 return eval(self.expression, context) 

85 except Exception as exc: 

86 gws.log.error(f'failed to compute expression: {exc!r}')