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,121 @@
|
|
|
1
|
+
"""运行时执行模型:Execution 树 / ProcessInstance / ActivityInstance。
|
|
2
|
+
|
|
3
|
+
对齐 Camunda ACT_RU_EXECUTION 语义:
|
|
4
|
+
- ProcessInstance 是树的根(process_instance_id == 根 execution id 场景下,Camunda
|
|
5
|
+
的 root execution 与 process instance 是两条记录,这里 M1 合并为一条根 Execution)
|
|
6
|
+
- 并行网关 fork 时创建子 Execution(parent 停驻成为 scope)
|
|
7
|
+
- 每条「活动中的路径」由一条 Execution 携带:activity_id 指向当前所在节点
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING: # 仅类型检查,避免与 task.py 循环(task 不依赖 execution)
|
|
17
|
+
from camunda.model.task import Task
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ExecutionState(str, Enum):
|
|
21
|
+
ACTIVE = "ACTIVE" # 正等待:用户任务 / join / scope 停驻
|
|
22
|
+
ENDED = "ENDED" # 已走完(endEvent 或实例结束)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ProcessInstanceState(str, Enum):
|
|
26
|
+
ACTIVE = "ACTIVE"
|
|
27
|
+
COMPLETED = "COMPLETED" # 正常走完 endEvent
|
|
28
|
+
TERMINATED = "TERMINATED" # 保留字段(M3+ 支持终止)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Execution:
|
|
33
|
+
"""一条执行路径(token 载体)。id 全局唯一。
|
|
34
|
+
|
|
35
|
+
role:
|
|
36
|
+
- TOKEN : 活动 token,沿流程推进,可停在 userTask / join 等待
|
|
37
|
+
- SCOPE : 并行 fork 后停驻的父 execution(等待子 TOKEN 完成后再恢复)
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
id: str
|
|
41
|
+
process_instance_id: str
|
|
42
|
+
parent_id: Optional[str] = None
|
|
43
|
+
role: str = "TOKEN" # TOKEN | SCOPE
|
|
44
|
+
# None 表示该 execution 当前不在具体活动上(如 fork 后等待、根 scope)
|
|
45
|
+
activity_id: Optional[str] = None
|
|
46
|
+
state: ExecutionState = ExecutionState.ACTIVE
|
|
47
|
+
# 子 execution(并行网关 fork 产生)
|
|
48
|
+
children: List["Execution"] = field(default_factory=list)
|
|
49
|
+
# 当前停留活动实例(_open_activity 写入,_close_activity 结算时间后清空)
|
|
50
|
+
open_activity: Optional["ActivityInstance"] = None
|
|
51
|
+
# 多实例状态(M4-2c):None = 非多实例执行。
|
|
52
|
+
# MI 容器(有 "total" 键):{sequential, total, active, completed, next_index,
|
|
53
|
+
# items, element_variable, completion_condition} —— parallel 容器挂在转 SCOPE
|
|
54
|
+
# 的宿主 token 上;sequential 容器挂在宿主 token 自身(token 兼作实例载体)。
|
|
55
|
+
# MI 实例(仅并行 child,{"index": i}):标识该实例序号(收束回报计数用)。
|
|
56
|
+
mi: Optional[Dict[str, Any]] = None
|
|
57
|
+
|
|
58
|
+
# ---- 便捷 ----
|
|
59
|
+
@property
|
|
60
|
+
def is_root(self) -> bool:
|
|
61
|
+
return self.parent_id is None
|
|
62
|
+
|
|
63
|
+
def is_ended(self) -> bool:
|
|
64
|
+
return self.state == ExecutionState.ENDED
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def is_mi_container(self) -> bool:
|
|
68
|
+
"""是否为多实例容器载体(挂 total 键的 MI 状态)。"""
|
|
69
|
+
return self.mi is not None and "total" in self.mi
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class ActivityInstance:
|
|
74
|
+
"""活动实例历史痕迹(M1 内存版,M2 落 ACT_HI_ACTINST)。
|
|
75
|
+
|
|
76
|
+
每次「进入某节点」记录一条,start_time 进入、end_time 离开。
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
id: str
|
|
80
|
+
process_instance_id: str
|
|
81
|
+
activity_id: str
|
|
82
|
+
activity_name: Optional[str] = None
|
|
83
|
+
execution_id: str = ""
|
|
84
|
+
start_time: Optional[str] = None
|
|
85
|
+
end_time: Optional[str] = None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass
|
|
89
|
+
class ProcessInstance:
|
|
90
|
+
"""流程实例(对齐 ACT_HI_PROCINST 运行时视图)。"""
|
|
91
|
+
|
|
92
|
+
id: str
|
|
93
|
+
process_definition_key: str
|
|
94
|
+
business_key: Optional[str] = None
|
|
95
|
+
state: ProcessInstanceState = ProcessInstanceState.ACTIVE
|
|
96
|
+
variables: Dict[str, Any] = field(default_factory=dict)
|
|
97
|
+
root_execution: Optional[Execution] = None
|
|
98
|
+
start_time: Optional[str] = None
|
|
99
|
+
end_time: Optional[str] = None
|
|
100
|
+
# execution id 索引(与树冗余,便于 O(1) 查找)
|
|
101
|
+
executions: Dict[str, Execution] = field(default_factory=dict)
|
|
102
|
+
# 并行网关 join 到达登记:gateway_id -> 已到达的 execution id 列表
|
|
103
|
+
join_arrivals: Dict[str, List[str]] = field(default_factory=dict)
|
|
104
|
+
# 活动痕迹(ACT_HI_ACTINST 内存版)
|
|
105
|
+
activity_history: List[ActivityInstance] = field(default_factory=list)
|
|
106
|
+
# 已完成任务归档(complete 后从 engine 任务表移入,HI_TASKINST 落库)
|
|
107
|
+
completed_tasks: List["Task"] = field(default_factory=list)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def is_completed(self) -> bool:
|
|
111
|
+
return self.state != ProcessInstanceState.ACTIVE
|
|
112
|
+
|
|
113
|
+
# ---- 并行网关 join 辅助 ----
|
|
114
|
+
def register_join_arrival(self, join_activity_id: str, execution_id: str) -> None:
|
|
115
|
+
self.join_arrivals.setdefault(join_activity_id, []).append(execution_id)
|
|
116
|
+
|
|
117
|
+
def join_arrived(self, join_activity_id: str) -> List[str]:
|
|
118
|
+
return self.join_arrivals.get(join_activity_id, [])
|
|
119
|
+
|
|
120
|
+
def clear_join_arrivals(self, join_activity_id: str) -> None:
|
|
121
|
+
self.join_arrivals.pop(join_activity_id, None)
|
camunda/model/job.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""可调度作业模型(对齐 ACT_RU_JOB 语义,M3)。
|
|
2
|
+
|
|
3
|
+
Camunda JobEntity 关键字段:ID_ / TYPE_ / DUEDATE_ / RETRIES_ / LOCK_OWNER_ /
|
|
4
|
+
LOCK_EXP_TIME_ / EXECUTION_ID_ / PROCESS_INSTANCE_ID_ / PROCESS_DEFINITION_ID_ /
|
|
5
|
+
ACTIVITY_ID_。M3 保留核心子集。
|
|
6
|
+
|
|
7
|
+
job_type(对齐 Camunda Job.TYPE_ 取值语义):
|
|
8
|
+
- "timer-catch" : token 停在 intermediateCatchEvent(timer),duedate 到期继续流转
|
|
9
|
+
- "timer-start" : 定义级作业(无 process_instance),到点启动流程实例;
|
|
10
|
+
timerCycle 触发后按 repeat 续排下一个 duedate
|
|
11
|
+
- "timer-boundary" : timer 边界事件(M4-1 中断式;M4-2b4 起 cancelActivity=false
|
|
12
|
+
非中断式 = 触发不取消宿主、spawn 并发线)。宿主活动等待期内
|
|
13
|
+
到点触发,中断式取消宿主并让 token 改走边界事件出边
|
|
14
|
+
- "timer-event-start" : 事件子流程的 timer start 订阅(M4-2b3,实例级,execution_id
|
|
15
|
+
= 宿主 scope,activity_id = 订阅容器 subProcess id,None=
|
|
16
|
+
流程级=根 Process 容器)。宿主 scope 激活期单发,到点触发
|
|
17
|
+
中断式(取消宿主)或非中断式(并行 spawn)事件子流程
|
|
18
|
+
- "async-continuation" : camunda:asyncBefore 拆分出的「节点行为执行」作业
|
|
19
|
+
(Camunda 里即 async continuation job)
|
|
20
|
+
- "async-after" : camunda:asyncAfter 拆分出的「离开推进」作业(M4-1;
|
|
21
|
+
serviceTask/XOR 行为完成后异步流转,XOR 离开时重求值条件)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from typing import Any, Dict, Optional
|
|
28
|
+
|
|
29
|
+
# 默认重试次数与失败延迟(对齐 Camunda 默认行为;M3 不解析 failedJobRetryTimeCycle,
|
|
30
|
+
# retry 间隔固定可配。文档化差异)
|
|
31
|
+
DEFAULT_MAX_RETRIES = 3
|
|
32
|
+
DEFAULT_RETRY_DELAY_SECONDS = 5.0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class Job:
|
|
37
|
+
"""一条待执行作业。"""
|
|
38
|
+
|
|
39
|
+
id: str
|
|
40
|
+
job_type: str # timer-catch | timer-start | async-continuation
|
|
41
|
+
duedate: str # 定长 ISO "%Y-%m-%dT%H:%M:%S"(与 clock.now() 同格式,可字典序比较)
|
|
42
|
+
created: str
|
|
43
|
+
# 实例级(timer-catch / async-continuation)
|
|
44
|
+
process_instance_id: Optional[str] = None
|
|
45
|
+
execution_id: Optional[str] = None
|
|
46
|
+
# 定义级(timer-start):指向 process key;node_id 为 startEvent id
|
|
47
|
+
process_definition_key: Optional[str] = None
|
|
48
|
+
node_id: Optional[str] = None
|
|
49
|
+
# timer-event-start:订阅容器 subProcess id(None = 流程级/根 Process 容器);
|
|
50
|
+
# 与 execution_id(宿主 scope)共同唯一定位「哪个容器上的订阅」,撤销精确匹配
|
|
51
|
+
activity_id: Optional[str] = None
|
|
52
|
+
# 重试策略
|
|
53
|
+
retries: int = DEFAULT_MAX_RETRIES
|
|
54
|
+
max_retries: int = DEFAULT_MAX_RETRIES
|
|
55
|
+
retry_delay_seconds: float = DEFAULT_RETRY_DELAY_SECONDS
|
|
56
|
+
# timer-start cycle 续排参数({"kind": "interval"|"cron", ...},见 common/timers)
|
|
57
|
+
repeat: Optional[Dict[str, Any]] = None
|
|
58
|
+
# 抢占锁(多实例 JobExecutor 预留;M3 单进程不使用)
|
|
59
|
+
lock_owner: Optional[str] = None
|
|
60
|
+
lock_expire_time: Optional[str] = None
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def is_definition_level(self) -> bool:
|
|
64
|
+
"""定义级作业(timer-start)不挂在任何实例上。"""
|
|
65
|
+
return self.process_instance_id is None
|
|
66
|
+
|
|
67
|
+
def is_due(self, now: str) -> bool:
|
|
68
|
+
return self.duedate <= now
|
|
69
|
+
|
|
70
|
+
def is_dead(self) -> bool:
|
|
71
|
+
return self.retries <= 0
|
|
72
|
+
|
|
73
|
+
def to_dict(self) -> dict:
|
|
74
|
+
return {
|
|
75
|
+
"id": self.id,
|
|
76
|
+
"job_type": self.job_type,
|
|
77
|
+
"process_instance_id": self.process_instance_id,
|
|
78
|
+
"execution_id": self.execution_id,
|
|
79
|
+
"process_definition_key": self.process_definition_key,
|
|
80
|
+
"node_id": self.node_id,
|
|
81
|
+
"activity_id": self.activity_id,
|
|
82
|
+
"duedate": self.duedate,
|
|
83
|
+
"created": self.created,
|
|
84
|
+
"retries": self.retries,
|
|
85
|
+
"repeat": self.repeat,
|
|
86
|
+
"lock_owner": self.lock_owner,
|
|
87
|
+
"lock_expire_time": self.lock_expire_time,
|
|
88
|
+
}
|
camunda/model/task.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""人工任务模型(对齐 ACT_RU_TASK 语义)。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Task:
|
|
11
|
+
"""用户任务运行时实例:等待用户 complete。"""
|
|
12
|
+
|
|
13
|
+
id: str
|
|
14
|
+
name: Optional[str] = None
|
|
15
|
+
process_instance_id: str = ""
|
|
16
|
+
execution_id: str = "" # 持有该任务的 execution
|
|
17
|
+
task_definition_key: str = "" # BPMN userTask id
|
|
18
|
+
assignee: Optional[str] = None
|
|
19
|
+
candidate_users: List[str] = field(default_factory=list)
|
|
20
|
+
candidate_groups: List[str] = field(default_factory=list)
|
|
21
|
+
create_time: Optional[str] = None
|
|
22
|
+
end_time: Optional[str] = None # complete 后写入(HI_TASKINST 归档)
|
|
23
|
+
|
|
24
|
+
def to_dict(self) -> dict:
|
|
25
|
+
return {
|
|
26
|
+
"id": self.id,
|
|
27
|
+
"name": self.name,
|
|
28
|
+
"process_instance_id": self.process_instance_id,
|
|
29
|
+
"task_definition_key": self.task_definition_key,
|
|
30
|
+
"assignee": self.assignee,
|
|
31
|
+
"create_time": self.create_time,
|
|
32
|
+
"end_time": self.end_time,
|
|
33
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""流程变量体系(M1 简化版)。
|
|
2
|
+
|
|
3
|
+
对齐 Camunda 变量语义的 Python 映射:
|
|
4
|
+
- Camunda 变量的 Java 类型名 -> Python 类型判断,序列化策略 JSON
|
|
5
|
+
- ObjectValue(Java 序列化对象)在 Python 侧无等价物:M1 用 JSON 可序列化对象表示,
|
|
6
|
+
文档化差异(M2 持久化时落 TEXT/JSON 列)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Dict
|
|
12
|
+
|
|
13
|
+
# Java 类型 -> 判定函数(按需扩展)
|
|
14
|
+
_JAVA_TYPE_OF = [
|
|
15
|
+
("String", lambda v: isinstance(v, str)),
|
|
16
|
+
("Integer", lambda v: isinstance(v, int) and not isinstance(v, bool)),
|
|
17
|
+
("Boolean", lambda v: isinstance(v, bool)),
|
|
18
|
+
("Double", lambda v: isinstance(v, float)),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def java_type_name(value: Any) -> str:
|
|
23
|
+
"""推断变量值的 Java 类型名(Camunda REST API 的 type 字段)。"""
|
|
24
|
+
for name, check in _JAVA_TYPE_OF:
|
|
25
|
+
if check(value):
|
|
26
|
+
return name
|
|
27
|
+
return "Object" # dict/list/None 等统一按对象处理
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def to_typed_dict(variables: Dict[str, Any]) -> Dict[str, dict]:
|
|
31
|
+
"""把 {key: value} 转成 Camunda REST 风格 {key: {type, value}}(供 API 层/调试用)。"""
|
|
32
|
+
return {
|
|
33
|
+
k: {"type": java_type_name(v), "value": v}
|
|
34
|
+
for k, v in variables.items()
|
|
35
|
+
}
|