SevyDevy

Guide · Backend

Python 3.10 End of Life Explained: Upgrade Before the 31 October Lambda Deadline

20 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • Python 3.10 end of life was 1 October 2026. That day the Python project shipped 3.10.22, its final security release, and it will fix nothing more in 3.10.
  • Your code keeps running, but the platforms around it move on. PyTorch 2.15, the next release of the machine-learning library, is due on 28 October with no 3.10 builds. On 31 October, AWS Lambda, Amazon's service that runs functions on demand, deprecates python3.10, the managed Python environment it runs them on.
  • For most teams the target is Python 3.14. As of 19 October 2026, it is the newest version both Lambda and PyTorch fully support, and Python plans to fix it until October 2030.
  • Find every pin, a line that fixes the Python version, test on 3.10 and 3.14 side by side, then move one service at a time with a way back.
  • Beginners can stop after the section on choosing a version. Engineers get three changes tests often miss: warnings hidden by cached compiled files, worker processes that lose the parent's variables, and a moved label. Docker's ready-made image python:3-slim, which tracks the latest Python, now gives you Python 3.15.

What goes wrong when you keep running Python 3.10 after 1 October 2026?

Nothing breaks on the day, but Python 3.10 no longer gets fixes from the Python project. The Python 3.10 end of life came on 1 October 2026, so a security flaw found from now on stays open in the official releases.

The slower damage comes from everything around your code. Platforms stop accepting 3.10, libraries stop shipping new versions for it, and ready-made 3.10 images for containers stop being rebuilt. Each one turns a calm upgrade into a rushed one.

Picture a common setup. A school-fees platform runs three Python 3.10 parts: a Django web app in Docker, a Lambda function that makes PDF receipts, and a nightly PyTorch job. The setup is made up, but the dates and behaviours are real.

Django is a Python web framework. Docker packages an app with the operating-system files it needs into an image. Lambda is the AWS service that runs a function on demand, and PyTorch is a machine-learning library.

The nightly job flags auto-debits that may bounce. PyTorch 2.15 is due on 28 October and Lambda's deadline is 31 October, so the team has under two weeks. This guide follows their move off 3.10.

Think of a bus model the maker no longer services. The bus is your 3.10 program, the maker is the Python project and the garage is its security fixes. The depots that stop taking the old model are Lambda, PyTorch and image builders.

Where the picture breaks: a bus wears out on its own. Code does not wear out. It only becomes riskier as new flaws are found and the tools around it move on.

Which words do you need before planning a Python upgrade?

You need seven words: feature version, patch release, bugfix status, security status, end of life, wheel and runtime.

  • Feature version: the first two numbers, such as 3.10 or 3.14. A new one ships about once a year and can add or remove features.
  • Patch release: the third number, such as 3.10.22. It carries only fixes, so moving between patch releases of one feature version is usually safe.
  • Bugfix status: the first two years of a version (18 months for versions before 3.13). It gets bug and security fixes, with new installers roughly every two months.
  • Security status: the years after that. Only security fixes are accepted, and the project publishes source code but no new installers.
  • End of life (EOL): the point when the project freezes a version and accepts no more changes. For Python, that is five years after its first release.
  • Wheel: a ready-built package file that pip, Python's package installer, installs. A wheel tagged cp310-cp310 is built for CPython 3.10 only (CPython is the standard Python), so pip skips it on 3.14.
  • Runtime (in Lambda): the managed language environment AWS runs your function on, named like python3.10 or python3.14.

When was Python 3.10 end of life, and what does it mean?

Python 3.10 end of life was 1 October 2026. That day the project released 3.10.22 and froze the 3.10 branch, so no further changes, including security fixes, will be made.

Python 3.10.0 first shipped on 4 October 2021. It got regular bugfix releases with installers until 3.10.11 in April 2023, then source-only security releases until its five years were up.

The final release listed 11 fixes, eight of them with CVEs, public IDs for known security flaws. They touch modules such as ssl, tarfile and zipfile. The next flaw found there gets no official 3.10 fix.

Python support dates and AWS Lambda runtime deprecation dates, as of 19 October 2026
VersionPython statusFirst releasePython end of lifeLambda deprecation
3.10End of life4 October 20211 October 202631 October 2026
3.11Security24 October 2022October 2027 (planned)30 June 2027
3.12Security2 October 2023October 2028 (planned)31 October 2028
3.13Security7 October 2024October 2029 (planned)30 June 2029
3.14Bugfix7 October 2025October 2030 (planned)30 June 2029
3.15Bugfix9 October 2026October 2031 (planned)Public preview, not scheduled

A team on 3.10.14 has also missed eight patch releases, 3.10.15 to 3.10.22. Only an upgrade closes that gap now.

This is about the Python project's own releases. A Linux distribution that packages 3.10 patches its copy on its own schedule. Ubuntu 22.04, for example, has backported fixes such as CVE-2023-40217 to its python3.10 package.

When do AWS Lambda and PyTorch drop Python 3.10 support?

PyTorch 2.15 is due on 28 October 2026 with no Python 3.10 builds, and AWS Lambda deprecates its python3.10 runtime on 31 October 2026. Neither stops a running program, but each makes staying harder.

Upcoming dates for teams still on Python 3.10, as of 19 October 2026
DateWhat happensWhat the team does
28 October 2026PyTorch 2.15 is due, with no Python 3.10 wheelsKeep the nightly job on PyTorch 2.14 until it moves
31 October 2026Lambda deprecates python3.10; console create and update stopMove the receipt function to python3.14 first
29 July 2027Lambda blocks creating new python3.10 functionsNothing, if the move is done
31 August 2027Lambda blocks updating python3.10 functionsNothing, if the move is done

AWS Lambda deprecates its python3.10 runtime on 31 October 2026

After deprecation, AWS may stop applying security patches to the runtime. Functions on it are also no longer eligible for technical support.

From that date the Lambda console no longer lets you create or update python3.10 functions. The AWS CLI, AWS SAM and CloudFormation, tools that deploy from commands or template files, keep working until the block dates.

AWS says it will not block creates or updates before the dates in its runtime table. It also says you can keep invoking a deprecated function indefinitely.

AWS warns you by email and in its Health Dashboard and Trusted Advisor tools. Those warnings list only $LATEST, the editable working copy of each function, not older published versions.

To find every version on python3.10, AWS documents this command. Run it once per region and per account.

bash
aws lambda list-functions --function-version ALL --region ap-south-1 --output text --query "Functions[?Runtime=='python3.10'].FunctionArn"

It prints the ARN, the full AWS name, of each python3.10 version, with tabs between them. An empty result means none are left in that region. We checked the filter and the text output on sample data with the AWS CLI's own formatter, awscli 1.46.1.

PyTorch 2.15 drops Python 3.10 wheels on 28 October 2026

The PyTorch notice of 16 September 2026 makes 2.14 the last release with Python 3.10 wheels. From 2.15, the minimum is Python 3.11, and builds cover 3.11 to 3.15.

PyPI, the public package index pip installs from, shows that torch 2.14.1 still has cp310 wheels. Released 2.14 files stay available, but the notice calls staying on 2.14 a stopgap, with no further bug fixes, security fixes or features.

Libraries and base images move on too

Each library sets its own policy. When a release declares a minimum Python version, pip on 3.10 skips it and installs the newest release that still fits.

You can see this with pip's dry run, which resolves versions without installing anything. The --no-deps flag leaves out each library's own dependencies, so the output stays short. On 19 October 2026, pip install --dry-run --no-deps django numpy printed "Would install Django-5.2.18 numpy-2.2.6" on 3.10. On 3.14 it printed "Would install Django-6.1.2 numpy-2.5.4".

Not every old wheel is stuck. A wheel tagged abi3 uses the stable ABI (application binary interface), which stays fixed across versions, so a cp310-abi3 wheel still installs on 3.14. Pip's own tag check on 3.14.6 accepts cp310-abi3 and rejects cp310-cp310.

Base images follow the same pattern. As of 19 October 2026, the official python image on Docker Hub lists no 3.10 tag among its supported tags. The python:3-slim tag now points at 3.15.0, so a Dockerfile that says FROM python:3-slim builds on 3.15 today.

Which Python version should you upgrade to in 2026?

Upgrade to Python 3.14 in most cases. It is in bugfix status, so it still gets installers and bug fixes, and Python plans to support it until October 2030.

It also fits the platforms. Lambda supports python3.14 until 30 June 2029, and the PyTorch notice lists 3.14 for 2.15. Python 3.13 gets the same Lambda date but one year less from Python.

Python 3.11 is the smallest step, but Python support ends in October 2027 and Lambda deprecates it on 30 June 2027. On 19 October 2026, Python 3.15 is ten days old, and Lambda offers it only as a public preview.

Choosing a target Python version in October 2026

Follow the arrows: the question that decides most cases is whether every dependency already supports 3.14. Python 3.15 is for testing until Lambda and your libraries support it fully.

Diagram connections:

  • Project on Python 3.10 to All libraries on 3.14? (Check dependencies)
  • All libraries on 3.14? to Upgrade to 3.14 (Yes)
  • All libraries on 3.14? to Upgrade to 3.13 for now (No, one blocks it)
  • Upgrade to 3.14 to Try 3.15 in tests only (Next upgrade, later)
  • Upgrade to 3.13 for now to Upgrade to 3.14 (When the blocker moves)
  • Project on Python 3.10: End of life since 1 October 2026
  • All libraries on 3.14?: Run pip install --dry-run on 3.14
  • Upgrade to 3.14: Python to October 2030, Lambda to June 2029
  • Upgrade to 3.13 for now: Python to October 2029, Lambda to June 2029
  • Try 3.15 in tests only: Lambda python3.15 is a public preview

How do you find where your project pins Python 3.10?

Search the repository for files that name a Python version. Each such line is a pin. In the team's repo, pins sit in the Dockerfile, the Lambda template, packaging files, tox settings, a CI workflow and a .python-version file. CI, short for continuous integration, runs your tests on every change, and tox runs them under several Python versions.

This script scans those files and skips virtual environments, the private package folders each project keeps. It ran unchanged on Python 3.10.20 and 3.14.6.

python
import re
from pathlib import Path

REPO = Path(".")  # run from the repository root
SKIP = {".git", ".venv", "venv", "node_modules"}
NAMES = {"Dockerfile", "pyproject.toml", "setup.py", "setup.cfg", "tox.ini", ".python-version", "runtime.txt"}
# python3.10, python:3.10, python-3.10, python-version: 3.10, "3.10", py310,
# a line starting 3.10 (case ignored)
PIN = re.compile(r"python[-:]?3\.10\b|python[\w-]*: *3\.10\b|python :: 3\.10\b|['\"]3\.10['\"]|requires.*3\.10\b|^3\.10\b|py310", re.I)

hits = 0
for path in sorted(REPO.rglob("*")):
    if SKIP & set(path.parts) or not path.is_file():
        continue
    if path.name not in NAMES and path.suffix not in {".yml", ".yaml"}:
        continue
    for n, line in enumerate(path.read_text(errors="ignore").splitlines(), 1):
        if PIN.search(line.strip()):
            hits += 1
            print(f"{path}:{n}: {line.strip()}")
print(hits, "lines name Python 3.10")
raise SystemExit(1 if hits else 0)  # a hit fails the CI step

On the team's repo it prints seven lines, from FROM python:3.10-slim in the Dockerfile to Runtime: python3.10 in template.yaml. The last line reads "7 lines name Python 3.10".

After the upgrade, the same script prints "0 lines name Python 3.10". Keep it as a CI step: it exits with code 1 when it finds a pin, which fails the build, and with 0 when it finds none.

What changes when you upgrade Python 3.10 to 3.14?

Most code runs unchanged, but four feature versions of removals arrive at once. Read the What's New pages for 3.11 to 3.14, then run the checks below. Each one ran on 3.10.20, 3.11.17, 3.12.3, 3.13.16 and 3.14.6.

Removed modules fail at import time

  • 3.11: removes binhex and deprecates many old modules, including cgi and imghdr. Importing them raises a DeprecationWarning, which Python hides by default inside imported modules.
  • 3.12: removes distutils, imp, asynchat, asyncore and smtpd. New virtual environments made with python -m venv no longer include setuptools, the classic packaging library. Install it yourself if a tool needs it.
  • 3.13: removes 19 old modules, including cgi, crypt, imghdr, pipes and telnetlib. The 2to3 tool and lib2to3 go too.

In total, 26 public standard-library modules that 3.10 has are gone in 3.14. This script lists every import of one of them, without running your code.

python
import ast
from pathlib import Path

# Standard-library modules in Python 3.10 that are gone by 3.14
REMOVED = set("""aifc asynchat asyncore audioop binhex cgi cgitb chunk crypt
distutils imghdr imp lib2to3 mailcap msilib nis nntplib ossaudiodev pipes
smtpd sndhdr spwd sunau telnetlib uu xdrlib""".split())
SKIP = {".git", ".venv", "venv", "node_modules"}

for path in sorted(Path(".").rglob("*.py")):
    if SKIP & set(path.parts):
        continue
    for node in ast.walk(ast.parse(path.read_bytes(), str(path))):
        if isinstance(node, ast.Import):
            names = [a.name for a in node.names]
        elif isinstance(node, ast.ImportFrom) and node.level == 0:
            names = [node.module or ""]
        else:
            continue
        for name in names:
            if name.split(".")[0] in REMOVED:
                print(f"{path}:{node.lineno}: imports {name}")

In the team's repo it prints two lines: the fee report imports cgi and the logo upload helper imports imghdr. Both need rewrites before 3.14.

One trap: from 3.12, import distutils still works wherever setuptools is installed, because setuptools ships its own copy. A machine with setuptools hides the removal, so test in a clean virtual environment.

Escape-sequence warnings hide in cached compiled files

A string such as "\d+" has an invalid escape sequence. From 3.12 Python emits a SyntaxWarning for it, shown by default. On 3.10 it was a DeprecationWarning that stayed silent in our tests.

The catch is that Python warns only when it compiles a file. Python caches compiled modules as .pyc files, so the next import of the same module prints nothing.

The flag -W error::DeprecationWarning does not catch it on 3.12 or later, because the warning class changed. Force a fresh compile with every warning as an error instead:

bash
python3.14 -W error -m compileall -f -q src

Here src is your code folder. For each file with a bad string, it prints an "Error compiling" message with the line, then exits with code 1. Each run shows only the first bad string in each file. Fix them with raw strings, such as r"\d+", and re-run until it exits with 0.

Worker processes no longer copy the parent on Linux

On Linux and other Unix systems except macOS, 3.14 starts the workers of ProcessPoolExecutor, Python's built-in pool of worker processes, with forkserver instead of fork. Fork copies the running program. Forkserver starts a clean helper process, and each worker is a copy of that helper. Plain multiprocessing pools get the same new default.

Pause and predict: the nightly job loads its model into a global variable, then hands work to a pool. What does each worker see on 3.14?

python
import sys
from concurrent.futures import ProcessPoolExecutor

MODEL = None              # the parent fills this in at run time

def score(debit):
    return "no model" if MODEL is None else f"scored {debit}"

if __name__ == "__main__":
    MODEL = "weights"     # stands in for loading a real model
    with ProcessPoolExecutor(max_workers=2) as pool:
        print(sys.version.split()[0], list(pool.map(score, [101, 102])))

On 3.10.20 and 3.13.16 the printed list is ['scored 101', 'scored 102']. On 3.14.6 it is ['no model', 'no model'], with no error at all.

Under forkserver, the helper imports your main module without running the main guard, the if __name__ == "__main__": block. Workers copy the helper, not the parent, so a value set under the main guard never reaches them. A function defined inside the main guard fails louder: workers raise AttributeError and the pool raises BrokenProcessPool, so define such functions at module level. Load shared state in each worker with the pool's initializer argument, or request fork explicitly with mp_context.

Annotations are now evaluated only when read

Annotations are the type hints on functions and classes. From 3.14 they are evaluated only when something reads them, unless the module uses from __future__ import annotations. A hint can now name a class defined later without quotes.

Reading the annotations before that class exists still raises a NameError. Python's own notes say most code keeps working as it is.

How do you upgrade Python 3.10 safely, step by step?

Test both versions side by side, fix what the checks find, then switch one service at a time with a way back. Here is the team's plan.

  1. List every pin

    Run the pin scanner and record each line that names 3.10, with an owner and a date.

  2. Test on both versions

    Add 3.14 to the CI matrix next to 3.10. Tools such as tox or nox run your tests under several Python versions.

  3. Upgrade dependencies

    Move Django and the PDF library to releases that support 3.14, one at a time, testing after each.

  4. Fix removals and warnings

    Rewrite the cgi and imghdr code, fix each compileall error and check every process pool that relies on globals.

  5. Move the Lambda function first

    Publish the receipt function on python3.14 as a new version, test it, then point the live alias, the name callers use, at it before 31 October.

  6. Roll out the web app

    Build the Docker image on python:3.14-slim, not python:3-slim, and send a small share of traffic to it first.

  7. Move the PyTorch job

    Move the job to 3.14 on the same PyTorch 2.14.1, which has 3.14 wheels, and compare its flags with the old run. Upgrade PyTorch later, separately.

  8. Drop 3.10 everywhere

    Remove 3.10 from CI and packaging files, then run the scanner until it prints zero lines.

The Lambda move comes first because its date is closest. A Lambda alias is a named pointer to a published version that you can update, so rolling back means pointing it at the old version.

Moving the receipt function to python3.14 with a way back

Notice that callers only ever use the alias name. The alias moves to version 8 only after tests pass, and pointing it back at version 7 is the rollback.

Diagram connections:

  • Version 7 on python3.10 to Version 8 on python3.14 (Copy and change runtime)
  • Version 8 on python3.14 to Test with sample payments (Invoke version 8 directly)
  • Test with sample payments to Alias live (Tests pass)
  • Alias live to Watch errors for a day (Live traffic on version 8)
  • Version 7 on python3.10: The current live receipt function
  • Version 8 on python3.14: Same code, new runtime, published by SAM
  • Test with sample payments: Compare 200 PDF receipts with version 7
  • Alias live: The payments service calls this name
  • Watch errors for a day: Point live back to version 7 if errors rise

The rollback is one documented command, with your own function and alias names:

bash
aws lambda update-alias --function-name receipt-maker --name live --function-version 7

One more change hides in the runtime table. Lambda's python3.10 runs on Amazon Linux 2, while python3.14 runs on Amazon Linux 2023. Rebuild and test any layer or package that ships compiled code.

To confirm each move, run python3 --version inside the new image. On python:3.14-slim it prints Python 3.14.8 as of 19 October 2026. For Lambda, rerun the list-functions command and expect no output.

The team's test run takes 40 minutes (made up), and the receipts match. A week later the web app and the nightly job follow, and the scanner prints zero lines.

How do you explain a Python version upgrade in a system design interview?

Show that you treat a language upgrade as a planned change with dates, tests and a way back. Use this frame.

  1. State the deadline and the risk

    Python 3.10 stopped getting fixes on 1 October 2026, and platforms such as Lambda and PyTorch are dropping it.

  2. Find the blast radius

    List every service, image, function and job that pins the old version, and who owns each.

  3. Pick the target

    Choose the newest version your platforms and libraries fully support, 3.14 as of October 2026, for the longest runway.

  4. Test both versions together

    Run the suite on old and new versions in CI, plus checks for silent changes such as process start methods.

  5. Roll out gradually with a way back

    Move one service at a time, start with the nearest deadline and keep a rollback such as a Lambda alias.

Questions people ask

Will my Python 3.10 code stop working after 1 October 2026?
No, it keeps running exactly as before. End of life means the Python project makes no more changes to 3.10, including security fixes. Any flaw found from now on stays open in the official releases, and libraries and platforms gradually stop supporting 3.10.
Can I upgrade straight from Python 3.10 to 3.14?
Yes. You do not need to install 3.11, 3.12 and 3.13 along the way. Read the What's New page for each version in between, because removals from all four arrive at once. Test both versions in CI before switching.
Should I upgrade to Python 3.15 instead of 3.14?
Not for production yet, as of 19 October 2026. Python 3.15.0 came out on 9 October 2026, and AWS Lambda offers python3.15 only as a public preview. Your libraries may also need time. Test on 3.15, ship on 3.14 and plan a smaller move later.
What happens to my Lambda function after 31 October 2026?
It keeps running when invoked. AWS may stop patching the runtime, technical support ends and the console no longer lets you create or update python3.10 functions. The CLI, SAM and CloudFormation still work until the block dates in July and August 2027.
Is Python 3.11 a good enough upgrade target?
It works, but it buys little time. Python plans to support 3.11 until October 2027, and Lambda deprecates python3.11 on 30 June 2027. You would repeat this project within a year. Python 3.14 is planned until October 2030, and Lambda supports it until 30 June 2029.
How do I know if a library has dropped Python 3.10?
Run pip install --dry-run with the library name on both versions and compare the versions pip would install. A release that declares it needs a newer Python is skipped on 3.10. The release notes or the package page on PyPI then tell you why.

Sources and further reading

Practise this with Lala

Practise