|
22 | 22 | import contextlib |
23 | 23 | import copy |
24 | 24 | import dataclasses |
| 25 | +import functools |
25 | 26 | import importlib |
26 | 27 | import importlib.util |
27 | 28 | import json |
@@ -2934,7 +2935,7 @@ def x(arg1, arg2, arg3): |
2934 | 2935 | }, |
2935 | 2936 | "_disallow_kwargs_override": False, |
2936 | 2937 | "_expand_input_attr": "op_kwargs_expand_input", |
2937 | | - "python_callable_name": qualname(x), |
| 2938 | + "python_callable_name": "test_taskflow_expand_serde.<locals>.x", |
2938 | 2939 | } |
2939 | 2940 |
|
2940 | 2941 | deserialized = BaseSerialization.deserialize(serialized) |
@@ -3001,7 +3002,7 @@ def x(arg1, arg2, arg3): |
3001 | 3002 | "_task_module": "airflow.providers.standard.decorators.python", |
3002 | 3003 | "task_type": "_PythonDecoratedOperator", |
3003 | 3004 | "_operator_name": "@task", |
3004 | | - "python_callable_name": qualname(x), |
| 3005 | + "python_callable_name": "test_taskflow_expand_kwargs_serde.<locals>.x", |
3005 | 3006 | "partial_kwargs": { |
3006 | 3007 | "op_args": [], |
3007 | 3008 | "op_kwargs": { |
@@ -3172,11 +3173,42 @@ def test_python_callable_in_partial_kwargs(): |
3172 | 3173 |
|
3173 | 3174 | serialized = OperatorSerialization.serialize_mapped_operator(operator) |
3174 | 3175 | assert "python_callable" not in serialized["partial_kwargs"] |
3175 | | - assert serialized["partial_kwargs"]["python_callable_name"] == qualname(empty_function) |
| 3176 | + assert serialized["partial_kwargs"]["python_callable_name"] == "empty_function" |
3176 | 3177 |
|
3177 | 3178 | deserialized = OperatorSerialization.deserialize_operator(serialized) |
3178 | 3179 | assert "python_callable" not in deserialized.partial_kwargs |
3179 | | - assert deserialized.partial_kwargs["python_callable_name"] == qualname(empty_function) |
| 3180 | + assert deserialized.partial_kwargs["python_callable_name"] == "empty_function" |
| 3181 | + |
| 3182 | + |
| 3183 | +def test_python_callable_name_uses_qualname_exclude_module(): |
| 3184 | + """Test python_callable_name is stable across bundle version changes.""" |
| 3185 | + from airflow.providers.standard.operators.python import PythonOperator |
| 3186 | + |
| 3187 | + # Module-level function |
| 3188 | + op1 = PythonOperator(task_id="task1", python_callable=empty_function) |
| 3189 | + serialized1 = OperatorSerialization.serialize_operator(op1) |
| 3190 | + assert serialized1["python_callable_name"] == "empty_function" |
| 3191 | + |
| 3192 | + # Nested function |
| 3193 | + def outer(): |
| 3194 | + def inner(): |
| 3195 | + pass |
| 3196 | + |
| 3197 | + return inner |
| 3198 | + |
| 3199 | + inner_func = outer() |
| 3200 | + op2 = PythonOperator(task_id="task2", python_callable=inner_func) |
| 3201 | + serialized2 = OperatorSerialization.serialize_operator(op2) |
| 3202 | + assert ( |
| 3203 | + serialized2["python_callable_name"] |
| 3204 | + == "test_python_callable_name_uses_qualname_exclude_module.<locals>.outer.<locals>.inner" |
| 3205 | + ) |
| 3206 | + |
| 3207 | + # functools.partial |
| 3208 | + partial_func = functools.partial(empty_function, x=1) |
| 3209 | + op3 = PythonOperator(task_id="task3", python_callable=partial_func) |
| 3210 | + serialized3 = OperatorSerialization.serialize_operator(op3) |
| 3211 | + assert serialized3["python_callable_name"] == "empty_function" |
3180 | 3212 |
|
3181 | 3213 |
|
3182 | 3214 | def test_handle_v1_serdag(): |
|
0 commit comments