Auparavant, le package différée google.appengine.ext.deferred dépendait du framework webapp dans Python 2. Étant donné que l'environnement logiciel webapp a été supprimé dans le SDK des services App Engine pour Python 3, vous devez apporter quelques modifications lors de la mise à niveau de votre application Python 2 vers Python 3.
Activer l'API Deferred
Pour activer l'API Deferred pour Python 3, vous n'avez plus besoin de définir builtins.deferred sur on dans le fichier app.yaml. Pour activer l'API, vous devez transmettre use_deferred=True dans l'appel à wrap_wsgi_app().
Si votre application utilise le point de terminaison /_ah/queue/deferred par défaut, l'utilisation de deferred.defer() dans Python 3 reste identique à Python 2.
Si votre application utilise une URL personnalisée pour l'exécution de tâches différées, vous devez apporter des modifications, car la classe TaskHandler du module deferred pour Python 2 a été supprimée dans la version Python 3 de cette API.
Pour définir une URL personnalisée pour l'exécution de tâches différées, l'application peut remplacer la méthode post ou run_from_request dans la classe deferred.Handler (anciennement deferred.TaskHandler dans Python 2) et transmettre le paramètre environ qui représente un dictionnaire contenant des paramètres de requête WSGI. La méthode post peut ensuite être appelée à partir du point de terminaison personnalisé (comme illustré dans les exemples Python 3).
L'utilisation de bout en bout de l'API Deferred Python 3, telle que le routage des requêtes et l'accès au dictionnaire environ, dépend du framework Web vers lequel l'application migre. Comparez les modifications de code apportées à partir de l'exemple Python 2 aux exemples Python 3 des sections suivantes.
Exemples Python 3
L'exemple suivant montre comment exécuter une tâche différée à l'aide d'un point de terminaison par défaut, ainsi que d'un point de terminaison personnalisé dans une application Flask et une application Django.
Flask
importosfromflaskimportFlask,requestfromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")app=Flask(__name__)app.wsgi_app=wrap_wsgi_app(app.wsgi_app,use_deferred=True)classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()@app.route("/counter/increment")defincrement_counter():# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)return"Deferred counter increment."@app.route("/counter/get")defview_counter():counter=Counter.get_or_insert(my_key,count=0)returnstr(counter.count)@app.route("/custom/path",methods=["POST"])defcustom_deferred():print("Executing deferred task.")# request.environ contains the WSGI `environ` dictionary (See PEP 0333)returndeferred.Handler().post(request.environ)
Django
importosfromdjango.confimportsettingsfromdjango.core.wsgiimportget_wsgi_applicationfromdjango.httpimportHttpResponsefromdjango.urlsimportpathfromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()defincrement_counter(request):# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)returnHttpResponse("Deferred counter increment.")defview_counter(request):counter=Counter.get_or_insert(my_key,count=0)returnHttpResponse(str(counter.count))defcustom_deferred(request):print("Executing deferred task.")# request.environ contains the WSGI `environ` dictionary (See PEP 0333)response,status,headers=deferred.Handler().post(request.environ)returnHttpResponse(response,status=status.value)urlpatterns=(path("counter/get",view_counter,name="view_counter"),path("counter/increment",increment_counter,name="increment_counter"),path("custom/path",custom_deferred,name="custom_deferred"),)settings.configure(DEBUG=True,SECRET_KEY="thisisthesecretkey",ROOT_URLCONF=__name__,MIDDLEWARE_CLASSES=("django.middleware.common.CommonMiddleware","django.middleware.csrf.CsrfViewMiddleware","django.middleware.clickjacking.XFrameOptionsMiddleware",),ALLOWED_HOSTS=["*"],)app=wrap_wsgi_app(get_wsgi_application(),use_deferred=True)
Sans framework
importosimportrefromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()defIncrementCounter(environ,start_response):# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)start_response("200 OK",[("Content-Type","text/html")])return[b"Deferred counter increment."]defViewCounter(environ,start_response):counter=Counter.get_or_insert(my_key,count=0)start_response("200 OK",[("Content-Type","text/html")])return[str(counter.count).encode("utf-8")]classCustomDeferredHandler(deferred.Handler):"""Deferred task handler that adds additional logic."""defpost(self,environ):print("Executing deferred task.")returnsuper().post(environ)routes={"counter/increment":IncrementCounter,"counter/get":ViewCounter,"custom/path":CustomDeferredHandler(),}classWSGIApplication:def__call__(self,environ,start_response):path=environ.get("PATH_INFO","").lstrip("/")forregex,handlerinroutes.items():match=re.search(regex,path)ifmatchisnotNone:returnhandler(environ,start_response)start_response("404 Not Found",[("Content-Type","text/plain")])return[b"Not found"]app=wrap_wsgi_app(WSGIApplication(),use_deferred=True)
Exemples de code
Pour afficher les exemples de code complets de ce guide, consultez GitHub.
Sauf indication contraire, le contenu de cette page est régi par une licence Creative Commons Attribution 4.0, et les échantillons de code sont régis par une licence Apache 2.0. Pour en savoir plus, consultez les Règles du site Google Developers. Java est une marque déposée d'Oracle et/ou de ses sociétés affiliées.
Dernière mise à jour le 2025/09/04 (UTC).
[[["Facile à comprendre","easyToUnderstand","thumb-up"],["J'ai pu résoudre mon problème","solvedMyProblem","thumb-up"],["Autre","otherUp","thumb-up"]],[["Difficile à comprendre","hardToUnderstand","thumb-down"],["Informations ou exemple de code incorrects","incorrectInformationOrSampleCode","thumb-down"],["Il n'y a pas l'information/les exemples dont j'ai besoin","missingTheInformationSamplesINeed","thumb-down"],["Problème de traduction","translationIssue","thumb-down"],["Autre","otherDown","thumb-down"]],["Dernière mise à jour le 2025/09/04 (UTC)."],[[["\u003cp\u003eThe Deferred API for Python 3 on Google App Engine no longer requires setting \u003ccode\u003ebuiltins.deferred\u003c/code\u003e in \u003ccode\u003eapp.yaml\u003c/code\u003e; instead, you must use \u003ccode\u003euse_deferred=True\u003c/code\u003e when calling \u003ccode\u003ewrap_wsgi_app()\u003c/code\u003e.\u003c/p\u003e\n"],["\u003cp\u003eThe default behavior of the Deferred API in Python 3 remains consistent with Python 2, using the \u003ccode\u003e/_ah/queue/deferred\u003c/code\u003e URL and the default queue, but keep in mind that Cloud Tasks will differ.\u003c/p\u003e\n"],["\u003cp\u003eWhen using the local development server for testing, you need to set \u003ccode\u003eDEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'\u003c/code\u003e in your \u003ccode\u003eapp.yaml\u003c/code\u003e to make sure the Deferred API works.\u003c/p\u003e\n"],["\u003cp\u003eCustom URLs for deferred task execution in Python 3 require using the \u003ccode\u003edeferred.Handler\u003c/code\u003e class's \u003ccode\u003epost\u003c/code\u003e or \u003ccode\u003erun_from_request\u003c/code\u003e methods, as the \u003ccode\u003eTaskHandler\u003c/code\u003e from Python 2 is no longer available.\u003c/p\u003e\n"],["\u003cp\u003eThe way a Python 3 App uses the Deferred API, including its handling of requests and accessing the \u003ccode\u003eenviron\u003c/code\u003e dictionary, is based on the web framework that is being migrated to, as shown in the provided examples with Flask, Django and without a framework.\u003c/p\u003e\n"]]],[],null,["# Deferred API for Python 3\n\nThis page describes how to use the Deferred API, one of the legacy bundled services,\nwith the [Python 3 runtime](/appengine/docs/standard/python3) for\nthe standard environment. Your app can access the bundled services\nthrough the [**App Engine services SDK for Python 3**](https://github.com/GoogleCloudPlatform/appengine-python-standard).\n\nOverview\n--------\n\nPreviously, the Deferred package [`google.appengine.ext.deferred`](/appengine/docs/legacy/standard/python/refdocs/google.appengine.ext.deferred.deferred)\ndepended on the webapp framework in Python 2. Since the webapp framework has\nbeen removed in the App Engine services SDK for Python 3, you need to\nmake some changes when upgrading your Python 2 app to Python 3.\n\nEnabling the Deferred API\n-------------------------\n\nTo enable the Deferred API for Python 3, you no longer need to set\n[`builtins.deferred`](/appengine/docs/legacy/standard/python/config/appref#builtins)\nto `on` in the `app.yaml` file. Instead, to enable the API, you must pass\n`use_deferred=True` in the call to `wrap_wsgi_app()`.\n| **Important:** When using the [local development server](/appengine/docs/standard/python3/services/access#testing_and_deployment) to test Python 3 apps that use the [Deferred API](/appengine/docs/legacy/standard/python/refdocs/google.appengine.ext.deferred.deferred), you must set the following environment variable in your `app.yaml`: \n| `DEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'`\n\nSimilarites and differences\n---------------------------\n\nBy default, the Deferred API for Python 3 uses the same URL `/_ah/queue/deferred`\nand the same [default queue](/appengine/docs/standard/python/taskqueue/push/migrating-push-queues#creating_queues)\nas it did in Python 2. Note that for apps migrating to [Cloud Tasks](/tasks), the default queue is\n[not created automatically](/appengine/docs/legacy/standard/python/taskqueue/push/migrating-push-queues#creating_queues)\nand the [deferred tasks library is not available](/appengine/docs/legacy/standard/python/taskqueue/push/migrating-push-queues#features-not-available).\n\nIf your app uses the default `/_ah/queue/deferred` endpoint, using\n[`deferred.defer()` in Python 3](/appengine/docs/standard/python3/reference/services/bundled/google/appengine/ext/deferred/deferred)\nremains the same as\n[Python 2](/appengine/docs/legacy/standard/python/taskqueue/push/creating-tasks#using_the_instead_of_a_worker_service).\nIf your app uses a custom URL for execution of deferred tasks, you need to make\nsome changes since the `TaskHandler` class in the `deferred` module for Python 2\nhas been removed in the Python 3 version of this API.\n\nTo set a custom URL for execution of deferred tasks, the app can override either\nthe `post` or the `run_from_request` method in the\n[`deferred.Handler` class](/appengine/docs/standard/python3/reference/services/bundled/google/appengine/ext/deferred/Handler)\n(formerly `deferred.TaskHandler` in Python 2), and pass the `environ` parameter\nwhich represents a dictionary containing WSGI request parameters. The `post` method can then be\ncalled from the custom endpoint (as shown in the [Python 3 samples](#python-3-examples)).\n\nThe end-to-end usage of the Python 3 Deferred API, such as routing of requests and\naccessing the [`environ` dictionary](https://www.python.org/dev/peps/pep-0333/#id19),\ndepends on the web framework the app is migrating to. Compare code changes made\nfrom the Python 2 example to the Python 3 examples in the following sections.\n\nPython 3 examples\n-----------------\n\nThe following example shows how to execute a deferred task using a\ndefault endpoint and a custom endpoint in a Flask app and Django app. \n\n### Flask\n\n import os\n\n from flask import Flask, request\n from google.appengine.api import wrap_wsgi_app\n from google.appengine.ext import deferred\n from google.appengine.ext import ndb\n\n my_key = os.environ.get(\"GAE_VERSION\", \"Missing\")\n\n app = Flask(__name__)\n app.wsgi_app = wrap_wsgi_app(app.wsgi_app, use_deferred=True)\n\n\n class Counter(ndb.Model):\n count = ndb.IntegerProperty(indexed=False)\n\n\n def do_something_later(key, amount):\n entity = Counter.get_or_insert(key, count=0)\n entity.count += amount\n entity.put()\n\n\n @app.route(\"/counter/increment\")\n def increment_counter():\n # Use default URL and queue name, no task name, execute ASAP.\n deferred.defer(do_something_later, my_key, 10)\n\n # Use default URL and queue name, no task name, execute after 1 minute.\n deferred.defer(do_something_later, my_key, 10, _countdown=60)\n\n # Providing non-default task queue arguments\n deferred.defer(do_something_later, my_key, 10, _url=\"/custom/path\", _countdown=120)\n\n return \"Deferred counter increment.\"\n\n\n @app.route(\"/counter/get\")\n def view_counter():\n counter = Counter.get_or_insert(my_key, count=0)\n return str(counter.count)\n\n\n @app.route(\"/custom/path\", methods=[\"POST\"])\n def custom_deferred():\n print(\"Executing deferred task.\")\n # request.environ contains the WSGI `environ` dictionary (See PEP 0333)\n return deferred.Handler().post(request.environ)\n\n### Django\n\n import os\n\n from django.conf import settings\n from django.core.wsgi import get_wsgi_application\n from django.http import HttpResponse\n from django.urls import path\n from google.appengine.api import wrap_wsgi_app\n from google.appengine.ext import deferred\n from google.appengine.ext import ndb\n\n my_key = os.environ.get(\"GAE_VERSION\", \"Missing\")\n\n\n class Counter(ndb.Model):\n count = ndb.IntegerProperty(indexed=False)\n\n\n def do_something_later(key, amount):\n entity = Counter.get_or_insert(key, count=0)\n entity.count += amount\n entity.put()\n\n\n def increment_counter(request):\n # Use default URL and queue name, no task name, execute ASAP.\n deferred.defer(do_something_later, my_key, 10)\n\n # Use default URL and queue name, no task name, execute after 1 minute.\n deferred.defer(do_something_later, my_key, 10, _countdown=60)\n\n # Providing non-default task queue arguments\n deferred.defer(do_something_later, my_key, 10, _url=\"/custom/path\", _countdown=120)\n\n return HttpResponse(\"Deferred counter increment.\")\n\n\n def view_counter(request):\n counter = Counter.get_or_insert(my_key, count=0)\n return HttpResponse(str(counter.count))\n\n\n def custom_deferred(request):\n print(\"Executing deferred task.\")\n # request.environ contains the WSGI `environ` dictionary (See PEP 0333)\n response, status, headers = deferred.Handler().post(request.environ)\n return HttpResponse(response, status=status.value)\n\n\n urlpatterns = (\n path(\"counter/get\", view_counter, name=\"view_counter\"),\n path(\"counter/increment\", increment_counter, name=\"increment_counter\"),\n path(\"custom/path\", custom_deferred, name=\"custom_deferred\"),\n )\n\n settings.configure(\n DEBUG=True,\n SECRET_KEY=\"thisisthesecretkey\",\n ROOT_URLCONF=__name__,\n MIDDLEWARE_CLASSES=(\n \"django.middleware.common.CommonMiddleware\",\n \"django.middleware.csrf.CsrfViewMiddleware\",\n \"django.middleware.clickjacking.XFrameOptionsMiddleware\",\n ),\n ALLOWED_HOSTS=[\"*\"],\n )\n\n app = wrap_wsgi_app(get_wsgi_application(), use_deferred=True)\n\n### Without any framework\n\n import os\n import re\n\n from google.appengine.api import wrap_wsgi_app\n from google.appengine.ext import deferred\n from google.appengine.ext import ndb\n\n my_key = os.environ.get(\"GAE_VERSION\", \"Missing\")\n\n\n class Counter(ndb.Model):\n count = ndb.IntegerProperty(indexed=False)\n\n\n def do_something_later(key, amount):\n entity = Counter.get_or_insert(key, count=0)\n entity.count += amount\n entity.put()\n\n\n def IncrementCounter(environ, start_response):\n # Use default URL and queue name, no task name, execute ASAP.\n deferred.defer(do_something_later, my_key, 10)\n\n # Use default URL and queue name, no task name, execute after 1 minute.\n deferred.defer(do_something_later, my_key, 10, _countdown=60)\n\n # Providing non-default task queue arguments\n deferred.defer(do_something_later, my_key, 10, _url=\"/custom/path\", _countdown=120)\n\n start_response(\"200 OK\", [(\"Content-Type\", \"text/html\")])\n return [b\"Deferred counter increment.\"]\n\n\n def ViewCounter(environ, start_response):\n counter = Counter.get_or_insert(my_key, count=0)\n start_response(\"200 OK\", [(\"Content-Type\", \"text/html\")])\n return [str(counter.count).encode(\"utf-8\")]\n\n\n class CustomDeferredHandler(deferred.Handler):\n \"\"\"Deferred task handler that adds additional logic.\"\"\"\n\n def post(self, environ):\n print(\"Executing deferred task.\")\n return super().post(environ)\n\n\n routes = {\n \"counter/increment\": IncrementCounter,\n \"counter/get\": ViewCounter,\n \"custom/path\": CustomDeferredHandler(),\n }\n\n\n class WSGIApplication:\n def __call__(self, environ, start_response):\n path = environ.get(\"PATH_INFO\", \"\").lstrip(\"/\")\n for regex, handler in routes.items():\n match = re.search(regex, path)\n if match is not None:\n return handler(environ, start_response)\n\n start_response(\"404 Not Found\", [(\"Content-Type\", \"text/plain\")])\n return [b\"Not found\"]\n\n\n app = wrap_wsgi_app(WSGIApplication(), use_deferred=True)\n\nCode samples\n------------\n\nTo view the complete code samples from this guide, see\n[GitHub](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine/standard_python3/bundled-services)."]]