This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new e35ae2db7c Airflow 3: airflow date utils date_range and days_ago
deprecations removal (#41496)
e35ae2db7c is described below
commit e35ae2db7c56a23b2f7abbd7c4269221ed98ead6
Author: Gopal Dirisala <[email protected]>
AuthorDate: Fri Aug 16 19:21:42 2024 +0530
Airflow 3: airflow date utils date_range and days_ago deprecations removal
(#41496)
* date utils deprecations removal
* news fragment added
* date utils deprecations removal
* Update 41496.significant.rst
Removed the recommendation for the removed method
---
airflow/utils/dates.py | 121 +--------------------
newsfragments/41496.significant.rst | 5 +
.../databricks/plugins/test_databricks_workflow.py | 2 -
tests/utils/test_dates.py | 63 +----------
4 files changed, 9 insertions(+), 182 deletions(-)
diff --git a/airflow/utils/dates.py b/airflow/utils/dates.py
index f3eeddb916..7a9ac2d803 100644
--- a/airflow/utils/dates.py
+++ b/airflow/utils/dates.py
@@ -17,14 +17,11 @@
# under the License.
from __future__ import annotations
-import warnings
from datetime import datetime, timedelta
-from typing import Collection
+from typing import TYPE_CHECKING, Collection
from croniter import croniter
-from dateutil.relativedelta import relativedelta # for doctest
-from airflow.exceptions import RemovedInAirflow3Warning
from airflow.typing_compat import Literal
from airflow.utils import timezone
@@ -37,103 +34,8 @@ cron_presets: dict[str, str] = {
"@yearly": "0 0 1 1 *",
}
-
-def date_range(
- start_date: datetime,
- end_date: datetime | None = None,
- num: int | None = None,
- delta: str | timedelta | relativedelta | None = None,
-) -> list[datetime]:
- """
- Get a list of dates in the specified range, separated by delta.
-
- .. code-block:: pycon
- >>> from airflow.utils.dates import date_range
- >>> from datetime import datetime, timedelta
- >>> date_range(datetime(2016, 1, 1), datetime(2016, 1, 3),
delta=timedelta(1))
- [datetime.datetime(2016, 1, 1, 0, 0, tzinfo=Timezone('UTC')),
- datetime.datetime(2016, 1, 2, 0, 0, tzinfo=Timezone('UTC')),
- datetime.datetime(2016, 1, 3, 0, 0, tzinfo=Timezone('UTC'))]
- >>> date_range(datetime(2016, 1, 1), datetime(2016, 1, 3), delta="0 0
* * *")
- [datetime.datetime(2016, 1, 1, 0, 0, tzinfo=Timezone('UTC')),
- datetime.datetime(2016, 1, 2, 0, 0, tzinfo=Timezone('UTC')),
- datetime.datetime(2016, 1, 3, 0, 0, tzinfo=Timezone('UTC'))]
- >>> date_range(datetime(2016, 1, 1), datetime(2016, 3, 3), delta="0 0
0 * *")
- [datetime.datetime(2016, 1, 1, 0, 0, tzinfo=Timezone('UTC')),
- datetime.datetime(2016, 2, 1, 0, 0, tzinfo=Timezone('UTC')),
- datetime.datetime(2016, 3, 1, 0, 0, tzinfo=Timezone('UTC'))]
-
- :param start_date: anchor date to start the series from
- :param end_date: right boundary for the date range
- :param num: alternatively to end_date, you can specify the number of
- number of entries you want in the range. This number can be negative,
- output will always be sorted regardless
- :param delta: step length. It can be datetime.timedelta or cron expression
as string
- """
- warnings.warn(
- "`airflow.utils.dates.date_range()` is deprecated. Please use
`airflow.timetables`.",
- category=RemovedInAirflow3Warning,
- stacklevel=2,
- )
-
- if not delta:
- return []
- if end_date:
- if start_date > end_date:
- raise ValueError("Wait. start_date needs to be before end_date")
- if num:
- raise ValueError("Wait. Either specify end_date OR num")
- if not end_date and not num:
- end_date = timezone.utcnow()
-
- delta_iscron = False
- time_zone = start_date.tzinfo
-
- abs_delta: timedelta | relativedelta
- if isinstance(delta, str):
- delta_iscron = True
- if timezone.is_localized(start_date):
- start_date = timezone.make_naive(start_date, time_zone)
- cron = croniter(cron_presets.get(delta, delta), start_date)
- elif isinstance(delta, timedelta):
- abs_delta = abs(delta)
- elif isinstance(delta, relativedelta):
- abs_delta = abs(delta)
- else:
- raise TypeError("Wait. delta must be either datetime.timedelta or cron
expression as str")
-
- dates = []
- if end_date:
- if timezone.is_naive(start_date) and not timezone.is_naive(end_date):
- end_date = timezone.make_naive(end_date, time_zone)
- while start_date <= end_date: # type: ignore
- if timezone.is_naive(start_date):
- dates.append(timezone.make_aware(start_date, time_zone))
- else:
- dates.append(start_date)
-
- if delta_iscron:
- start_date = cron.get_next(datetime)
- else:
- start_date += abs_delta
- else:
- num_entries: int = num # type: ignore
- for _ in range(abs(num_entries)):
- if timezone.is_naive(start_date):
- dates.append(timezone.make_aware(start_date, time_zone))
- else:
- dates.append(start_date)
-
- if delta_iscron and num_entries > 0:
- start_date = cron.get_next(datetime)
- elif delta_iscron:
- start_date = cron.get_prev(datetime)
- elif num_entries > 0:
- start_date += abs_delta
- else:
- start_date -= abs_delta
-
- return sorted(dates)
+if TYPE_CHECKING:
+ from dateutil.relativedelta import relativedelta # for doctest
def round_time(
@@ -256,23 +158,6 @@ def scale_time_units(time_seconds_arr: Collection[float],
unit: TimeUnit) -> Col
return [x / factor for x in time_seconds_arr]
-def days_ago(n, hour=0, minute=0, second=0, microsecond=0):
- """
- Get a datetime object representing *n* days ago.
-
- By default the time is set to midnight.
- """
- warnings.warn(
- "Function `days_ago` is deprecated and will be removed in Airflow 3.0.
"
- "You can achieve equivalent behavior with
`pendulum.today('UTC').add(days=-N, ...)`",
- RemovedInAirflow3Warning,
- stacklevel=2,
- )
-
- today = timezone.utcnow().replace(hour=hour, minute=minute, second=second,
microsecond=microsecond)
- return today - timedelta(days=n)
-
-
def parse_execution_date(execution_date_str):
"""Parse execution date string to datetime object."""
return timezone.parse(execution_date_str)
diff --git a/newsfragments/41496.significant.rst
b/newsfragments/41496.significant.rst
new file mode 100644
index 0000000000..aecb35e723
--- /dev/null
+++ b/newsfragments/41496.significant.rst
@@ -0,0 +1,5 @@
+Removed deprecated methods in airflow/utils/dates.py
+
+Methods removed:
+ * date_range
+ * days_ago (Use ``pendulum.today('UTC').add(days=-N, ...)``)
diff --git a/tests/providers/databricks/plugins/test_databricks_workflow.py
b/tests/providers/databricks/plugins/test_databricks_workflow.py
index c90caaceee..c140ac4450 100644
--- a/tests/providers/databricks/plugins/test_databricks_workflow.py
+++ b/tests/providers/databricks/plugins/test_databricks_workflow.py
@@ -39,13 +39,11 @@ from
airflow.providers.databricks.plugins.databricks_workflow import (
get_launch_task_id,
get_task_instance,
)
-from airflow.utils.dates import days_ago
from airflow.www.app import create_app
DAG_ID = "test_dag"
TASK_ID = "test_task"
RUN_ID = "test_run_1"
-DAG_RUN_DATE = days_ago(1)
TASK_INSTANCE_KEY = TaskInstanceKey(dag_id=DAG_ID, task_id=TASK_ID,
run_id=RUN_ID, try_number=1)
DATABRICKS_CONN_ID = "databricks_default"
DATABRICKS_RUN_ID = 12345
diff --git a/tests/utils/test_dates.py b/tests/utils/test_dates.py
index 225eb8a3ca..9a789724e5 100644
--- a/tests/utils/test_dates.py
+++ b/tests/utils/test_dates.py
@@ -17,9 +17,8 @@
# under the License.
from __future__ import annotations
-from datetime import datetime, timedelta
+from datetime import timedelta
-import pendulum
import pytest
from dateutil.relativedelta import relativedelta
@@ -27,21 +26,6 @@ from airflow.utils import dates, timezone
class TestDates:
- @pytest.mark.filterwarnings(
- "ignore:Function `days_ago` is
deprecated.*:airflow.exceptions.RemovedInAirflow3Warning"
- )
- def test_days_ago(self):
- today = pendulum.today()
- today_midnight =
pendulum.instance(datetime.fromordinal(today.date().toordinal()))
-
- assert dates.days_ago(0) == today_midnight
- assert dates.days_ago(100) == today_midnight + timedelta(days=-100)
-
- assert dates.days_ago(0, hour=3) == today_midnight + timedelta(hours=3)
- assert dates.days_ago(0, minute=3) == today_midnight +
timedelta(minutes=3)
- assert dates.days_ago(0, second=3) == today_midnight +
timedelta(seconds=3)
- assert dates.days_ago(0, microsecond=3) == today_midnight +
timedelta(microseconds=3)
-
def test_parse_execution_date(self):
execution_date_str_wo_ms = "2017-11-02 00:00:00"
execution_date_str_w_ms = "2017-11-05 16:18:30.989729"
@@ -103,48 +87,3 @@ class TestDates:
arr4 = dates.scale_time_units([200000, 100000], "days")
assert arr4 == pytest.approx([2.3147, 1.1574], rel=1e-3)
-
-
[email protected](
- r"ignore:`airflow.utils.dates.date_range\(\)` is
deprecated:airflow.exceptions.RemovedInAirflow3Warning"
-)
-class TestUtilsDatesDateRange:
- def test_no_delta(self):
- assert dates.date_range(datetime(2016, 1, 1), datetime(2016, 1, 3)) ==
[]
-
- def test_end_date_before_start_date(self):
- with pytest.raises(ValueError, match="Wait. start_date needs to be
before end_date"):
- dates.date_range(datetime(2016, 2, 1), datetime(2016, 1, 1),
delta=timedelta(seconds=1))
-
- def test_both_end_date_and_num_given(self):
- with pytest.raises(ValueError, match="Wait. Either specify end_date OR
num"):
- dates.date_range(datetime(2016, 1, 1), datetime(2016, 1, 3),
num=2, delta=timedelta(seconds=1))
-
- def test_invalid_delta(self):
- exception_msg = "Wait. delta must be either datetime.timedelta or cron
expression as str"
- with pytest.raises(TypeError, match=exception_msg):
- dates.date_range(datetime(2016, 1, 1), datetime(2016, 1, 3),
delta=1)
-
- def test_positive_num_given(self):
- for num in range(1, 10):
- result = dates.date_range(datetime(2016, 1, 1), num=num,
delta=timedelta(1))
- assert len(result) == num
-
- for i in range(num):
- assert timezone.is_localized(result[i])
-
- def test_negative_num_given(self):
- for num in range(-1, -5, -10):
- result = dates.date_range(datetime(2016, 1, 1), num=num,
delta=timedelta(1))
- assert len(result) == -num
-
- for i in range(num):
- assert timezone.is_localized(result[i])
-
- def test_delta_cron_presets(self):
- preset_range = dates.date_range(datetime(2016, 1, 1), num=2,
delta="@hourly")
- timedelta_range = dates.date_range(datetime(2016, 1, 1), num=2,
delta=timedelta(hours=1))
- cron_range = dates.date_range(datetime(2016, 1, 1), num=2, delta="0 *
* * *")
-
- assert preset_range == timedelta_range
- assert preset_range == cron_range