diff --git a/.github/workflows/deploy-python.yml b/.github/workflows/deploy-python.yml index e743f631c..8ef29d5fb 100644 --- a/.github/workflows/deploy-python.yml +++ b/.github/workflows/deploy-python.yml @@ -37,6 +37,8 @@ jobs: - cp311-musllinux - cp312-manylinux - cp312-musllinux + - cp313-manylinux + - cp313-musllinux steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1 @@ -48,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # 3.0.0 - name: Build Wheels - uses: pypa/cibuildwheel@8d945475ac4b1aac4ae08b2fd27db9917158b6ce # 2.17.0 + uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # 2.21.1 env: CIBW_PLATFORM: linux CIBW_BUILD: "${{ matrix.wheel }}*" diff --git a/newrelic/common/_monotonic.c b/newrelic/common/_monotonic.c index b393c51e1..95f28f1a0 100644 --- a/newrelic/common/_monotonic.c +++ b/newrelic/common/_monotonic.c @@ -121,7 +121,6 @@ static PyMethodDef monotonic_methods[] = { { NULL, NULL } }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_monotonic", /* m_name */ @@ -133,18 +132,13 @@ static struct PyModuleDef moduledef = { NULL, /* m_clear */ NULL, /* m_free */ }; -#endif static PyObject * moduleinit(void) { PyObject *module; -#if PY_MAJOR_VERSION >= 3 module = PyModule_Create(&moduledef); -#else - module = Py_InitModule3("_monotonic", monotonic_methods, NULL); -#endif if (module == NULL) return NULL; @@ -152,17 +146,10 @@ moduleinit(void) return module; } -#if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC init_monotonic(void) -{ - moduleinit(); -} -#else PyMODINIT_FUNC PyInit__monotonic(void) { return moduleinit(); } -#endif /* ------------------------------------------------------------------------- */ diff --git a/newrelic/core/_thread_utilization.c b/newrelic/core/_thread_utilization.c index 8688fdb14..c3cc6b539 100644 --- a/newrelic/core/_thread_utilization.c +++ b/newrelic/core/_thread_utilization.c @@ -16,8 +16,8 @@ /* ------------------------------------------------------------------------- */ +#include #include - #include #ifndef PyVarObject_HEAD_INIT @@ -254,14 +254,10 @@ static PyObject *NRUtilization_enter(NRUtilizationObject *self, PyObject *args) PyObject *func = NULL; dict = PyModule_GetDict(module); -#if PY_MAJOR_VERSION >= 3 func = PyDict_GetItemString(dict, "current_thread"); -#else - func = PyDict_GetItemString(dict, "currentThread"); -#endif if (func) { Py_INCREF(func); - thread = PyEval_CallObject(func, (PyObject *)NULL); + thread = PyObject_Call(func, (PyObject *)NULL, (PyObject *)NULL); if (!thread) PyErr_Clear(); @@ -408,7 +404,6 @@ PyTypeObject NRUtilization_Type = { /* ------------------------------------------------------------------------- */ -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_thread_utilization", /* m_name */ @@ -420,18 +415,13 @@ static struct PyModuleDef moduledef = { NULL, /* m_clear */ NULL, /* m_free */ }; -#endif static PyObject * moduleinit(void) { PyObject *module; -#if PY_MAJOR_VERSION >= 3 module = PyModule_Create(&moduledef); -#else - module = Py_InitModule3("_thread_utilization", NULL, NULL); -#endif if (module == NULL) return NULL; @@ -446,16 +436,9 @@ moduleinit(void) return module; } -#if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC init_thread_utilization(void) -{ - moduleinit(); -} -#else PyMODINIT_FUNC PyInit__thread_utilization(void) { return moduleinit(); } -#endif /* ------------------------------------------------------------------------- */ diff --git a/setup.py b/setup.py index d7c9af8ee..02132b962 100644 --- a/setup.py +++ b/setup.py @@ -61,6 +61,9 @@ def newrelic_agent_guess_next_version(tag_version): + if hasattr(tag_version, "tag"): # For setuptools_scm 7.0+ + tag_version = tag_version.tag + version, _, _ = str(tag_version).partition("+") version_info = list(map(int, version.split("."))) if len(version_info) < 3: @@ -142,6 +145,7 @@ def build_extension(self, ext): "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: System :: Monitoring", @@ -155,7 +159,7 @@ def build_extension(self, ext): "git_describe_command": "git describe --dirty --tags --long --match *.*.*", "write_to": "newrelic/version.txt", }, - setup_requires=["setuptools_scm>=3.2,<7"], + setup_requires=["setuptools_scm>=3.2,<9"], description="New Relic Python Agent", long_description=open(readme_file).read(), url="https://docs.newrelic.com/docs/apm/agents/python-agent/", diff --git a/tests/agent_unittests/test_full_uri_payloads.py b/tests/agent_unittests/test_full_uri_payloads.py index e25a26bd2..054b3e1e7 100644 --- a/tests/agent_unittests/test_full_uri_payloads.py +++ b/tests/agent_unittests/test_full_uri_payloads.py @@ -20,7 +20,10 @@ from testing_support.fixtures import collector_agent_registration_fixture from newrelic.core.agent_protocol import AgentProtocol from newrelic.common.agent_http import HttpClient -from newrelic.core.config import global_settings +from newrelic.core.config import global_settings, _environ_as_bool + +DEVELOPER_MODE = _environ_as_bool("NEW_RELIC_DEVELOPER_MODE", False) or "NEW_RELIC_LICENSE_KEY" not in os.environ +SKIP_IF_DEVELOPER_MODE = pytest.mark.skipif(DEVELOPER_MODE, reason="Cannot connect to collector in developer mode") class FullUriClient(HttpClient): @@ -55,10 +58,7 @@ def session(application): } -@pytest.mark.skipif( - "NEW_RELIC_LICENSE_KEY" not in os.environ, - reason="License key is not expected to be valid", -) +@SKIP_IF_DEVELOPER_MODE @pytest.mark.parametrize( "method,payload", [ @@ -86,10 +86,7 @@ def test_full_uri_payload(session, method, payload): protocol.send(method, payload) -@pytest.mark.skipif( - "NEW_RELIC_LICENSE_KEY" not in os.environ, - reason="License key is not expected to be valid", -) +@SKIP_IF_DEVELOPER_MODE def test_full_uri_connect(): # An exception will be raised here if there's a problem with the response AgentProtocol.connect( diff --git a/tests/datastore_aiomcache/test_aiomcache.py b/tests/datastore_aiomcache/test_aiomcache.py index 3d66e1671..32d9d44d3 100644 --- a/tests/datastore_aiomcache/test_aiomcache.py +++ b/tests/datastore_aiomcache/test_aiomcache.py @@ -23,13 +23,15 @@ from newrelic.api.background_task import background_task from newrelic.api.transaction import set_background_task +from newrelic.common import system_info DB_SETTINGS = memcached_settings()[0] MEMCACHED_HOST = DB_SETTINGS["host"] MEMCACHED_PORT = DB_SETTINGS["port"] MEMCACHED_NAMESPACE = str(os.getpid()) -INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{MEMCACHED_HOST}/{MEMCACHED_PORT}" +INSTANCE_METRIC_HOST = system_info.gethostname() if MEMCACHED_HOST == "127.0.0.1" else MEMCACHED_HOST +INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{INSTANCE_METRIC_HOST}/{MEMCACHED_PORT}" _test_bt_set_get_delete_scoped_metrics = [ ("Datastore/operation/Memcached/set", 1), diff --git a/tests/datastore_bmemcached/test_memcache.py b/tests/datastore_bmemcached/test_memcache.py index fa052c670..94b8a0481 100644 --- a/tests/datastore_bmemcached/test_memcache.py +++ b/tests/datastore_bmemcached/test_memcache.py @@ -22,6 +22,7 @@ from newrelic.api.background_task import background_task from newrelic.api.transaction import set_background_task +from newrelic.common import system_info DB_SETTINGS = memcached_settings()[0] @@ -29,7 +30,8 @@ MEMCACHED_PORT = DB_SETTINGS["port"] MEMCACHED_NAMESPACE = str(os.getpid()) MEMCACHED_ADDR = f"{MEMCACHED_HOST}:{MEMCACHED_PORT}" -INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{MEMCACHED_HOST}/{MEMCACHED_PORT}" +INSTANCE_METRIC_HOST = system_info.gethostname() if MEMCACHED_HOST == "127.0.0.1" else MEMCACHED_HOST +INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{INSTANCE_METRIC_HOST}/{MEMCACHED_PORT}" _test_bt_set_get_delete_scoped_metrics = [ ("Datastore/operation/Memcached/set", 1), diff --git a/tests/datastore_pymemcache/test_memcache.py b/tests/datastore_pymemcache/test_memcache.py index ce2cf6aa2..66033af44 100644 --- a/tests/datastore_pymemcache/test_memcache.py +++ b/tests/datastore_pymemcache/test_memcache.py @@ -21,13 +21,17 @@ from newrelic.api.background_task import background_task from newrelic.api.transaction import set_background_task +from newrelic.common import system_info + DB_SETTINGS = memcached_settings()[0] MEMCACHED_HOST = DB_SETTINGS["host"] MEMCACHED_PORT = DB_SETTINGS["port"] MEMCACHED_NAMESPACE = DB_SETTINGS["namespace"] - MEMCACHED_ADDR = (MEMCACHED_HOST, int(MEMCACHED_PORT)) +INSTANCE_METRIC_HOST = system_info.gethostname() if MEMCACHED_HOST == "127.0.0.1" else MEMCACHED_HOST +INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{INSTANCE_METRIC_HOST}/{MEMCACHED_PORT}" + _test_bt_set_get_delete_scoped_metrics = [ ("Datastore/operation/Memcached/set", 1), @@ -43,7 +47,7 @@ ("Datastore/operation/Memcached/set", 1), ("Datastore/operation/Memcached/get", 1), ("Datastore/operation/Memcached/delete", 1), - (f"Datastore/instance/Memcached/{MEMCACHED_HOST}/{MEMCACHED_PORT}", 3), + (INSTANCE_METRIC_NAME, 3), ] @@ -81,7 +85,7 @@ def test_bt_set_get_delete(): ("Datastore/operation/Memcached/set", 1), ("Datastore/operation/Memcached/get", 1), ("Datastore/operation/Memcached/delete", 1), - (f"Datastore/instance/Memcached/{MEMCACHED_HOST}/{MEMCACHED_PORT}", 3), + (INSTANCE_METRIC_NAME, 3), ] diff --git a/tox.ini b/tox.ini index 1d7fe60af..d31ab3653 100644 --- a/tox.ini +++ b/tox.ini @@ -44,131 +44,148 @@ setupdir = {toxinidir} ; Fail tests when interpreters are missing. skip_missing_interpreters = false envlist = - elasticsearchserver07-datastore_elasticsearch-{py37,py38,py39,py310,py311,py312,pypy310}-elasticsearch07, - elasticsearchserver08-datastore_elasticsearch-{py37,py38,py39,py310,py311,py312,pypy310}-elasticsearch08, - firestore-datastore_firestore-{py37,py38,py39,py310,py311,py312}, - grpc-framework_grpc-{py37,py38,py39,py310,py311,py312}-grpclatest, - kafka-messagebroker_confluentkafka-py39-confluentkafka{0107,0106}, + elasticsearchserver07-datastore_elasticsearch-{py37,py38,py39,py310,py311,py312,py313,pypy310}-elasticsearch07, + elasticsearchserver08-datastore_elasticsearch-{py37,py38,py39,py310,py311,py312,py313,pypy310}-elasticsearch08, + firestore-datastore_firestore-{py37,py38,py39,py310,py311,py312,py313}, + grpc-framework_grpc-{py37,py38,py39,py310,py311,py312,py313}-grpclatest, + kafka-messagebroker_confluentkafka-py39-confluentkafka{0108,0107,0106}, kafka-messagebroker_confluentkafka-{py37,py38,py39,py310,py311,py312}-confluentkafkalatest, - kafka-messagebroker_confluentkafka-py39-confluentkafka{0108}, + ;; Package not ready for Python 3.13 (missing wheel) + ; kafka-messagebroker_confluentkafka-py313-confluentkafkalatest, kafka-messagebroker_kafkapython-py38-kafkapython{020001,020000}, kafka-messagebroker_kafkapython-{py37,py38,pypy310}-kafkapythonlatest, - kafka-messagebroker_kafkapython-{py38,py39,py310,py311,py312,pypy310}-kafkapythonnglatest, - memcached-datastore_bmemcached-{py37,py38,py39,py310,py311,py312}-memcached030, - memcached-datastore_aiomcache-{py38,py39,py310,py311,py312}-memcached030, - memcached-datastore_memcache-{py37,py38,py39,py310,py311,py312,pypy310}-memcached01, + kafka-messagebroker_kafkapython-{py38,py39,py310,py311,py312,py313,pypy310}-kafkapythonnglatest, + memcached-datastore_aiomcache-{py38,py39,py310,py311,py312,py313}, + memcached-datastore_bmemcached-{py37,py38,py39,py310,py311,py312,py313}, + memcached-datastore_memcache-{py37,py38,py39,py310,py311,py312,py313,pypy310}-memcached01, memcached-datastore_pylibmc-py37, - memcached-datastore_pymemcache-{py37,py38,py39,py310,py311,py312,pypy310}, + memcached-datastore_pymemcache-{py37,py38,py39,py310,py311,py312,py313,pypy310}, mongodb-datastore_pymongo-{py37,py38,py39,py310,py311,py312}-pymongo03, - mongodb-datastore_pymongo-{py37,py38,py39,py310,py311,py312,pypy310}-pymongo04, - mssql-datastore_pymssql-{py37,py38,py39,py310,py311,py312}, - mysql-datastore_mysql-mysqllatest-{py37,py38,py39,py310,py311,py312}, - mysql-datastore_pymysql-{py37,py38,py39,py310,py311,py312,pypy310}, + mongodb-datastore_pymongo-{py37,py38,py39,py310,py311,py312,py313,pypy310}-pymongo04, + mssql-datastore_pymssql-{py37,py38,py39,py310,py311,py312,py313}, + mysql-datastore_mysql-mysqllatest-{py37,py38,py39,py310,py311,py312,py313}, + mysql-datastore_pymysql-{py37,py38,py39,py310,py311,py312,py313,pypy310}, postgres16-datastore_asyncpg-{py37,py38,py39,py310,py311,py312}, - postgres16-datastore_psycopg-{py38,py39,py310,py311,py312,pypy310}-psycopglatest, + ;; Package not ready for Python 3.13 (missing wheel) + ; postgres16-datastore_asyncpg-py313, + postgres16-datastore_psycopg-{py38,py39,py310,py311,py312,py313,pypy310}-psycopglatest, postgres16-datastore_psycopg-py312-psycopg_{purepython,binary,compiled}0301, postgres16-datastore_psycopg2-{py37,py38,py39,py310,py311,py312}-psycopg2latest, postgres16-datastore_psycopg2cffi-{py37,py38,py39,py310,py311,py312}-psycopg2cffilatest, postgres16-datastore_pyodbc-{py37,py38,py39,py310,py311,py312}-pyodbclatest, + ;; Package not ready for Python 3.13 (missing wheel) + ; postgres16-datastore_pyodbc-py313-pyodbclatest, postgres9-datastore_postgresql-{py37,py38,py39}, - python-adapter_asgiref-{py37,py38,py39,py310,py311,py312,pypy310}-asgireflatest, + python-adapter_asgiref-{py37,py38,py39,py310,py311,py312,py313,pypy310}-asgireflatest, python-adapter_asgiref-py310-asgiref{0303,0304,0305,0306,0307}, - python-adapter_cheroot-{py37,py38,py39,py310,py311,py312}, - python-adapter_daphne-{py37,py38,py39,py310,py311,py312}-daphnelatest, + python-adapter_cheroot-{py37,py38,py39,py310,py311,py312,py313}, + python-adapter_daphne-{py37,py38,py39,py310,py311,py312,py313}-daphnelatest, python-adapter_gevent-{py37,py38,py310,py311,py312}, - python-adapter_gunicorn-{py37,py38,py39,py310,py311,py312}-aiohttp03-gunicornlatest, - python-adapter_hypercorn-{py38,py39,py310,py311,py312}-hypercornlatest, + ;; Package not ready for Python 3.13 (missing wheel) + ; python-adapter_gevent-py313, + python-adapter_gunicorn-{py37,py38,py39,py310,py311,py312,py313}-aiohttp03-gunicornlatest, + python-adapter_hypercorn-{py38,py39,py310,py311,py312,py313}-hypercornlatest, python-adapter_hypercorn-py38-hypercorn{0010,0011,0012,0013}, - python-adapter_uvicorn-{py37,py38,py39,py310,py311,py312}-uvicornlatest, + python-adapter_uvicorn-{py37,py38,py39,py310,py311,py312,py313}-uvicornlatest, python-adapter_uvicorn-py38-uvicorn014, - python-adapter_waitress-{py37,py38,py39,py310,py311,py312}-waitresslatest, + python-adapter_waitress-{py37,py38,py39,py310,py311,py312,py313}-waitresslatest, python-adapter_waitress-{py37,py38,py39,py310}-waitress02, python-adapter_waitress-{py37,py38,py39}-waitress010404, - python-agent_features-{py37,py38,py39,py310,py311,py312}-{with,without}_extensions, + python-agent_features-{py37,py38,py39,py310,py311,py312,py313}-{with,without}_extensions, python-agent_features-pypy310-without_extensions, - python-agent_streaming-{py37,py38,py39,py310,py311,py312}-protobuf04-{with,without}_extensions, + python-agent_streaming-{py37,py38,py39,py310,py311,py312,py313}-protobuf04-{with,without}_extensions, python-agent_streaming-py39-protobuf{03,0319}-{with,without}_extensions, - python-agent_unittests-{py37,py38,py39,py310,py311,py312}-{with,without}_extensions, + python-agent_unittests-{py37,py38,py39,py310,py311,py312,py313}-{with,without}_extensions, python-agent_unittests-pypy310-without_extensions, - python-application_celery-{py37,py38,py39,py310,py311,py312,pypy310}-celerylatest, + python-application_celery-{py37,py38,py39,py310,py311,py312,py313,pypy310}-celerylatest, python-application_celery-py311-celery{0504,0503,0502}, - python-component_djangorestframework-{py37,py38,py39,py310,py311,py312}-djangorestframeworklatest, - python-component_flask_rest-{py38,py39,py310,py311,py312,pypy310}-flaskrestxlatest, + python-component_djangorestframework-{py37,py38,py39,py310,py311,py312,py313}-djangorestframeworklatest, + python-component_flask_rest-{py38,py39,py310,py311,py312,py313,pypy310}-flaskrestxlatest, python-component_flask_rest-py37-flaskrestx110, python-component_graphqlserver-{py37,py38,py39,py310,py311,py312}, - python-component_tastypie-{py37,py38,py39,py310,py311,py312,pypy310}-tastypielatest, + ;; Package not ready for Python 3.13 (missing wheel for uvloop) + ; python-component_graphqlserver-py313, + python-component_tastypie-{py37,py38,py39,py310,py311,py312,py313,pypy310}-tastypielatest, python-coroutines_asyncio-{py37,py38,py39,py310,py311,py312,pypy310}, - python-cross_agent-{py37,py38,py39,py310,py311,py312}-{with,without}_extensions, - python-datastore_sqlite-{py37,py38,py39,py310,py311,py312,pypy310}, - python-external_aiobotocore-{py38,py39,py310,py311,py312}-aiobotocorelatest, - python-external_botocore-{py38,py39,py310,py311,py312}-botocorelatest, + ;; Package not ready for Python 3.13 (missing wheel for uvloop) + ; python-coroutines_asyncio-py313, + python-cross_agent-{py37,py38,py39,py310,py311,py312,py313}-{with,without}_extensions, + python-datastore_sqlite-{py37,py38,py39,py310,py311,py312,py313,pypy310}, + python-external_aiobotocore-{py38,py39,py310,py311,py312,py313}-aiobotocorelatest, + python-external_botocore-{py38,py39,py310,py311,py312,py313}-botocorelatest, python-external_botocore-{py311}-botocorelatest-langchain, python-external_botocore-py310-botocore0125, python-external_botocore-py311-botocore128, - python-external_feedparser-{py37,py38,py39,py310,py311,py312}-feedparser06, - python-external_http-{py37,py38,py39,py310,py311,py312}, - python-external_httplib-{py37,py38,py39,py310,py311,py312,pypy310}, - python-external_httplib2-{py37,py38,py39,py310,py311,py312,pypy310}, - python-external_httpx-{py37,py38,py39,py310,py311,py312}, - python-external_requests-{py37,py38,py39,py310,py311,py312,pypy310}, - python-external_urllib3-{py37,py38,py39,py310,py311,py312,pypy310}-urllib3latest, - python-external_urllib3-{py37,py312,pypy310}-urllib30126, - python-framework_aiohttp-{py37,py38,py39,py310,py311,py312,pypy310}-aiohttp03, - python-framework_ariadne-{py37,py38,py39,py310,py311,py312}-ariadnelatest, + python-external_feedparser-{py37,py38,py39,py310,py311,py312,py313}-feedparser06, + python-external_http-{py37,py38,py39,py310,py311,py312,py313}, + python-external_httplib-{py37,py38,py39,py310,py311,py312,py313,pypy310}, + python-external_httplib2-{py37,py38,py39,py310,py311,py312,py313,pypy310}, + python-external_httpx-{py37,py38,py39,py310,py311,py312,py313}, + python-external_requests-{py37,py38,py39,py310,py311,py312,py313,pypy310}, + python-external_urllib3-{py37,py38,py39,py310,py311,py312,py313,pypy310}-urllib3latest, + python-external_urllib3-{py37,py312,py313,pypy310}-urllib30126, + python-framework_aiohttp-{py37,py38,py39,py310,py311,py312,py313,pypy310}-aiohttp03, + python-framework_ariadne-{py37,py38,py39,py310,py311,py312,py313}-ariadnelatest, python-framework_ariadne-py37-ariadne{0011,0012,0013}, - python-framework_bottle-{py37,py38,py39,py310,py311,py312,pypy310}-bottle0012, - python-framework_cherrypy-{py37,py38,py39,py310,py311,py312,pypy310}-CherryPylatest, - python-framework_django-{py37,py38,py39,py310,py311,py312}-Djangolatest, + python-framework_bottle-{py37,py38,py39,py310,py311,py312,py313,pypy310}-bottle0012, + python-framework_cherrypy-{py37,py38,py39,py310,py311,py312,py313,pypy310}-CherryPylatest, + python-framework_django-{py37,py38,py39,py310,py311,py312,py313}-Djangolatest, python-framework_django-{py39}-Django{0202,0300,0301,0302,0401}, - python-framework_falcon-{py37,py312}-falcon0300, python-framework_falcon-{py37,py38,py39,py310,py311,py312,pypy310}-falconlatest, - python-framework_falcon-{py38,py39,py310,py311,py312,pypy310}-falconmaster, - python-framework_fastapi-{py37,py38,py39,py310,py311,py312}, + ;; Package not ready for Python 3.13 (missing wheel) + ; python-framework_falcon-py313-falconlatest, + python-framework_falcon-{py38,py39,py310,py311,py312,py313,pypy310}-falconmaster, + python-framework_fastapi-{py37,py38,py39,py310,py311,py312,py313}, python-framework_flask-py37-flask020205, - python-framework_flask-{py38,py39,py310,py311,py312,pypy310}-flask{020205,latest,master}, - python-framework_graphene-{py37,py38,py39,py310,py311,py312}-graphenelatest, - python-framework_graphql-{py37,py38,py39,py310,py311,py312,pypy310}-graphql03, - ; Remove graphqlmaster tests. - python-framework_graphql-{py37,py38,py39,py310,py311,py312,pypy310}-graphql{latest}, + python-framework_flask-{py38,py39,py310,py311,py312,pypy310}-flask02, + python-framework_flask-{py38,py39,py310,py311,py312,py313,pypy310}-flask{latest,master}, + python-framework_graphene-{py37,py38,py39,py310,py311,py312,py313}-graphenelatest, + python-framework_graphql-{py37,py38,py39,py310,py311,py312,py313,pypy310}-graphql03, + python-framework_graphql-{py37,py38,py39,py310,py311,py312,py313,pypy310}-graphql{latest}, python-framework_graphql-py37-graphql{0301,0302}, - python-framework_pyramid-{py37,py38,py39,py310,py311,py312,pypy310}-Pyramidlatest, - python-framework_pyramid-{py37,py38,py39,py310,py311,py312,pypy310}-Pyramid0110-cornice, + python-framework_pyramid-{py37,py38,py39,py310,py311,py312,py313,pypy310}-Pyramidlatest, + python-framework_pyramid-{py37,py38,py39,py310,py311,py312,py313,pypy310}-Pyramid0110-cornice, python-framework_sanic-{py37,py38,py39,py310,py311,py312,pypy310}-saniclatest, + ;; Package not ready for Python 3.13 (missing wheel for uvloop) + ; python-framework_sanic-py313-saniclatest, python-framework_sanic-{py38,pypy310}-sanic{200904,210300,2109,2112,2203,2290}, python-framework_starlette-{py310,pypy310}-starlette{0014,0015,0019,0028}, - python-framework_starlette-{py37,py38,py39,py310,py311,py312,pypy310}-starlettelatest, + python-framework_starlette-{py37,py38,py39,py310,py311,py312,py313,pypy310}-starlettelatest, python-framework_starlette-{py37,py38}-starlette002001, python-framework_strawberry-{py38,py39,py310,py311,py312}-strawberry02352, - python-framework_strawberry-{py37,py38,py39,py310,py311,py312}-strawberrylatest, - python-framework_tornado-{py38,py39,py310,py311,py312}-tornadolatest, - python-framework_tornado-{py39,py310,py311,py312}-tornadomaster, - python-logger_logging-{py37,py38,py39,py310,py311,py312,pypy310}, - python-logger_loguru-{py37,py38,py39,py310,py311,py312,pypy310}-logurulatest, - python-logger_structlog-{py37,py38,py39,py310,py311,py312,pypy310}-structloglatest, + python-framework_strawberry-{py37,py38,py39,py310,py311,py312,py313}-strawberrylatest, + python-framework_tornado-{py38,py39,py310,py311,py312,py313}-tornadolatest, + python-framework_tornado-{py39,py310,py311,py312,py313}-tornadomaster, + python-logger_logging-{py37,py38,py39,py310,py311,py312,py313,pypy310}, + python-logger_loguru-{py37,py38,py39,py310,py311,py312,py313,pypy310}-logurulatest, + python-logger_structlog-{py37,py38,py39,py310,py311,py312,py313,pypy310}-structloglatest, python-mlmodel_langchain-{py39,py310,py311,py312}, + ;; Package not ready for Python 3.13 (missing wheel for numpy) + ; python-mlmodel_langchain-py313, python-mlmodel_openai-openai0-{py37,py38,py39,py310,py311,py312}, python-mlmodel_openai-openai107-py312, - python-mlmodel_openai-openailatest-{py37,py38,py39,py310,py311,py312}, + python-mlmodel_openai-openailatest-{py37,py38,py39,py310,py311,py312,py313}, python-mlmodel_sklearn-{py37}-scikitlearn0101, - python-mlmodel_sklearn-{py38,py39,py310,py311,py312}-scikitlearnlatest, - python-template_genshi-{py37,py38,py39,py310,py311,py312}-genshilatest, - python-template_jinja2-{py38,py39,py310,py311,py312}-jinja2latest, + python-mlmodel_sklearn-{py38,py39,py310,py311,py312,py313}-scikitlearnlatest, + python-template_genshi-{py37,py38,py39,py310,py311,py312,py313}-genshilatest, + python-template_jinja2-{py38,py39,py310,py311,py312,py313}-jinja2latest, python-template_jinja2-py37-jinja2030103, - python-template_mako-{py37,py38,py39,py310,py311,py312}, - rabbitmq-messagebroker_pika-{py37,py38,py39,py310,py311,py312,pypy310}-pikalatest, + python-template_mako-{py37,py38,py39,py310,py311,py312,py313}, + rabbitmq-messagebroker_pika-{py37,py38,py39,py310,py311,py312,py313,pypy310}-pikalatest, redis-datastore_redis-{py37,py311,pypy310}-redis04, - redis-datastore_redis-{py37,py38,py39,py310,py311,py312,pypy310}-redislatest, - rediscluster-datastore_rediscluster-{py37,py312,pypy310}-redislatest, - solr-datastore_pysolr-{py37,py38,py39,py310,py311,py312,pypy310}, + redis-datastore_redis-{py37,py38,py39,py310,py311,py312,py313,pypy310}-redislatest, + rediscluster-datastore_rediscluster-{py37,py312,py313,pypy310}-redislatest, + solr-datastore_pysolr-{py37,py38,py39,py310,py311,py312,py313,pypy310}, [testenv] deps = # Base Dependencies - {py38,py39,py310,py311,py312,pypy310}: pytest==8.3.2 + {py38,py39,py310,py311,py312,py313,pypy310}: pytest==8.3.2 py37: pytest==7.4.4 iniconfig coverage WebTest==3.0.0 + py313: legacy-cgi==2.6.1 # cgi was removed from the stdlib in 3.13, and is required for WebTest # Test Suite Dependencies adapter_asgiref-asgireflatest: asgiref @@ -231,13 +248,12 @@ deps = component_tastypie-tastypielatest: django-tastypie component_tastypie-tastypielatest: django<4.1 component_tastypie-tastypielatest: asgiref<3.7.1 # asgiref==3.7.1 only suppport Python 3.10+ - coroutines_asyncio-{py37,py38,py39,py310,py311,py312}: uvloop + coroutines_asyncio-{py37,py38,py39,py310,py311,py312,py313}: uvloop cross_agent: mock==1.0.1 cross_agent: requests datastore_asyncpg: asyncpg - datastore_aiomcache-memcached030: aiomcache - datastore_bmemcached-memcached030: python-binary-memcached<0.31 - datastore_bmemcached-memcached030: uhashring<2.0 + datastore_aiomcache: aiomcache + datastore_bmemcached: python-binary-memcached datastore_elasticsearch: requests datastore_elasticsearch-elasticsearch07: elasticsearch<8.0 datastore_elasticsearch-elasticsearch08: elasticsearch<9.0 @@ -260,6 +276,7 @@ deps = datastore_pymongo-pymongo04: pymongo<5.0 datastore_pymssql: pymssql datastore_pymysql: PyMySQL<0.11 + datastore_pymysql: cryptography datastore_pysolr: pysolr<4.0 datastore_redis-redis04: redis<5 datastore_redis-redislatest: redis @@ -274,7 +291,7 @@ deps = external_botocore-botocorelatest-langchain: langchain external_botocore-botocore128: botocore<1.29 external_botocore-botocore0125: botocore<1.26 - external_botocore-{py38,py39,py310,py311,py312}: moto + external_botocore: moto external_feedparser-feedparser06: feedparser<7 external_httplib2: httplib2<1.0 external_httpx: httpx<0.17 @@ -306,6 +323,8 @@ deps = framework_fastapi: fastapi framework_fastapi: asyncio framework_flask: Flask-Compress + framework_flask-flask02: flask[async]<3 + framework_flask-flask02: jinja2<3.1.3 framework_flask-flask020205: jinja2<3.1.3 framework_flask-flask020205: flask[async]<2.3 framework_flask-flasklatest: markupsafe