diff --git a/README.md b/README.md index c8ef4606b1404..24f0f66fcca8e 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Use Airflow to author workflows as directed acyclic graphs (DAGs) of tasks. The - [Semantic versioning](#semantic-versioning) - [Version Life Cycle](#version-life-cycle) - [Support for Python and Kubernetes versions](#support-for-python-and-kubernetes-versions) +- [Approach to dependencies of Airflow](#approach-to-dependencies-of-airflow) - [Contributing](#contributing) - [Who uses Apache Airflow?](#who-uses-apache-airflow) - [Who Maintains Apache Airflow?](#who-maintains-apache-airflow) @@ -308,6 +309,54 @@ They are based on the official release schedule of Python and Kubernetes, nicely * Previous versions [require](https://github.com/apache/airflow/issues/8162) at least Python 3.5.3 when using Python 3. +## Approach to dependencies of Airflow + +Airflow has a lot of dependencies - direct and transitive, also Airflow is both - library and application, +therefore our policies to dependencies has to include both - stability of installation of application, +but also ability to install newer version of dependencies for those users who develop DAGs. We developed +the approach where `constraints` are used to make sure airflow can be installed in a repeatable way, while +we do not limit our users to upgrade most of the dependencies. As a result we decided not to upper-bound +version of Airflow dependencies by default, unless we have good reasons to believe upper-bounding them is +needed because of importance of the dependency as well as risk it involves to upgrade specific dependency. +We also upper-bound the dependencies that we know cause problems. + +The constraint mechanism of ours takes care about finding and upgrading all the non-upper bound dependencies +automatically (providing that all the tests pass). Our `main` build failures will indicate in case there +are versions of dependencies that break our tests - indicating that we should either upper-bind them or +that we should fix our code/tests to account for the upstream changes from those dependencies. + +Whenever we upper-bound such a dependency, we should always comment why we are doing it - i.e. we should have +a good reason why dependency is upper-bound. And we should also mention what is the condition to remove the +binding. + +### Approach for dependencies for Airflow Core + +Those `extras` and `providers` dependencies are maintained in `setup.cfg`. + +There are few dependencies that we decided are important enough to upper-bound them by default, as they are +known to follow predictable versioning scheme, and we know that new versions of those are very likely to +bring breaking changes. We commit to regularly review and attempt to upgrade to the newer versions of +the dependencies as they are released, but this is manual process. + +The important dependencies are: + +* `SQLAlchemy`: upper-bound to specific MINOR version (SQLAlchemy is known to remove deprecations and + introduce breaking changes especially that support for different Databases varies and changes at + various speed (example: SQLAlchemy 1.4 broke MSSQL integration for Airflow) +* `Alembic`: it is important to handle our migrations in predictable and performant way. It is developed + together with SQLAlchemy. Our experience with Alembic is that it very stable in MINOR version +* `Flask`: We are using Flask as the back-bone of our web UI and API. We know major version of Flask + are very likely to introduce breaking changes across those so limiting it to MAJOR version makes sense +* `werkzeug`: the library is known to cause problems in new versions. It is tightly coupled with Flask + libraries, and we should update them together + +### Approach for dependencies in Airflow Providers and extras + +Those `extras` and `providers` dependencies are maintained in `setup.py`. + +By default, we should not upper-bound dependencies for providers, however each provider's maintainer +might decide to add additional limits (and justify them with comment) + ## Contributing Want to help build Apache Airflow? Check out our [contributing documentation](https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst). diff --git a/setup.cfg b/setup.cfg index 573c1ec3b1b25..bd6d97e8c0195 100644 --- a/setup.cfg +++ b/setup.cfg @@ -79,20 +79,30 @@ setup_requires = # DEPENDENCIES_EPOCH_NUMBER in the Dockerfile.ci ##################################################################################################### install_requires = + # Alembic is important to handle our migrations in predictable and performant way. It is developed + # together with SQLAlchemy. Our experience with Alembic is that it very stable in minor version alembic>=1.5.1, <2.0 - argcomplete>=1.10, <3.0 - attrs>=20.0, <21.0 + argcomplete>=1.10 + # We limit the version of attrs to work with the old version of cattrs + attrs>=20.0,<21.0 blinker - cached_property~=1.5;python_version<="3.7" + cached_property>=1.5.0;python_version<="3.7" + # Cattrs upgrades were known to break lineage https://github.com/apache/airflow/issues/16172 + # TODO: Cattrs is now at 3.8 version so we should attempt to upgrade cattrs soon. cattrs~=1.1, !=1.7.* - colorlog>=4.0.2, <7.0 + colorlog>=4.0.2 connexion[swagger-ui,flask]>=2.10.0 cron-descriptor>=1.2.24 croniter>=0.3.17 cryptography>=0.9.3 deprecated>=1.2.13 - dill>=0.2.2, <0.4 + dill>=0.2.2 + # Flask and all related libraries are limited to below 2.0.0 because we expect it to introduce + # Serious breaking changes. Flask 2.0 has been introduced in May 2021 and 2.0.2 version is available + # now (Feb 2022): TODO: we should attempt to migrate to Flask 2 and all below flask libraries soon. flask>=1.1.0, <2.0 + # FlaskAppBuilder is very tight integration for UI but we are likely to remove it as a dependency soon + # TODO: Remove it when we are ready flask-appbuilder~=3.4, <4.0.0 flask-caching>=1.5.0, <2.0.0 flask-login>=0.3, <0.5 @@ -101,44 +111,52 @@ install_requires = gunicorn>=20.1.0 httpx importlib_metadata>=1.7;python_version<"3.9" - importlib_resources~=5.2;python_version<"3.9" - # Logging is broken with itsdangerous > 2 + importlib_resources>=5.2;python_version<"3.9" + # Logging is broken with itsdangerous > 2 - likely due to changed serializing support + # https://itsdangerous.palletsprojects.com/en/2.0.x/changes/#version-2-0-0 + # itsdangerous 2 has been released in May 2020 + # TODO: we should attempt to upgrade to line 2 of itsdangerous itsdangerous>=1.1.0, <2.0 # Jinja2 3.1 will remove the 'autoescape' and 'with' extensions, which would # break Flask 1.x, so we limit this for future compatibility. Remove this # when bumping Flask to >=2. jinja2>=2.10.1,<3.1 + # We are using JSONSchema 3 for unknown reason + # TODO: we should attempt to remove the upper binding of JSONSchema jsonschema~=3.0 lazy-object-proxy lockfile>=0.12.2 - markdown~=3.0 + markdown>=3.0 markupsafe>=1.1.1 marshmallow-oneofschema>=2.0.1 packaging>=14.0 - pendulum~=2.0 - pep562~=1.0;python_version<"3.7" - pluggy~=1.0 - psutil>=4.2.0, <6.0.0 - pygments>=2.0.1, <3.0 + pendulum>=2.0 + pep562>=1.0;python_version<"3.7" + pluggy>=1.0 + psutil>=4.2.0 + pygments>=2.0.1 # python daemon crashes with 'socket operation on non-socket' for python 3.8+ in version < 2.2.4 # https://pagure.io/python-daemon/issue/34 python-daemon>=2.2.4 - python-dateutil>=2.3, <3 - python-nvd3~=0.15.0 - python-slugify~=5.0 + python-dateutil>=2.3 + python-nvd3>=0.15.0 + python-slugify>=5.0 rich>=9.2.0 - setproctitle>=1.1.8, <2 + setproctitle>=1.1.8 # SQL Alchemy 1.4.10 introduces a bug where for PyODBC driver UTCDateTime fields get wrongly converted # as string and fail to be converted back to datetime. It was supposed to be fixed in # https://github.com/sqlalchemy/sqlalchemy/issues/6366 (released in 1.4.12) but apparently our case # is different. Opened https://github.com/sqlalchemy/sqlalchemy/issues/7660 to track it sqlalchemy>=1.3.18,<1.4.10 - sqlalchemy_jsonfield~=1.0 - tabulate>=0.7.5, <0.9 + sqlalchemy_jsonfield>=1.0 + tabulate>=0.7.5 tenacity>=6.2.0 termcolor>=1.1.0 typing-extensions>=3.7.4;python_version<"3.8" unicodecsv>=0.14.1 + # Werkzeug is known to cause breaking changes and it is very closely tied with FlaskAppBuilder and other + # Flask dependencies and the limit to 1.* line should be reviewed when we upgrade Flask and remove + # FlaskAppBuilder. werkzeug~=1.0, >=1.0.1 [options.packages.find] diff --git a/setup.py b/setup.py index b4d5709b452ae..27ca2f09b9f8e 100644 --- a/setup.py +++ b/setup.py @@ -182,6 +182,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version # We limit Pandas to <1.4 because Pandas 1.4 requires SQLAlchemy 1.4 which # We should remove the limits as soon as Flask App Builder releases version 3.4.4 # Release candidate is there: https://pypi.org/project/Flask-AppBuilder/3.4.4rc1/ +# TODO: remove it when we fix all SQLAlchemy 1.4 problems pandas_requirement = 'pandas>=0.17.1, <1.4' # 'Start dependencies group' and 'Start dependencies group' are mark for ./scripts/ci/check_order_setup.py @@ -191,11 +192,15 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'oss2>=2.14.0', ] amazon = [ - 'boto3>=1.15.0,<2.0.0', + 'boto3>=1.15.0', + # watchtower 3 has been released end Jan and introduced breaking change across the board that might + # change logging behaviour: + # https://github.com/kislyuk/watchtower/blob/develop/Changes.rst#changes-for-v300-2022-01-26 + # TODO: update to watchtower >3 'watchtower~=2.0.1', 'jsonpath_ng>=1.5.3', - 'redshift_connector~=2.0.888', - 'sqlalchemy_redshift~=0.8.6', + 'redshift_connector>=2.0.888', + 'sqlalchemy_redshift>=0.8.6', pandas_requirement, ] apache_beam = [ @@ -203,7 +208,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version ] asana = ['asana>=0.10'] async_packages = [ - 'eventlet>= 0.9.7', + 'eventlet>=0.9.7', 'gevent>=0.13', 'greenlet>=0.4.9', ] @@ -212,11 +217,13 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version ] azure = [ 'azure-batch>=8.0.0', - 'azure-cosmos>=4.0.0,<5', + 'azure-cosmos>=4.0.0', 'azure-datalake-store>=0.0.45', 'azure-identity>=1.3.1', 'azure-keyvault>=4.1.0', 'azure-kusto-data>=0.0.43,<0.1', + # Azure integration uses old librarires and the limits below reflect that + # TODO: upgrade to newer versions of all the below libraries 'azure-mgmt-containerinstance>=1.5.0,<2.0', 'azure-mgmt-datafactory>=1.0.0,<2.0', 'azure-mgmt-datalake-store>=0.5.0', @@ -227,11 +234,11 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'azure-storage-file>=2.1.0', ] cassandra = [ - 'cassandra-driver>=3.13.0,<4', + 'cassandra-driver>=3.13.0', ] celery = [ 'celery>=5.2.3', - 'flower~=1.0.0', + 'flower>=1.0.0', ] cgroups = [ 'cgroupspy>=0.1.4', @@ -240,12 +247,15 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'cloudant>=2.0', ] dask = [ + # Dask support is limited, we need Dask team to upgrade support for dask if we were to continue + # Supporting it in the future + # TODO: upgrade libraries used or maybe deprecate and drop DASK support 'cloudpickle>=1.4.1, <1.5.0', 'dask>=2.9.0, <2021.6.1', # dask 2021.6.1 does not work with `distributed` 'distributed>=2.11.1, <2.20', ] databricks = [ - 'requests>=2.26.0, <3', + 'requests>=2.26.0', ] datadog = [ 'datadog>=0.14.0', @@ -254,20 +264,20 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'requests>=2.26.0', ] doc = [ - 'click>=7.1,<9', - 'sphinx>=4.4.0, <5.0.0', + 'click>=7.1', + 'sphinx>=4.4.0', # Without this, Sphinx goes in to a _very_ large backtrack on Python 3.7, # even though Sphinx 4.4.0 has this but with python_version<3.10. 'importlib-metadata>=4.4; python_version < "3.8"', 'sphinx-airflow-theme', 'sphinx-argparse>=0.1.13', - 'sphinx-autoapi~=1.8.0', + 'sphinx-autoapi>=1.8.0', 'sphinx-copybutton', - 'sphinx-jinja~=1.1', + 'sphinx-jinja>=1.1', 'sphinx-rtd-theme>=0.1.6', 'sphinxcontrib-httpdomain>=1.7.0', 'sphinxcontrib-redoc>=1.6.0', - 'sphinxcontrib-spelling~=7.3', + 'sphinxcontrib-spelling>=7.3', ] docker = [ 'docker>=5.0.3', @@ -281,7 +291,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'elasticsearch-dbapi', 'elasticsearch-dsl>=5.0.0', ] -exasol = ['pyexasol>=0.5.1,<1.0.0', pandas_requirement] +exasol = ['pyexasol>=0.5.1', pandas_requirement] facebook = [ 'facebook-business>=6.0.2', ] @@ -292,47 +302,49 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'pygithub', ] google = [ + # Google has very clear rules on what dependencies should be used. All the limits below + # follow strict guidelines of Google Libraries as quoted here: + # While this issue is open, dependents of google-api-core, google-cloud-core. and google-auth + # should preserve >1, <3 pins on these packages. + # https://github.com/googleapis/google-cloud-python/issues/10566 + # Some of Google Packages are limited to <2.0.0 because 2.0.0 releases of the libraries + # Introduced breaking changes across the board. Those libraries should be upgraded soon + # TODO: Upgrade all Google libraries that are limited to <2.0.0 'PyOpenSSL', # The Google Ads 14.0.1 breaks PIP and eager upgrade as it requires # google-api-core>=2.0.0 which cannot be used yet (see below comment) # and https://github.com/apache/airflow/issues/18705#issuecomment-933746150 'google-ads>=12.0.0,<14.0.1', - # Maintainers, please do not require google-api-core>=2.x.x - # Until this issue is closed - # https://github.com/googleapis/google-cloud-python/issues/10566 'google-api-core>=1.25.1,<3.0.0', 'google-api-python-client>=1.6.0,<2.0.0', - # Maintainers, please do not require google-auth>=2.x.x - # Until this issue is closed - # https://github.com/googleapis/google-cloud-python/issues/10566 - 'google-auth>=1.0.0,<3.0.0', + 'google-auth>=1.0.0', 'google-auth-httplib2>=0.0.1', 'google-cloud-aiplatform>=1.7.1,<2.0.0', - 'google-cloud-automl>=2.1.0,<3.0.0', - 'google-cloud-bigquery-datatransfer>=3.0.0,<4.0.0', + 'google-cloud-automl>=2.1.0', + 'google-cloud-bigquery-datatransfer>=3.0.0', 'google-cloud-bigtable>=1.0.0,<2.0.0', - 'google-cloud-build>=3.0.0,<4.0.0', + 'google-cloud-build>=3.0.0', 'google-cloud-container>=0.1.1,<2.0.0', - 'google-cloud-datacatalog>=3.0.0,<4.0.0', - 'google-cloud-dataproc>=3.1.0,<4.0.0', + 'google-cloud-datacatalog>=3.0.0', + 'google-cloud-dataproc>=3.1.0', 'google-cloud-dataproc-metastore>=1.2.0,<2.0.0', 'google-cloud-dlp>=0.11.0,<2.0.0', - 'google-cloud-kms>=2.0.0,<3.0.0', + 'google-cloud-kms>=2.0.0', 'google-cloud-language>=1.1.1,<2.0.0', - 'google-cloud-logging>=2.1.1,<3.0.0', + 'google-cloud-logging>=2.1.1', # 1.1.0 removed field_mask and broke import for released providers # We can remove the <1.1.0 limitation after we release new Google Provider 'google-cloud-memcache>=0.2.0,<1.1.0', - 'google-cloud-monitoring>=2.0.0,<3.0.0', - 'google-cloud-os-login>=2.0.0,<3.0.0', + 'google-cloud-monitoring>=2.0.0', + 'google-cloud-os-login>=2.0.0', 'google-cloud-orchestration-airflow>=1.0.0,<2.0.0', - 'google-cloud-pubsub>=2.0.0,<3.0.0', - 'google-cloud-redis>=2.0.0,<3.0.0', + 'google-cloud-pubsub>=2.0.0', + 'google-cloud-redis>=2.0.0', 'google-cloud-secret-manager>=0.2.0,<2.0.0', 'google-cloud-spanner>=1.10.0,<2.0.0', 'google-cloud-speech>=0.36.3,<2.0.0', 'google-cloud-storage>=1.30,<2.0.0', - 'google-cloud-tasks>=2.0.0,<3.0.0', + 'google-cloud-tasks>=2.0.0', 'google-cloud-texttospeech>=0.4.0,<2.0.0', 'google-cloud-translate>=1.5.0,<2.0.0', 'google-cloud-videointelligence>=1.7.0,<2.0.0', @@ -340,7 +352,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'google-cloud-workflows>=0.1.0,<2.0.0', 'grpcio-gcp>=0.2.2', 'httpx', - 'json-merge-patch~=0.2', + 'json-merge-patch>=0.2', # pandas-gbq 0.15.0 release broke google provider's bigquery import # _check_google_client_version (airflow/providers/google/cloud/hooks/bigquery.py:49) 'pandas-gbq<0.15.0', @@ -348,12 +360,17 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'sqlalchemy-bigquery>=1.2.1', ] grpc = [ + # Google has very clear rules on what dependencies should be used. All the limits below + # follow strict guidelines of Google Libraries as quoted here: + # While this issue is open, dependents of google-api-core, google-cloud-core. and google-auth + # should preserve >1, <3 pins on these packages. + # https://github.com/googleapis/google-cloud-python/issues/10566 'google-auth>=1.0.0, <3.0.0', 'google-auth-httplib2>=0.0.1', 'grpcio>=1.15.0', ] hashicorp = [ - 'hvac~=0.10', + 'hvac>=0.10', ] hdfs = [ 'snakebite-py3', @@ -402,16 +419,17 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version ] leveldb = ['plyvel'] mongo = [ - 'dnspython>=1.13.0,<3.0.0', + 'dnspython>=1.13.0', # pymongo 4.0.0 removes connection option `ssl_cert_reqs` which is used in providers-mongo/2.2.0 + # TODO: Upgrade to pymongo 4.0.0+ 'pymongo>=3.6.0,<4.0.0', ] mssql = [ - 'pymssql~=2.1,>=2.1.5', + 'pymssql>=2.1.5', ] mysql = [ - 'mysql-connector-python>=8.0.11, <9', - 'mysqlclient>=1.3.6,<3', + 'mysql-connector-python>=8.0.11', + 'mysqlclient>=1.3.6', ] neo4j = ['neo4j>=4.2.1'] odbc = [ @@ -424,7 +442,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'cx_Oracle>=5.1.2', ] pagerduty = [ - 'pdpyras>=4.1.2,<5', + 'pdpyras>=4.1.2', ] pandas = [ pandas_requirement, @@ -440,7 +458,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version pinot = [ # pinotdb v0.1.1 may still work with older versions of Apache Pinot, but we've confirmed that it # causes a problem with newer versions. - 'pinotdb>0.1.2,<1.0.0', + 'pinotdb>0.1.2', ] plexus = [ 'arrow>=0.16.0', @@ -449,11 +467,13 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'psycopg2-binary>=2.7.4', ] presto = [ + # The limit to Presto 0.8 for unknown reason + # TODO: Remove the limit 'presto-python-client>=0.7.0,<0.8', pandas_requirement, ] psrp = [ - 'pypsrp~=0.8', + 'pypsrp>=0.8', ] qubole = [ 'qds-sdk>=1.10.4', @@ -462,6 +482,10 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'amqp', ] redis = [ + # Redis 4 introduced a number of changes that likely need testing including mixins in redis commands + # as well as unquoting URLS with `urllib.parse.unquote`: + # https://github.com/redis/redis-py/blob/master/CHANGES + # TODO: upgrade to support redis package >=4 'redis~=3.2', ] salesforce = ['simple-salesforce>=1.0.0', 'tableauserverclient', pandas_requirement] @@ -472,7 +496,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'analytics-python>=1.2.9', ] sendgrid = [ - 'sendgrid>=6.0.0,<7', + 'sendgrid>=6.0.0', ] sentry = [ 'blinker>=1.1', @@ -480,7 +504,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version ] singularity = ['spython>=0.0.56'] slack = [ - 'slack_sdk>=3.0.0,<4.0.0', + 'slack_sdk>=3.0.0', ] snowflake = [ # Snowflake connector 2.7.2 requires pyarrow >=6.0.0 but apache-beam requires < 6.0.0 @@ -497,16 +521,16 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version ssh = [ 'paramiko>=2.6.0', 'pysftp>=0.2.9', - 'sshtunnel>=0.3.2,<0.5', + 'sshtunnel>=0.3.2', ] statsd = [ - 'statsd>=3.3.0, <4.0', + 'statsd>=3.3.0', ] tableau = [ 'tableauserverclient', ] telegram = [ - 'python-telegram-bot~=13.0', + 'python-telegram-bot>=13.0', ] trino = [ 'trino>=0.301.0', @@ -522,7 +546,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'hdfs[avro,dataframe,kerberos]>=2.0.4', ] winrm = [ - 'pywinrm~=0.4', + 'pywinrm>=0.4', ] yandex = [ 'yandexcloud>=0.122.0', @@ -537,6 +561,7 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version # for details. Wy want to install them explicitly because we want to eventually move to # mypyd which does not support installing the types dynamically with --install-types mypy_dependencies = [ + # TODO: upgrade to newer versions of MyPy continuously as they are released 'mypy==0.910', 'types-boto', 'types-certifi', @@ -563,11 +588,11 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version # Dependencies needed for development only devel_only = [ 'aws_xray_sdk', - 'beautifulsoup4~=4.7.1', + 'beautifulsoup4>=4.7.1', 'black', 'blinker', 'bowler', - 'click>=7.1,<9', + 'click>=7.1', 'coverage', 'filelock', 'flake8>=3.6.0', @@ -580,7 +605,9 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'jira', 'jsondiff', 'mongomock', - 'moto~=2.2, >=2.2.12', + # Moto3 is limited for unknown reason + # TODO: attempt to remove the limitation + 'moto~=2.2,>=2.2.12', 'parameterized', 'paramiko', 'pipdeptree', @@ -588,10 +615,16 @@ def write_version(filename: str = os.path.join(*[my_dir, "airflow", "git_version 'pypsrp', 'pygithub', 'pysftp', + # Pytest 7 has been released in February 2022 and we should attempt to upgrade and remove the limit + # It contains a number of potential breaking changes but none of them looks breaking our use + # https://docs.pytest.org/en/latest/changelog.html#pytest-7-0-0-2022-02-03 + # TODO: upgrade it and remove the limit 'pytest~=6.0', 'pytest-asyncio', 'pytest-cov', 'pytest-instafail', + # We should attempt to remove the limit when we upgrade Pytest + # TODO: remove the limit when we upgrade pytest 'pytest-rerunfailures~=9.1', 'pytest-timeouts', 'pytest-xdist',