1818from __future__ import annotations
1919
2020import datetime
21+ import enum
2122import functools
2223import hashlib
2324import time
3233from airflow .configuration import conf
3334from airflow .exceptions import (
3435 AirflowException ,
35- AirflowFailException ,
36+ AirflowPokeFailException ,
3637 AirflowRescheduleException ,
3738 AirflowSensorTimeout ,
3839 AirflowSkipException ,
@@ -109,15 +110,31 @@ def _orig_start_date(
109110 )
110111
111112
113+ class FailPolicy (str , enum .Enum ):
114+ """Class with sensor's fail policies."""
115+
116+ # if poke method raise an exception, sensor will not be skipped on.
117+ NONE = "none"
118+
119+ # If poke method raises an exception, sensor will be skipped on.
120+ SKIP_ON_ANY_ERROR = "skip_on_any_error"
121+
122+ # If poke method raises AirflowSensorTimeout, AirflowTaskTimeout,AirflowPokeFailException or AirflowSkipException
123+ # sensor will be skipped on.
124+ SKIP_ON_TIMEOUT = "skip_on_timeout"
125+
126+ # If poke method raises an exception different from AirflowSensorTimeout, AirflowTaskTimeout,
127+ # AirflowSkipException or AirflowFailException sensor will ignore exception and re-poke until timeout.
128+ IGNORE_ERROR = "ignore_error"
129+
130+
112131class BaseSensorOperator (BaseOperator , SkipMixin ):
113132 """
114133 Sensor operators are derived from this class and inherit these attributes.
115134
116135 Sensor operators keep executing at a time interval and succeed when
117136 a criteria is met and fail if and when they time out.
118137
119- :param soft_fail: Set to true to mark the task as SKIPPED on failure.
120- Mutually exclusive with never_fail.
121138 :param poke_interval: Time that the job should wait in between each try.
122139 Can be ``timedelta`` or ``float`` seconds.
123140 :param timeout: Time elapsed before the task times out and fails.
@@ -145,13 +162,10 @@ class BaseSensorOperator(BaseOperator, SkipMixin):
145162 :param exponential_backoff: allow progressive longer waits between
146163 pokes by using exponential backoff algorithm
147164 :param max_wait: maximum wait interval between pokes, can be ``timedelta`` or ``float`` seconds
148- :param silent_fail: If true, and poke method raises an exception different from
149- AirflowSensorTimeout, AirflowTaskTimeout, AirflowSkipException
150- and AirflowFailException, the sensor will log the error and continue
151- its execution. Otherwise, the sensor task fails, and it can be retried
152- based on the provided `retries` parameter.
153- :param never_fail: If true, and poke method raises an exception, sensor will be skipped.
154- Mutually exclusive with soft_fail.
165+ :param fail_policy: defines the rule by which sensor skip itself. Options are:
166+ ``{ none | skip_on_any_error | skip_on_timeout | ignore_error }``
167+ default is ``none``. Options can be set as string or
168+ using the constants defined in the static class ``airflow.sensors.base.FailPolicy``
155169 """
156170
157171 ui_color : str = "#e6f1f2"
@@ -166,26 +180,19 @@ def __init__(
166180 * ,
167181 poke_interval : timedelta | float = 60 ,
168182 timeout : timedelta | float = conf .getfloat ("sensors" , "default_timeout" ),
169- soft_fail : bool = False ,
170183 mode : str = "poke" ,
171184 exponential_backoff : bool = False ,
172185 max_wait : timedelta | float | None = None ,
173- silent_fail : bool = False ,
174- never_fail : bool = False ,
186+ fail_policy : str = FailPolicy .NONE ,
175187 ** kwargs ,
176188 ) -> None :
177189 super ().__init__ (** kwargs )
178190 self .poke_interval = self ._coerce_poke_interval (poke_interval ).total_seconds ()
179- self .soft_fail = soft_fail
180191 self .timeout = self ._coerce_timeout (timeout ).total_seconds ()
181192 self .mode = mode
182193 self .exponential_backoff = exponential_backoff
183194 self .max_wait = self ._coerce_max_wait (max_wait )
184- if soft_fail is True and never_fail is True :
185- raise ValueError ("soft_fail and never_fail are mutually exclusive, you can not provide both." )
186-
187- self .silent_fail = silent_fail
188- self .never_fail = never_fail
195+ self .fail_policy = fail_policy
189196 self ._validate_input_values ()
190197
191198 @staticmethod
@@ -282,21 +289,20 @@ def run_duration() -> float:
282289 except (
283290 AirflowSensorTimeout ,
284291 AirflowTaskTimeout ,
285- AirflowFailException ,
292+ AirflowPokeFailException ,
293+ AirflowSkipException ,
286294 ) as e :
287- if self .soft_fail :
288- raise AirflowSkipException ("Skipping due to soft_fail is set to True." ) from e
289- elif self .never_fail :
290- raise AirflowSkipException ("Skipping due to never_fail is set to True." ) from e
291- raise e
292- except AirflowSkipException as e :
295+ if self .fail_policy == FailPolicy .SKIP_ON_TIMEOUT :
296+ raise AirflowSkipException ("Skipping due fail_policy set to SKIP_ON_TIMEOUT." ) from e
297+ elif self .fail_policy == FailPolicy .SKIP_ON_ANY_ERROR :
298+ raise AirflowSkipException ("Skipping due to SKIP_ON_ANY_ERROR is set to True." ) from e
293299 raise e
294300 except Exception as e :
295- if self .silent_fail :
301+ if self .fail_policy == FailPolicy . IGNORE_ERROR :
296302 self .log .error ("Sensor poke failed: \n %s" , traceback .format_exc ())
297303 poke_return = False
298- elif self .never_fail :
299- raise AirflowSkipException ("Skipping due to never_fail is set to True." ) from e
304+ elif self .fail_policy == FailPolicy . SKIP_ON_ANY_ERROR :
305+ raise AirflowSkipException ("Skipping due to SKIP_ON_ANY_ERROR is set to True." ) from e
300306 else :
301307 raise e
302308
@@ -306,13 +312,13 @@ def run_duration() -> float:
306312 break
307313
308314 if run_duration () > self .timeout :
309- # If sensor is in soft fail mode but times out raise AirflowSkipException.
315+ # If sensor is in SKIP_ON_TIMEOUT mode but times out it raise AirflowSkipException.
310316 message = (
311317 f"Sensor has timed out; run duration of { run_duration ()} seconds exceeds "
312318 f"the specified timeout of { self .timeout } ."
313319 )
314320
315- if self .soft_fail :
321+ if self .fail_policy == FailPolicy . SKIP_ON_TIMEOUT :
316322 raise AirflowSkipException (message )
317323 else :
318324 raise AirflowSensorTimeout (message )
@@ -335,7 +341,7 @@ def resume_execution(self, next_method: str, next_kwargs: dict[str, Any] | None,
335341 try :
336342 return super ().resume_execution (next_method , next_kwargs , context )
337343 except (AirflowException , TaskDeferralError ) as e :
338- if self .soft_fail :
344+ if self .fail_policy == FailPolicy . SKIP_ON_ANY_ERROR :
339345 raise AirflowSkipException (str (e )) from e
340346 raise
341347
0 commit comments