Access legacy bundled services for Python 3 Stay organized with collections Save and categorize content based on your preferences.
This page describes how to install and use legacy bundled serviceswith thePython 3 runtime forthe standard environment. Your app must access the bundled servicesthrough theApp Engine services SDK for Python 3.
Note: To continue using legacy bundled services with thelatest supported version of Python, ensure youupgrade to the latest version of the App Engine services SDK by addingappengine-python-standard>=1.0.0 in yourrequirements.txt file.Before you begin
- Refer to thelist of legacy bundled services APIsyou can call in the Python runtime.
- Before starting a migration project to Python 3, see theruntime migration overview andmigration considerations when using legacy bundled services.
Installing the App Engine services SDK
To install the App Engine services SDK, follow these steps:
Include the SDK with your app by adding the following line to your
requirements.txtfile:appengine-python-standard>=1.0.0You can find the SDK on GitHub under the
appengine-python-standardrepository, and onPyPI.Add the following code in your main Python script. This code creates WSGImiddleware that sets the variables required to enable your API calls.
Flask
fromflaskimportFlaskfromgoogle.appengine.apiimportwrap_wsgi_appapp=Flask(__name__)app.wsgi_app=wrap_wsgi_app(app.wsgi_app)Django
fromDJANGO_PROJECT_NAME.wsgi import applicationfrom google.appengine.api import wrap_wsgi_appapp = wrap_wsgi_app(application)Pyramid
frompyramid.configimportConfiguratorfromgoogle.appengine.apiimportwrap_wsgi_appconfig=Configurator()# make configuration settingsapp=config.make_wsgi_app()app=wrap_wsgi_app(app)WSGI
importgoogle.appengine.apidefapp(environ,start_response):start_response('200 OK',[('Content-Type','text/plain')])yieldb'Hello world!\n'app=google.appengine.api.wrap_wsgi_app(app)Add the following line to your
app.yamlfile before deploying your app:app_engine_apis:trueTo deploy your app, use the
gcloud app deploycommand.
Migration considerations
You should be aware of the following considerations if you are migrating tothe Python 3 runtime and your app useslegacy bundled services.
Testing
Important:- Before running the local development server, follow thesetup instructions.
- The
dev_appservertool does not support development of Python 3 appson Windows.
To locally test the legacy bundled services functionality in yourPython 3 app, use thelocal development server.When running thedev_appserver.py command, you must set the--runtime_python_path argument to include a path to the Python 3 interpreter.For example:
python3CLOUD_SDK_ROOT/bin/dev_appserver.py--runtime_python_path=/usr/bin/python3You can also set the argument to a comma-separated list of[RUNTIME_ID]=[PYTHON_INTERPRETER_PATH] pairs. For example:
python3CLOUD_SDK_ROOT/bin/dev_appserver.py--runtime_python_path="python27=/user/bin/python2.7,python3=/usr/bin/python3"app.yaml:DEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'Pickle Compatibility
Shared services including Memcache, Cloud NDB anddeferreduse thepickle module toserialize and share Python objects. If your App Engine environment usesboth Python 2 and Python 3, which is common during a migration, you must ensurethat shared serialized objects written by one version of Python can bereconstitued by the other. You can find guidance on implementing cross versionpickle compatibility in theguide.
By default, Python 3 uses pickling protocols that are not supported in Python 2.This can cause failures when your app tries to reconstitute a Python objectin a Python 2 environment that was written in a Python 3 environment.To avoid this issue, set the followingenvironment variablesin theapp.yaml file for your Python 3 app as needed:
- For apps that use Memcache including apps that use NDB, set:
MEMCACHE_USE_CROSS_COMPATIBLE_PROTOCOL: 'True' - For apps that use NDB to connect to Datastore, set:
NDB_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True' - For apps that use deferred, set:
DEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'
In Python 2,string objects hold a sequence of 8 bit byte values. In Python3,string objects hold a sequence of unicode characters. By default Python 3pickle translates a Python 2string to unicode by interpreting the Python 3string as ASCII. This can lead to errors for values outside the ASCIIcharacter range of 0 to 127. Memcache supports overriding this default mapping.
fromgoogle.appengine.apiimportmemcacheimportsix.moves.cPickleaspickledef_unpickle_factory(file):returnpickle.Unpickler(file,encoding='latin1')memcache.setup_client(memcache.Client(unpickler=_unpickle_factory))Thelatin1 encoding, defines a mapping for each of the 256 possible values ofeach byte in a Python 2string. This prevents decoding errors. However, ifyour Python 2string contains actual unicode data outside of thelatin1range, such as data read from a file, cPickle won't map the datacorrectly. Therefore, it is important that you update your Python 2 code to holdunicode data withunicode objects and notstring objects, for objects youPickle. The compatibilityguide includes details onthe needed updates.
The previous described method for updating your Python 2 code to produce Python3 compatible serializations addresses short-lived serializations, such as thosestored in Memcache. You might need to update or rewrite long-lived Python 2serializations, such as those stored in Datastore as part of yourmigration. For example, serialization written usinggoogle.appengine.ext.ndb.model.PicklePropertymight require an upgrade.
See the compatibilityguideto learn more about limitations and less common issues.
Web frameworks
webapp2 is not bundled or supported in Python 3, so any application needs tobe rewritten to make use of any WSGI-compatible framework (such asFlask).
A recommended migration strategy is to first replace the use ofwebapp2 inyour Python 2.7 app with Flask (or an alternative web framework such asDjango,Pyramid,Bottle, orweb.py), while remaining on Python 2.7.Then, when your updated app is stable, migrate the code to Python 3 anddeploy and test using App Engine for Python 3.
For examples of how to convert Python 2.7 apps which usewebapp2to use the Flask framework, you can refer tothese additional resources.
Using handlers
A Python 3 app can only have one script associated with it, so if yourapp.yaml has multiplescript handlers mapping URLs to different scripts,you will need to combine those scripts into one which handles the URL routing.
The following example shows the handler differences in theapp.yaml filefor the respective runtimes.
Python 2
runtime:python27api_version:1threadsafe:truehandlers:-url:/script:home.app-url:/index\.htmlscript:home.app-url:/stylesheetsstatic_dir:stylesheets-url:/(.*\.(gif|png|jpg))$static_files:static/\1upload:static/.*\.(gif|png|jpg)$-url:/admin/.*script:admin.applogin:admin-url:/.*script:not_found.app
Python 3
runtime:python314app_engine_apis:truehandlers:-url:/stylesheetsstatic_dir:stylesheets-url:/(.*\.(gif|png|jpg))$static_files:static/\1upload:static/.*\.(gif|png|jpg)$-url:/admin/.*script:autologin:adminYour Python 3 app must handle URL routing (for example, withFlask decorators).
If you want to use multiplescript handlers with different URL patterns, orif you want to use other attributes in your handlers, each handler mustspecifyscript: auto.
You can also override the default startup behavior byspecifying anentrypoint fieldin yourapp.yaml file.
See theBlobstore,Deferred, andMailoverviews for more information on how to use specific handlers.
Thread safety
Apps are assumed to be thread safe. API calls must be made on the requestthread. If you use a legacy bundled services API when the app isstarting, this can lead to security errors.
To learn more, seeSecurity errors when using legacy bundled services for Python.
Using URL Fetch
To use URL Fetch for Python, you need to explicitly call theURL Fetch library.
If your Python 3 app uses the URL Fetch API, theX-Appengine-Inbound-Appidrequest header is added when your app sends a request to anotherApp Engine app. This allows the receiving app to verify the callingapp's identity. To learn more, seeMigrating outbound requests.
Example (App Enginendb)
Below is a basic Python 2 app registering page visits using App Enginendb toaccess Datastore. Its companion is a Python 3 equivalent app wherewebapp2usage has been replaced by Flask, and the required changes described above toaccess bundled services in Python 3 have been implemented.
Python 2 (webapp2)
importosimportwebapp2fromgoogle.appengine.extimportndbfromgoogle.appengine.ext.webappimporttemplateclassVisit(ndb.Model):'Visit entity registers visitor IP address & timestamp'visitor=ndb.StringProperty()timestamp=ndb.DateTimeProperty(auto_now_add=True)defstore_visit(remote_addr,user_agent):'create new Visit entity in Datastore'Visit(visitor='{}:{}'.format(remote_addr,user_agent)).put()deffetch_visits(limit):'get most recent visits'returnVisit.query().order(-Visit.timestamp).fetch(limit)classMainHandler(webapp2.RequestHandler):'main application (GET) handler'defget(self):store_visit(self.request.remote_addr,self.request.user_agent)visits=fetch_visits(10)tmpl=os.path.join(os.path.dirname(__file__),'index.html')self.response.out.write(template.render(tmpl,{'visits':visits}))app=webapp2.WSGIApplication([('/',MainHandler),],debug=True)Python 3 (Flask)
fromflaskimportFlask,render_template,requestfromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportndbapp=Flask(__name__)app.wsgi_app=wrap_wsgi_app(app.wsgi_app)classVisit(ndb.Model):'Visit entity registers visitor IP address & timestamp'visitor=ndb.StringProperty()timestamp=ndb.DateTimeProperty(auto_now_add=True)defstore_visit(remote_addr,user_agent):'create new Visit entity in Datastore'Visit(visitor='{}:{}'.format(remote_addr,user_agent)).put()deffetch_visits(limit):'get most recent visits'returnVisit.query().order(-Visit.timestamp).fetch(limit)@app.route('/')defroot():'main application (GET) handler'store_visit(request.remote_addr,request.user_agent)visits=fetch_visits(10)returnrender_template('index.html',visits=visits)Both of these apps can be found in theopen sourcerepo for the PythonApp Engine migration content (code samples,videos,codelabs), specifically in themod0andmod1b folders, respectively.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-12-15 UTC.