Skip to content

Commit 5a632f7

Browse files
IKholopovIgor Kholopov
andauthored
Google Cloud Providers - Fix _MethodDefault deepcopy failure (#29518)
This is the attempt to fix #28751 by setting a memo to the same instance of DEFAULT, which is the stub for a Literal value type introduced to support Python 3.7 with mypy in Google's API core Python client. Without this any operator that has the parameter set to DEFAULT constant (which is often the default value for retry parameters) will throw the error in mini-scheduler after execution as the attemt to deepcopy this project would fail. Co-authored-by: Igor Kholopov <kholopovus@gmail.com>
1 parent ec31648 commit 5a632f7

2 files changed

Lines changed: 66 additions & 1 deletion

File tree

airflow/providers/google/cloud/operators/cloud_base.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"""This module contains a Google API base operator."""
1919
from __future__ import annotations
2020

21+
from google.api_core.gapic_v1.method import DEFAULT
22+
2123
from airflow.models import BaseOperator
2224

2325

@@ -27,4 +29,11 @@ class GoogleCloudBaseOperator(BaseOperator):
2729
on top of Google API client libraries.
2830
"""
2931

30-
pass
32+
def __deepcopy__(self, memo):
33+
"""
34+
Updating the memo to fix the non-copyable global constant.
35+
This constant can be specified in operator parameters as a retry configuration to indicate a default.
36+
See https://github.com/apache/airflow/issues/28751 for details.
37+
"""
38+
memo[id(DEFAULT)] = DEFAULT
39+
return super().__deepcopy__(memo)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
from __future__ import annotations
19+
20+
import copy
21+
import unittest
22+
23+
from google.api_core.gapic_v1.method import DEFAULT, _MethodDefault
24+
from google.api_core.retry import Retry
25+
26+
from airflow.providers.google.cloud.operators.cloud_base import GoogleCloudBaseOperator
27+
28+
TASK_ID = "task-id"
29+
30+
31+
class GoogleSampleOperator(GoogleCloudBaseOperator):
32+
def __init__(
33+
self,
34+
retry: Retry | _MethodDefault = DEFAULT,
35+
config: dict | None = None,
36+
**kwargs,
37+
) -> None:
38+
super().__init__(**kwargs)
39+
self.retry = retry
40+
self.config = config
41+
42+
43+
class TestGoogleCloudBaseOperator(unittest.TestCase):
44+
def test_handles_deepcopy_with_method_default(self):
45+
op = GoogleSampleOperator(task_id=TASK_ID)
46+
copied_op = copy.deepcopy(op)
47+
48+
self.assertEqual(copied_op.retry, DEFAULT)
49+
self.assertEqual(copied_op.config, None)
50+
51+
def test_handles_deepcopy_with_non_default_retry(self):
52+
op = GoogleSampleOperator(task_id=TASK_ID, retry=Retry(deadline=30), config={"config": "value"})
53+
copied_op = copy.deepcopy(op)
54+
55+
self.assertEqual(copied_op.retry.deadline, 30)
56+
self.assertEqual(copied_op.config, {"config": "value"})

0 commit comments

Comments
 (0)