Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/bones/code.py: 45%
48 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-25 15:02 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-25 15:02 +0000
1import ast
2import jinja2
3import logics
4from viur.core.bones.raw import RawBone
7class CodeBone(RawBone):
8 """
9 Stores source code with optional language-specific syntax validation.
11 The ``syntax`` parameter sets the type suffix used by the frontend for syntax
12 highlighting, e.g. ``syntax="python"`` yields ``type = "raw.code.python"``.
13 ``type_suffix`` can override this to an arbitrary suffix.
15 Neither ``multiple`` nor ``languages`` are supported.
16 Setting ``validate=False`` disables any syntax validation in subclasses.
17 """
19 type = "raw.code"
21 def __init__(
22 self,
23 *,
24 indexed: bool = False,
25 languages=None,
26 multiple: bool = False,
27 syntax: str | None = None,
28 type_suffix: str = "",
29 validate: bool = True,
30 **kwargs,
31 ):
32 assert not multiple, "CodeBone does not support multiple values"
33 assert not languages, "CodeBone does not support language variants"
34 self.syntax = syntax
35 self.validate = validate
36 if self.syntax: 36 ↛ 38line 36 didn't jump to line 38 because the condition on line 36 was always true
37 type_suffix = type_suffix or syntax
38 super().__init__(indexed=indexed, type_suffix=type_suffix, **kwargs)
41class LogicsBone(CodeBone):
42 """
43 Validates its value as a Logics expression (https://github.com/viur-framework/logics).
44 Uses Python syntax highlighting in the frontend.
45 """
47 def __init__(self, *, syntax: str = "logics", **kwargs):
48 super().__init__(syntax=syntax, type_suffix="python", **kwargs)
50 def singleValueFromClient(self, value, skel, bone_name, client_data):
51 if value := str(value or "").strip():
52 value += "\n"
53 return super().singleValueFromClient(value, skel, bone_name, client_data)
55 def isInvalid(self, value):
56 if self.validate and value:
57 try:
58 logics.Logics(value)
59 except logics.ParseException as e:
60 return str(e).replace("&eof", "end-of-expression")
63class JinjaBone(CodeBone):
64 """
65 Validates its value as a Jinja2 template.
66 """
68 def __init__(self, *, syntax: str = "jinja2", **kwargs):
69 super().__init__(syntax=syntax, **kwargs)
71 def isInvalid(self, value):
72 if self.validate and value:
73 env = jinja2.Environment()
74 try:
75 env.parse(value)
76 except jinja2.TemplateSyntaxError as e:
77 return f"Syntax error in line {e.lineno}: {e.message}"
78 except jinja2.TemplateError as e:
79 return f"General error: {e}"
82class PythonBone(CodeBone):
83 """
84 Validates its value as Python source code.
85 """
87 def __init__(self, *, syntax: str = "python", **kwargs):
88 super().__init__(syntax=syntax, **kwargs)
90 def isInvalid(self, value):
91 if self.validate and value:
92 try:
93 ast.parse(value)
94 except SyntaxError as e:
95 return f"Syntax error in line {e.lineno}: {e.msg}"