Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit Flask to <2.3 in the wake of 2.2 breaking our tests #25511

Merged
merged 1 commit into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions scripts/in_container/verify_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ class ProviderPackageDetails(NamedTuple):
"scrapbook",
),
("SelectableGroups dict interface is deprecated. Use select.", "markdown"),
("'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.", "flask_sqlalchemy"),
("'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.", "flask_appbuilder"),
# Currently (2.2) Flask app builder has the `remoevd` typo in the messages,
# and they might want to fix it, so adding both
("'_request_ctx_stack' is deprecated and will be remoevd in Flask 2.3.", 'flask_appbuilder'),
("'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.", 'flask_appbuilder'),
("'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.", 'flask_jwt_extended'),
}

KNOWN_COMMON_DEPRECATED_MESSAGES: Set[str] = {
Expand Down
5 changes: 4 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ install_requires =
cryptography>=0.9.3
deprecated>=1.2.13
dill>=0.2.2
flask>=2.0
# Flask 2.3 is scheduled to introduce a number of deprecation removals - some of them might be breaking
# for our dependencies - notably `_app_ctx_stack` and `_request_ctx_stack` removals.
# We should remove the limitation after 2.3 is released and our dependencies are updated to handle it
flask>=2.0,<2.3
# We are tightly coupled with FAB version because we vendored in part of FAB code related to security manager
# This is done as part of preparation to removing FAB as dependency, but we are not ready for it yet
# Every time we update FAB version here, please make sure that you review the classes and models in
Expand Down
20 changes: 11 additions & 9 deletions tests/www/views/test_views_custom_user_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,24 @@ class TestSecurity(unittest.TestCase):
def setUpClass(cls):
settings.configure_orm()
cls.session = settings.Session
cls.app = application.create_app(testing=True)
cls.appbuilder = cls.app.appbuilder
cls.app.config['WTF_CSRF_ENABLED'] = False
cls.security_manager = cls.appbuilder.sm

cls.delete_roles()

def setUp(self):
# We cannot reuse the app in tests (on class level) as in Flask 2.2 this causes
# an exception because app context teardown is removed and if even single request is run via app
# it cannot be re-intialized again by passing it as constructor to SQLA
# This makes the tests slightly slower (but they work with Flask 2.1 and 2.2
self.app = application.create_app(testing=True)
self.appbuilder = self.app.appbuilder
self.app.config['WTF_CSRF_ENABLED'] = False
self.security_manager = self.appbuilder.sm
self.delete_roles()
self.db = SQLA(self.app)
self.appbuilder.add_view(CustomUserDBModelView, "CustomUserDBModelView", category="ModelViews")
self.client = self.app.test_client() # type:ignore

@classmethod
def delete_roles(cls):
def delete_roles(self):
for role_name in ['role_edit_one_dag']:
delete_role(cls.app, role_name)
delete_role(self.app, role_name)

@parameterized.expand(
[
Expand Down
40 changes: 24 additions & 16 deletions tests/www/views/test_views_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from datetime import datetime, timedelta
from typing import List

import freezegun
import pendulum
import pytest
from dateutil.tz import UTC

from airflow.lineage.entities import File
from airflow.models import DagBag
Expand All @@ -36,7 +36,6 @@
from tests.test_utils.mock_operators import MockOperator

DAG_ID = 'test'
CURRENT_TIME = pendulum.DateTime(2021, 9, 7)


@pytest.fixture(autouse=True, scope="module")
Expand Down Expand Up @@ -73,18 +72,17 @@ def dag_without_runs(dag_maker, session, app, monkeypatch):

@pytest.fixture
def dag_with_runs(dag_without_runs):
with freezegun.freeze_time(CURRENT_TIME):
date = dag_without_runs.dag.start_date
run_1 = dag_without_runs.create_dagrun(
run_id='run_1', state=DagRunState.SUCCESS, run_type=DagRunType.SCHEDULED, execution_date=date
)
run_2 = dag_without_runs.create_dagrun(
run_id='run_2',
run_type=DagRunType.SCHEDULED,
execution_date=dag_without_runs.dag.next_dagrun_info(date).logical_date,
)
date = dag_without_runs.dag.start_date
run_1 = dag_without_runs.create_dagrun(
run_id='run_1', state=DagRunState.SUCCESS, run_type=DagRunType.SCHEDULED, execution_date=date
)
run_2 = dag_without_runs.create_dagrun(
run_id='run_2',
run_type=DagRunType.SCHEDULED,
execution_date=dag_without_runs.dag.next_dagrun_info(date).logical_date,
)

yield run_1, run_2
yield run_1, run_2


def test_no_runs(admin_client, dag_without_runs):
Expand Down Expand Up @@ -155,13 +153,23 @@ def test_one_run(admin_client, dag_with_runs: List[DagRun], session):
session.flush()

resp = admin_client.get(f'/object/grid_data?dag_id={DAG_ID}', follow_redirects=True)

assert resp.status_code == 200, resp.json
assert resp.json == {

# We cannot use freezegun here as it does not play well with Flask 2.2 and SqlAlchemy
# Unlike real datetime, when FakeDatetime is used, it coerces to
# '2020-08-06 09:00:00+00:00' which is rejected by MySQL for EXPIRY Column
current_date_placeholder = '2022-01-02T00:00:00+00:00'
actual_date_in_json = datetime.fromisoformat(resp.json['dag_runs'][0]['end_date'])
assert datetime.now(tz=UTC) - actual_date_in_json < timedelta(minutes=5)
res = resp.json
res['dag_runs'][0]['end_date'] = current_date_placeholder
assert res == {
'dag_runs': [
{
'data_interval_end': '2016-01-02T00:00:00+00:00',
'data_interval_start': '2016-01-01T00:00:00+00:00',
'end_date': '2021-09-07T00:00:00+00:00',
'end_date': current_date_placeholder,
'execution_date': '2016-01-01T00:00:00+00:00',
'last_scheduling_decision': None,
'run_id': 'run_1',
Expand Down
13 changes: 7 additions & 6 deletions tests/www/views/test_views_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
import re
import unittest.mock
import urllib.parse
from datetime import timedelta

import pytest
from freezegun import freeze_time

from airflow import settings
from airflow.exceptions import AirflowException
Expand Down Expand Up @@ -546,7 +546,6 @@ def test_run_with_runnable_states(_, admin_client, session, state):
'airflow.executors.executor_loader.ExecutorLoader.get_default_executor',
return_value=_ForceHeartbeatCeleryExecutor(),
)
@freeze_time("2020-07-07 09:00:00")
def test_run_ignoring_deps_sets_queued_dttm(_, admin_client, session):
task_id = 'runme_0'
session.query(TaskInstance).filter(TaskInstance.task_id == task_id).update(
Expand All @@ -566,10 +565,12 @@ def test_run_ignoring_deps_sets_queued_dttm(_, admin_client, session):
resp = admin_client.post('run', data=form, follow_redirects=True)

assert resp.status_code == 200

assert session.query(TaskInstance.queued_dttm).filter(TaskInstance.task_id == task_id).all() == [
(timezone.utcnow(),)
]
# We cannot use freezegun here as it does not play well with Flask 2.2 and SqlAlchemy
# Unlike real datetime, when FakeDatetime is used, it coerces to
# '2020-08-06 09:00:00+00:00' which is rejected by MySQL for EXPIRY Column
assert timezone.utcnow() - session.query(TaskInstance.queued_dttm).filter(
TaskInstance.task_id == task_id
).scalar() < timedelta(minutes=5)


@pytest.mark.parametrize("state", QUEUEABLE_STATES)
Expand Down