diff --git a/ddtrace/contrib/asyncio/compat.py b/ddtrace/contrib/asyncio/compat.py index fc31448606a..104154e37b3 100644 --- a/ddtrace/contrib/asyncio/compat.py +++ b/ddtrace/contrib/asyncio/compat.py @@ -1,10 +1,16 @@ -import sys +import asyncio + + +if hasattr(asyncio, "current_task"): + + def asyncio_current_task(): + try: + return asyncio.current_task() + except RuntimeError: + return None + -# asyncio.Task.current_task method is deprecated and will be removed in Python -# 3.9. Instead use asyncio.current_task -if sys.version_info >= (3, 7, 0): - from asyncio import current_task as asyncio_current_task else: - import asyncio - asyncio_current_task = asyncio.Task.current_task + def asyncio_current_task(): + return asyncio.Task.current_task() diff --git a/releasenotes/notes/py39-5a422b73ba563189.yaml b/releasenotes/notes/py39-5a422b73ba563189.yaml new file mode 100644 index 00000000000..d6ff646af98 --- /dev/null +++ b/releasenotes/notes/py39-5a422b73ba563189.yaml @@ -0,0 +1,3 @@ +--- +prelude: > + Add support for Python 3.9 diff --git a/riotfile.py b/riotfile.py index 6b0d2d89fe3..7bec20d39e3 100644 --- a/riotfile.py +++ b/riotfile.py @@ -49,6 +49,7 @@ 3.6, 3.7, 3.8, + 3.9, ], pkgs=[("msgpack", [""])], ), diff --git a/setup.py b/setup.py index 6d39d0b4305..f3eb4aac05d 100644 --- a/setup.py +++ b/setup.py @@ -174,6 +174,7 @@ def get_exts_for(name): "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", ], use_scm_version=True, setup_requires=["setuptools_scm[toml]>=4", "cython"], diff --git a/tests/contrib/asyncio/test_tracer.py b/tests/contrib/asyncio/test_tracer.py index 671770d1123..edc6dab0c0c 100644 --- a/tests/contrib/asyncio/test_tracer.py +++ b/tests/contrib/asyncio/test_tracer.py @@ -2,6 +2,7 @@ import pytest from ddtrace.compat import CONTEXTVARS_IS_AVAILABLE +from ddtrace.contrib.asyncio.compat import asyncio_current_task from .utils import AsyncioTestCase, mark_asyncio @@ -16,7 +17,7 @@ class TestAsyncioTracer(AsyncioTestCase): def test_get_call_context(self): # it should return the context attached to the current Task # or create a new one - task = asyncio.Task.current_task() + task = asyncio_current_task() ctx = getattr(task, "__datadog_context", None) assert ctx is None # get the context from the loop creates a new one that @@ -77,7 +78,7 @@ def test_context_task_none(self): # it should handle the case where a Task is not available # Note: the @mark_asyncio is missing to simulate an execution # without a Task - task = asyncio.Task.current_task() + task = asyncio_current_task() # the task is not available assert task is None # but a new Context is still created making the operation safe diff --git a/tests/contrib/asyncio/test_tracer_safety.py b/tests/contrib/asyncio/test_tracer_safety.py index e389da2ff17..25c1f06c43e 100644 --- a/tests/contrib/asyncio/test_tracer_safety.py +++ b/tests/contrib/asyncio/test_tracer_safety.py @@ -1,6 +1,7 @@ import asyncio from ddtrace.provider import DefaultContextProvider +from ddtrace.contrib.asyncio.compat import asyncio_current_task from .utils import AsyncioTestCase, mark_asyncio @@ -22,7 +23,8 @@ def test_get_call_context(self): ctx = self.tracer.get_call_context() assert ctx is not None # test that it behaves the wrong way - task = asyncio.Task.current_task() + task = asyncio_current_task() + assert task task_ctx = getattr(task, "__datadog_context", None) assert task_ctx is None diff --git a/tox.ini b/tox.ini index 03afbcc6f86..508e5f5a046 100644 --- a/tox.ini +++ b/tox.ini @@ -20,31 +20,31 @@ envlist = wait {py27,py35,py36,py37,py38,py39}-profile{,-gevent} {py27,py35,py36,py37,py38,py39}-profile-minreqs{,-gevent} - {py27,py35,py36,py37,py38}-integration - py{27,35,36,37,38}-vendor - {py27,py35,py36,py37,py38}-ddtracerun - {py27,py35,py36,py37,py38}-test_logging + py{27,35,36,37,38,39}-integration + py{27,35,36,37,38,39}-vendor + py{27,35,36,37,38,39}-ddtracerun + py{27,35,36,37,38,39}-test_logging # Integrations environments # aiobotocore dropped Python 3.5 support in 0.12 aiobotocore_contrib-{py35}-aiobotocore{02,03,04,05,07,08,09,010,011} aiobotocore_contrib-{py36}-aiobotocore{02,03,04,05,07,08,09,010,011,012} # aiobotocore 0.2 and 0.4 do not work because they use async as a reserved keyword - aiobotocore_contrib-py{37,38}-aiobotocore{03,05,07,08,09,010,011,012} + aiobotocore_contrib-py{37,38,39}-aiobotocore{03,05,07,08,09,010,011,012} # Python 3.7 needs at least aiohttp 2.3 aiohttp_contrib-{py35,py36}-aiohttp{12,13,20,21,22}-aiohttp_jinja{012,013}-yarl aiohttp_contrib-{py35,py36,py37,py38}-aiohttp23-aiohttp_jinja015-yarl10 aiohttp_contrib-{py35,py36,py37}-aiohttp{30,31,32,33,35,36}-aiohttp_jinja{015}-yarl10 aiohttp_contrib-py38-aiohttp{30,31,32,33,36}-aiohttp_jinja015-yarl10 aiopg_contrib-{py35,py36}-aiopg{012,015} - aiopg_contrib-py{37,38}-aiopg015 - algoliasearch_contrib-py{27,35,36,37,38}-algoliasearch{1,2,} - asgi_contrib-{py36,py37,py38}-asgiref{min,} - asyncio_contrib-{py35,py36,py37,py38} + aiopg_contrib-py{37,38,39}-aiopg015 + algoliasearch_contrib-py{27,35,36,37,38,39}-algoliasearch{1,2,} + asgi_contrib-py{36,37,38,39}-asgiref{min,} + asyncio_contrib-py{35,36,37,38,39} # boto needs moto<1 and moto<1 does not support Python >= 3.7 boto_contrib-{py27,py35,py36}-boto - botocore_contrib-{py27,py35,py36,py37,py38}-botocore - bottle_contrib{,_autopatch}-py{27,35,36,37,38}-bottle{11,12,}-webtest - cassandra_contrib-{py27,py35,py36,py37,py38}-cassandra{35,36,37,38,315} + botocore_contrib-py{27,35,36,37,38,39}-botocore + bottle_contrib{,_autopatch}-py{27,35,36,37,38,39}-bottle{11,12,}-webtest + cassandra_contrib-py{27,35,36,37,38,39}-cassandra{35,36,37,38,315} # Non-4.x celery should be able to use the older redis lib, since it locks to an older kombu celery_contrib-py{27,35,36}-celery31-redis210 # 4.x celery bumps kombu to 4.4+, which requires redis 3.2 or later, this tests against @@ -56,9 +56,9 @@ envlist = celery_contrib-py{27,35,36}-celery42-redis210-kombu43 # Celery 4.3 wants Kombu >= 4.4 and Redis >= 3.2 # Python 3.7 needs Celery 4.3 - celery_contrib-py{27,35,36,37,38}-celery43-redis32-kombu44 - consul_contrib-py{27,35,36,37,38}-consul{07,10,11,} - dbapi_contrib-py{27,35,36,37,38} + celery_contrib-py{27,35,36,37,38,39}-celery43-redis32-kombu44 + consul_contrib-py{27,35,36,37,38,39}-consul{07,10,11,} + dbapi_contrib-py{27,35,36,37,38,39} # Django Python version support # 1.11 2.7, 3.4, 3.5, 3.6, 3.7 (added in 1.11.17) # 2.0 3.4, 3.5, 3.6, 3.7 @@ -69,14 +69,14 @@ envlist = # Source: https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django django_contrib{,_migration}-py{27,35,36}-django{18,111}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached-test_django django_contrib{,_migration}-py35-django{20,21,22}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached-test_django - django_contrib{,_migration}-py{36,37,38}-django{20,21,22,30,}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached-test_django + django_contrib{,_migration}-py{36,37,38,39}-django{20,21,22,30,}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached-test_django django_drf_contrib-py{27,35,36}-django{111}-djangorestframework{34,37}-test_django django_drf_contrib-py35-django{22}-djangorestframework{38,310,}-test_django django_drf_contrib-py{36,37}-django{22}-djangorestframework{38,310,}-test_django - django_drf_contrib-py{36,37,38}-django30-djangorestframework310-test_django - django_drf_contrib-py{36,37,38}-django-djangorestframework311-test_django + django_drf_contrib-py{36,37,38,39}-django30-djangorestframework310-test_django + django_drf_contrib-py{36,37,38,39}-django-djangorestframework311-test_django dogpile_contrib-py{27,35}-dogpilecache{06,07,08,09} - dogpile_contrib-py{36,37,38}-dogpilecache{06,07,08,09,10,} + dogpile_contrib-py{36,37,38,39}-dogpilecache{06,07,08,09,10,} elasticsearch_contrib-{py27,py35,py36}-elasticsearch{16,17,18,23,24,51,52,53,54,63,64} elasticsearch_contrib-{py27,py35,py36}-elasticsearch1{100} elasticsearch_contrib-{py27,py35,py36}-elasticsearch2{50} @@ -87,10 +87,10 @@ envlist = flask_contrib{,_autopatch}-{py27,py35,py36}-flask{010,011,012,10}-blinker # Flask <=0.9 does not support Python 3 flask_contrib{,_autopatch}-{py27}-flask{09}-blinker - flask_cache_contrib-{py27,py35,py36,py37,py38}-flask{010,011,012}-flaskcache{013}-memcached-redis{210}-blinker - flask_cache_contrib-{py27}-flask{010,011}-flaskcache{012}-memcached-redis{210}-blinker + flask_cache_contrib-py{27,35,36,37,38,39}-flask{010,011,012}-flaskcache{013}-memcached-redis{210}-blinker + flask_cache_contrib-py27-flask{010,011}-flaskcache{012}-memcached-redis{210}-blinker futures_contrib-py27-futures{30,31,32,} - futures_contrib-py{35,36,37,38} + futures_contrib-py{35,36,37,38,39} gevent_contrib-py27-gevent{11,12,13}-sslmodules gevent_contrib-py{35,36}-gevent{11,12,13}-sslmodules3-sslmodules gevent_contrib-py{37,38}-gevent{13,14}-sslmodules3-sslmodules @@ -103,56 +103,56 @@ envlist = grpc_contrib-py{27,35,36}-grpc{112,114,118,120,121,122}-googleapis-common-protos grpc_contrib-py{37}-grpc{114,118,120,121,122,124,126,128,}-googleapis-common-protos grpc_contrib-py{38}-grpc{124,126,128,}-googleapis-common-protos - httplib_contrib-py{27,35,36,37,38} - jinja2_contrib-py{27,35,36,37,38}-jinja{27,28,29,210,211,} + httplib_contrib-py{27,35,36,37,38,39} + jinja2_contrib-py{27,35,36,37,38,39}-jinja{27,28,29,210,211,} kombu_contrib-py{27,35,36}-kombu{40,41,42,43,44,45,46,} # Kombu >= 4.2 only supports Python 3.7+ - kombu_contrib-py{37,38}-kombu{42,43,44,45,46,} - mako_contrib-py{27,35,36,37,38}-mako{010,100,110,} - molten_contrib-py{36,37,38}-molten{06,07,10,} - mongoengine_contrib-py{27,35,36,37,38}-mongoengine{015,016,017,018,}-pymongo - mysql_contrib-py{27,35,36,37,38}-mysqlconnector{80,} + kombu_contrib-py{37,38,39}-kombu{42,43,44,45,46,} + mako_contrib-py{27,35,36,37,38,39}-mako{010,100,110,} + molten_contrib-py{36,37,38,39}-molten{06,07,10,} + mongoengine_contrib-py{27,35,36,37,38,39}-mongoengine{015,016,017,018,}-pymongo + mysql_contrib-py{27,35,36,37,38,39}-mysqlconnector{80,} mysqldb_contrib-py27-mysqldb{12,} - mysqldb_contrib-py{27,35,36,37,38}-mysqlclient{13,14,} + mysqldb_contrib-py{27,35,36,37,38,39}-mysqlclient{13,14,} psycopg_contrib-py{27,35,36}-psycopg2{24,25,26,27,28} psycopg_contrib-py37-psycopg2{27,28} # psycopg <2.7 doesn't support Python 3.8: https://github.com/psycopg/psycopg2/issues/854 - psycopg_contrib-py{38}-psycopg2{28} - pylibmc_contrib-py{27,35,36,37,38}-pylibmc{140,150,} + psycopg_contrib-py{38,39}-psycopg2{28} + pylibmc_contrib-py{27,35,36,37,38,39}-pylibmc{140,150,} pylons_contrib-py27-pylons{096,097,010,10,} - pymemcache_contrib{,_autopatch}-{py27,py35,py36,py37,py38}-pymemcache{130,140} + pymemcache_contrib{,_autopatch}-py{27,35,36,37,38,39}-pymemcache{130,140} pymongo_contrib-py{27,35,36,37}-pymongo{30,31,32,33,34,35,36,37,38,39,310,}-mongoengine # pymongo does not yet support Python 3.8: https://github.com/pymssql/pymssql/issues/586 # but these tests still work. - pymongo_contrib-py38-pymongo{30,31,32,33,35,36,37,38,39,310,}-mongoengine - pymysql_contrib-py{27,35,36,37,38}-pymysql{07,08,09,} - pynamodb_contrib-{py27,py35,py36,py37,py38}-pynamodb{40,41,42,43,}-moto1 - pyodbc_contrib-py{27,35,36,37,38}-pyodbc{3,4} - pyramid_contrib{,_autopatch}-py{27,35,36,37,38}-pyramid{17,18,19,110,}-webtest - redis_contrib-py{27,35,36,37,38}-redis{210,30,32,33,34,35,} - rediscluster_contrib-py{27,35,36,37,38}-rediscluster{135,136,200,}-redis210 - requests_contrib{,_autopatch}-{py27,py35,py36,py37,py38}-requests{208,209,210,211,212,213,219} + pymongo_contrib-py{38,39}-pymongo{30,31,32,33,35,36,37,38,39,310,}-mongoengine + pymysql_contrib-py{27,35,36,37,38,39}-pymysql{07,08,09,} + pynamodb_contrib-py{27,35,36,37,38,39}-pynamodb{40,41,42,43,}-moto1 + pyodbc_contrib-py{27,35,36,37,38,39}-pyodbc{3,4} + pyramid_contrib{,_autopatch}-py{27,35,36,37,38,39}-pyramid{17,18,19,110,}-webtest + redis_contrib-py{27,35,36,37,38,39}-redis{210,30,32,33,34,35,} + rediscluster_contrib-py{27,35,36,37,38,39}-rediscluster{135,136,200,}-redis210 + requests_contrib{,_autopatch}-py{27,35,36,37,38,39}-requests{208,209,210,211,212,213,219} # python 3.6 requests + gevent regression test # DEV: This is a known issue for gevent 1.1, suggestion is to upgrade to gevent > 1.2 # https://github.com/gevent/gevent/issues/903 requests_gevent_contrib-{py36}-requests{208,209,210,211,212,213,219}-gevent{12,13} requests_gevent_contrib-py{37,38}-requests{208,209,210,211,212,213,219}-gevent13 - sanic_contrib-py{36,37,38}-sanic{1906,1909,1912,2003,2006} - sqlalchemy_contrib-py{27,35,36,37,38}-sqlalchemy{10,11,12,13,}-psycopg228-mysqlconnector - sqlite3_contrib-{py27,py35,py36,py37,py38}-sqlite3 - starlette_contrib-{py36,py37,py38}-starlette{13,} - tornado_contrib-py{27,35,36,37,38}-tornado{44,45} - tornado_contrib-py{37,38}-tornado{50,51,60,} + sanic_contrib-py{36,37,38,39}-sanic{1906,1909,1912,2003,2006} + sqlalchemy_contrib-py{27,35,36,37,38,39}-sqlalchemy{10,11,12,13,}-psycopg228-mysqlconnector + sqlite3_contrib-py{27,35,36,37,38,39}-sqlite3 + starlette_contrib-py{36,37,38,39}-starlette{13,} + tornado_contrib-py{27,35,36,37,38,39}-tornado{44,45} + tornado_contrib-py{37,38,39}-tornado{50,51,60,} tornado_contrib-py27-tornado{44,45}-futures{30,31,32,} - vertica_contrib-{py27,py35,py36,py37,py38}-vertica{060,070} + vertica_contrib-py{27,35,36,37,38,39}-vertica{060,070} # Opentracer - {py27,py35,py36,py37,py38}-opentracer - {py35,py36,py37,py38}-opentracer_asyncio - py{35,36,37,38}-opentracer_tornado-tornado{44,45,50,60,} - {py27}-opentracer_gevent-gevent{10} - {py27,py35,py36}-opentracer_gevent-gevent{11,12} + py{27,35,36,37,38,39}-opentracer + py{35,36,37,38}-opentracer_asyncio + py{35,36,37,38,39}-opentracer_tornado-tornado{44,45,50,60,} + py27-opentracer_gevent-gevent{10} + py{27,35,36}-opentracer_gevent-gevent{11,12} py{37,38}-opentracer_gevent-gevent{13,14} - benchmarks-{py27,py35,py36,py37,py38} + benchmarks-py{27,35,36,37,38,39} isolated_build = true