Upgrading a Python application to a newer version sounds straightforward. Install the new Python version, update the runtime configuration, and deploy the application.
In practice, the difficult part is often the dependencies.
An application might work perfectly with Python 3.14 but fail after moving to Python 3.15 because one of its packages does not support the new version. A package might install successfully but fail when imported. Another might work locally but fail during deployment because a compatible binary wheel is unavailable.
These problems are especially important when deploying to Google App Engine, where the runtime and deployment environment also affect how dependencies are installed.
In this article, we will check Python package compatibility before upgrading an existing application to Python 3.15. We will use pip, virtual environments, dependency checks, and automated tests to identify problems before changing the production runtime.
Why Package Compatibility Matters
Python applications rarely depend on the standard library alone.
A typical web application might use:
Flask or Django for its web framework.
SQLAlchemy for database operations.
Requests or HTTPX for API communication.
Pydantic for data validation.
Gunicorn for application hosting.
NumPy or Pandas for data processing.
Each package has its own supported Python versions.
When a new Python version is released, package maintainers may need time to update their code, build systems, and native extensions.
For example, a package might declare that it supports Python versions below 3.15. Pip can reject that package during installation even if its code appears compatible.
Native extensions introduce another problem. Packages that contain compiled C or C++ code need compatible wheels or a working build environment.
Therefore, checking whether a package installs is only the first step. You also need to test whether the application works with the installed version.
Create a Separate Python 3.15 Environment
Do not upgrade your existing development environment immediately.
Keep the current Python version available and create a separate virtual environment for Python 3.15.
First, check the installed versions:
python3.14 --version
python3.15 --version
Create a new project environment:
python3.15 -m venv venv315
Activate it on macOS or Linux:
source venv315/bin/activate
On Windows:
venv315\Scripts\activate
Verify that the environment uses Python 3.15:
python --version
Upgrade pip:
python -m pip install --upgrade pip
Using a separate environment allows you to test the new Python version without changing the packages used by your existing application.
Review Your Existing Dependencies
Start by examining the application's dependency files.
A project might contain the following requirements.txt:
Flask==3.1.2
SQLAlchemy==2.0.44
requests==2.32.5
pydantic==2.12.3
gunicorn==23.0.0
These versions are examples. Always check the actual versions and Python support information for your project.
Look for the following:
Packages that have not received updates recently.
Dependencies with restricted Python version requirements.
Packages containing native extensions.
Dependencies installed directly from Git repositories.
Packages that rely on older versions of other libraries.
Also check whether the project uses multiple dependency files, such as requirements-dev.txt, requirements-test.txt, or a pyproject.toml file.
Checking only the production requirements file can leave problems in development and testing dependencies undiscovered.
Check Whether Packages Support Python 3.15
Pip reads package metadata and considers Python version requirements during dependency resolution.
You can ask pip to resolve the dependencies without installing them:
python -m pip install \
--dry-run \
--ignore-installed \
-r requirements.txt
This command attempts to resolve the packages for the active Python 3.15 environment.
It can identify problems such as incompatible Requires-Python declarations and dependency conflicts.
For example, if a package does not support Python 3.15, pip might report that no compatible distribution was found.
However, a successful dry run does not prove that every package will work correctly at runtime. It also does not replace application testing.
Check whether binary wheels are available
Packages with native extensions sometimes need precompiled wheels for each supported Python version and platform.
You can check whether all dependencies have compatible binary distributions:
python -m pip install \
--dry-run \
--ignore-installed \
--only-binary=:all: \
-r requirements.txt
The --only-binary=:all: option prevents pip from building packages from source.
If this command fails, investigate the packages that do not have compatible wheels.
This does not necessarily mean that the package cannot work with Python 3.15. It might support source installation, but the build environment and compiler requirements need additional testing.
Check Installed Dependency Versions
After installing the dependencies in the Python 3.15 environment, run:
python -m pip install -r requirements.txt
Then check the installed packages:
python -m pip list
You can also inspect the package metadata:
python -m pip show Flask
The output includes the installed version and dependency information.
Next, run:
python -m pip check
This command checks whether the installed packages have compatible declared dependencies.
For example, it can identify a situation where one package requires a different version of another package.
Remember that pip check does not test Python 3.15 compatibility or application behavior. It checks dependency consistency in the current environment.
Identify Python 3.15 Compatibility Changes
Even when all packages install successfully, changes in Python itself can affect existing application code.
Python 3.15 includes changes that developers should review when upgrading.
Changes to datetime.strptime
Python 3.15 raises ValueError when a datetime.strptime format contains %d without a year directive.
For example:
from datetime import datetime
date_string = "10 November"
date = datetime.strptime(
date_string,
"%d %B"
)
Code that previously accepted this format can now fail.
If the application expects a year, include it in the input and format:
from datetime import datetime
date_string = "10 November 2026"
date = datetime.strptime(
date_string,
"%d %B %Y"
)
print(date)
This is particularly important for applications that process reports, invoices, scheduling data, and date strings received from external APIs.
Removed deprecated modules
Python 3.15 removes several deprecated modules and APIs.
For example, the following modules are no longer available:
import sre_compile
import sre_constants
import sre_parse
These modules were deprecated and should not be used by application code.
The problem can also exist inside third-party packages. An application might never import these modules directly, but an older dependency could still rely on them.
If you see an import error after upgrading, check the complete traceback to identify which package is responsible.
Changes to NamedTuple construction
Python 3.15 no longer supports the undocumented keyword argument syntax for constructing NamedTuple classes.
For example, older code might contain:
from typing import NamedTuple
Point = NamedTuple(
"Point",
x=int,
y=int
)
Use the class-based syntax instead:
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int
Search your application and dependencies for these patterns if you encounter compatibility errors.
Test the Application, Not Just the Packages
Package installation is not enough to approve a Python upgrade.
You need to test the application using the new interpreter.
For example, a Flask application might have the following tests:
from app import app
def test_home_endpoint():
client = app.test_client()
response = client.get("/")
assert response.status_code == 200
def test_health_endpoint():
client = app.test_client()
response = client.get("/health")
assert response.status_code == 200
Install the testing dependency:
python -m pip install pytest
Run the tests:
python -m pytest
For a database-backed application, test database connections, transaction handling, and query execution.
For an API application, test authentication, request validation, serialization, and error handling.
Also test background jobs, scheduled tasks, and integrations with external services. These areas can expose compatibility problems that ordinary endpoint tests might miss.
Automate Compatibility Checks in CI
Running these checks manually for every dependency update can become difficult.
A CI pipeline can test the application against both the existing Python version and Python 3.15.
For example, the following GitHub Actions workflow uses a Python version matrix:
name: Python Compatibility
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.14", "3.15"]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip install pytest
- name: Check dependencies
run: python -m pip check
- name: Run tests
run: python -m pytest
This is an example workflow. Use action versions approved by your organization and verify that the selected Python versions are available on the CI runner.
The matrix allows the same test suite to run against both Python versions.
If a package works with Python 3.14 but fails with Python 3.15, the CI job helps identify the problem before deployment.
Prepare the Application for Google App Engine
If your application runs on Google App Engine standard environment, check the runtime configuration before deployment.
Your app.yaml should specify:
runtime: python315
Keep your application dependencies in requirements.txt.
After completing the local checks, deploy a separate application version:
gcloud app deploy
App Engine installs the application dependencies during deployment.
Check the deployment logs for package installation errors, missing imports, and application startup failures.
Do not direct production traffic to the new version until you have tested the deployed application.
Also remember that local compatibility does not guarantee deployment compatibility. The App Engine runtime uses its own environment, so test the actual deployed version.
Common Mistakes to Avoid
Updating every package at the same time
Changing Python and upgrading every dependency together makes it difficult to identify the source of a failure.
Keep dependency changes separate from the runtime upgrade where possible.
Assuming pip install means the package is compatible
A package can install successfully and still fail when a particular function or module is used.
Run application tests and check important code paths.
Ignoring development dependencies
Testing and debugging tools can also depend on Python internals. Include them in compatibility checks.
Forgetting native extensions
Packages containing compiled code might need updated wheels or a compatible build environment.
Test them on the same platform and runtime that will be used for deployment.
Upgrading production immediately
Keep the existing App Engine version available while testing the Python 3.15 deployment. Move traffic only after the new version passes your checks.
Summary
Checking package compatibility is an important part of moving an application to Python 3.15. Start by creating a separate virtual environment, reviewing dependency requirements, and using pip to identify installation and dependency conflicts. Then check Python 3.15 changes, test native extensions, run application tests, and automate the process through CI. For Google App Engine, deploy a separate version and verify its behavior before moving production traffic. These steps help identify dependency problems early and make the Python upgrade easier to manage.
Join the conversation! Your thoughts help the community grow.