camunda-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- camunda/__init__.py +10 -0
- camunda/api/__init__.py +14 -0
- camunda/api/app.py +80 -0
- camunda/api/deps.py +17 -0
- camunda/api/errors.py +84 -0
- camunda/api/pagination.py +74 -0
- camunda/api/routers/__init__.py +5 -0
- camunda/api/routers/decision.py +57 -0
- camunda/api/routers/deployment.py +139 -0
- camunda/api/routers/history.py +128 -0
- camunda/api/routers/process_definition.py +49 -0
- camunda/api/routers/process_instance.py +106 -0
- camunda/api/routers/task.py +92 -0
- camunda/api/schemas.py +200 -0
- camunda/common/__init__.py +19 -0
- camunda/common/clock.py +30 -0
- camunda/common/exceptions.py +34 -0
- camunda/common/idgen.py +23 -0
- camunda/common/timers.py +106 -0
- camunda/dmn/__init__.py +5 -0
- camunda/dmn/engine.py +219 -0
- camunda/dmn/feel.py +392 -0
- camunda/engine/__init__.py +9 -0
- camunda/engine/behavior.py +51 -0
- camunda/engine/expression.py +126 -0
- camunda/engine/process_engine.py +3237 -0
- camunda/job/__init__.py +9 -0
- camunda/job/executor.py +136 -0
- camunda/model/__init__.py +48 -0
- camunda/model/bpmn.py +326 -0
- camunda/model/dmn.py +101 -0
- camunda/model/execution.py +121 -0
- camunda/model/job.py +88 -0
- camunda/model/task.py +33 -0
- camunda/model/variable.py +35 -0
- camunda/parser/__init__.py +5 -0
- camunda/parser/bpmn_parser.py +646 -0
- camunda/parser/dmn_parser.py +225 -0
- camunda/persistence/__init__.py +21 -0
- camunda/persistence/entities.py +202 -0
- camunda/persistence/store.py +721 -0
- camunda_python-0.1.0.dist-info/METADATA +377 -0
- camunda_python-0.1.0.dist-info/RECORD +46 -0
- camunda_python-0.1.0.dist-info/WHEEL +5 -0
- camunda_python-0.1.0.dist-info/licenses/LICENSE +200 -0
- camunda_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""条件表达式安全求值(M1 安全子集)。
|
|
2
|
+
|
|
3
|
+
对齐 Camunda 语义:sequenceFlow 的 conditionExpression 形如
|
|
4
|
+
"${amount > 1000}" 或 "${approved == true}"。Camunda 用 JUEL/SpEL,
|
|
5
|
+
Python 侧没有直接等价物,M1 采用 **ast 白名单安全求值**:
|
|
6
|
+
|
|
7
|
+
支持:
|
|
8
|
+
- 字面量:数字 / 字符串 / true / false / null(->None)
|
|
9
|
+
- 变量引用:必须是流程变量(未定义抛 ProcessInstanceException,避免静默错误)
|
|
10
|
+
- 运算符:比较 == != < <= > >=,in / not in
|
|
11
|
+
- 逻辑:and / or / not(与 && / || / ! 做文本归一化)
|
|
12
|
+
- 算术:+ - * / %(数值)
|
|
13
|
+
|
|
14
|
+
不支持(M4 扩展):方法调用、属性访问、日期时间函数、FEEL 语法。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import ast
|
|
20
|
+
import re
|
|
21
|
+
from typing import Any, Dict
|
|
22
|
+
|
|
23
|
+
from camunda.common.exceptions import ProcessInstanceException
|
|
24
|
+
|
|
25
|
+
_ALLOWED_NODES = (
|
|
26
|
+
ast.Expression,
|
|
27
|
+
ast.Compare,
|
|
28
|
+
ast.BoolOp,
|
|
29
|
+
ast.UnaryOp,
|
|
30
|
+
ast.BinOp,
|
|
31
|
+
ast.Name,
|
|
32
|
+
ast.Constant,
|
|
33
|
+
ast.List,
|
|
34
|
+
ast.Tuple,
|
|
35
|
+
ast.Load,
|
|
36
|
+
ast.Eq,
|
|
37
|
+
ast.NotEq,
|
|
38
|
+
ast.Lt,
|
|
39
|
+
ast.LtE,
|
|
40
|
+
ast.Gt,
|
|
41
|
+
ast.GtE,
|
|
42
|
+
ast.In,
|
|
43
|
+
ast.NotIn,
|
|
44
|
+
ast.And,
|
|
45
|
+
ast.Or,
|
|
46
|
+
ast.Not,
|
|
47
|
+
ast.USub,
|
|
48
|
+
ast.Add,
|
|
49
|
+
ast.Sub,
|
|
50
|
+
ast.Mult,
|
|
51
|
+
ast.Div,
|
|
52
|
+
ast.Mod,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
_UNARY_TEXT = {"!": "not "}
|
|
56
|
+
# 兼容 JUEL/SpEL 风格逻辑符 -> Python
|
|
57
|
+
_TEXT_NORMALIZE = [
|
|
58
|
+
("&&", " and "),
|
|
59
|
+
("||", " or "),
|
|
60
|
+
("==", "=="), # 占位保持通用
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _normalize(expr: str) -> str:
|
|
65
|
+
"""把常见 Java/JUEL 风格语法转成 Python 语法。
|
|
66
|
+
|
|
67
|
+
注意顺序:先处理 && ||,再处理单字符 !(避免误伤 != / !in)。
|
|
68
|
+
true/false/null 小写字面量在 Python 中不是关键字,需替换为大写形式。
|
|
69
|
+
"""
|
|
70
|
+
out = expr
|
|
71
|
+
for java_op, py_op in (("&&", " and "), ("||", " or ")):
|
|
72
|
+
out = out.replace(java_op, py_op)
|
|
73
|
+
# !x -> not x;需要避开 != 与 not in 场景(此处仅处理 ! 后跟空白/标识符/括号)
|
|
74
|
+
out = re.sub(r"!(?=\s*\w|\s*\()", "not ", out)
|
|
75
|
+
# 小写布尔/空字面量(词边界替换,避免误伤变量名如 "nullable")
|
|
76
|
+
out = re.sub(r"\btrue\b", "True", out)
|
|
77
|
+
out = re.sub(r"\bfalse\b", "False", out)
|
|
78
|
+
out = re.sub(r"\bnull\b", "None", out)
|
|
79
|
+
return out
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def evaluate_expression(expr_text: str, variables: Dict[str, Any]) -> Any:
|
|
83
|
+
"""求值表达式文本(可含 ${...} 包裹),返回 Python 值。"""
|
|
84
|
+
expr = expr_text.strip()
|
|
85
|
+
if expr.startswith("${") and expr.endswith("}"):
|
|
86
|
+
expr = expr[2:-1].strip()
|
|
87
|
+
if not expr:
|
|
88
|
+
return True # 空表达式视为无条件真(对齐 JUEL 对空条件行为)
|
|
89
|
+
|
|
90
|
+
source = _normalize(expr)
|
|
91
|
+
try:
|
|
92
|
+
tree = ast.parse(source, mode="eval")
|
|
93
|
+
except SyntaxError as e:
|
|
94
|
+
raise ProcessInstanceException(
|
|
95
|
+
f"表达式语法错误 {expr_text!r}: {e}"
|
|
96
|
+
) from e
|
|
97
|
+
|
|
98
|
+
for node in ast.walk(tree):
|
|
99
|
+
if not isinstance(node, _ALLOWED_NODES):
|
|
100
|
+
raise ProcessInstanceException(
|
|
101
|
+
f"表达式含不支持语法 {expr_text!r}: {type(node).__name__}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# 变量解析:Name 必须是流程变量
|
|
105
|
+
def _resolve(name: str) -> Any:
|
|
106
|
+
# 内建布尔字面量在 3.8+ 是 Constant;Name 仅在变量表里查
|
|
107
|
+
if name in variables:
|
|
108
|
+
return variables[name]
|
|
109
|
+
raise ProcessInstanceException(f"流程变量未定义: {name!r}(表达式 {expr_text!r})")
|
|
110
|
+
|
|
111
|
+
env = {name: _resolve(name) for name in set(
|
|
112
|
+
n.id for n in ast.walk(tree) if isinstance(n, ast.Name)
|
|
113
|
+
)}
|
|
114
|
+
try:
|
|
115
|
+
return eval(compile(tree, "<bpmn-expr>", "eval"), {"__builtins__": {}}, env)
|
|
116
|
+
except ProcessInstanceException:
|
|
117
|
+
raise
|
|
118
|
+
except Exception as e: # 运行时类型错误(如 str > int)
|
|
119
|
+
raise ProcessInstanceException(
|
|
120
|
+
f"表达式求值失败 {expr_text!r}: {e}"
|
|
121
|
+
) from e
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def evaluate_condition(expr_text: str, variables: Dict[str, Any]) -> bool:
|
|
125
|
+
"""条件求值 -> bool(用于排他网关/条件流)。"""
|
|
126
|
+
return bool(evaluate_expression(expr_text, variables))
|