Teams on an end-of-life Python
Applications still running on a Python version that no longer receives fixes.

Python codebases age quietly: an end-of-life interpreter, a requirements file nobody trusts, no type checks and a worker setup tuned years ago. Agnotic Technologies upgrades Python applications to supported versions, modernizes packaging and typing, and fixes concurrency and service structure, with tests guarding each step.
Trusted by global innovators
























Python consulting
Python consulting is senior engineering help for teams whose Python applications have become slow, fragile or hard to upgrade. Agnotic Technologies reviews the codebase, moves it off end-of-life Python versions, sets up pyproject.toml packaging with lock files, adds type checking gradually and chooses the right concurrency model for each workload.
Our engineers do the work with your team instead of only writing a report. Hiring individual Python developers is a separate service on our Python developers page.
Building and training machine learning models sits with our ML development services. Here we look after the application code around them: APIs, workers, pipelines and packaging.
Python standards
The Python Enhancement Proposals we apply most on upgrade work, and what each changes in your repository.
| Code | Name | Typical use |
|---|---|---|
| PEP 621 | Project metadata in pyproject.toml | One [project] table replaces setup.py metadata, so every build tool reads the same name, version and dependencies |
| PEP 751 | Standard lock file (pylock.toml) | Records the exact dependency versions to install, so CI and production build the same environment |
| PEP 484 | Type hints | Annotations that mypy or pyright check in CI, added first to the modules that change most |
| PEP 703 | Making the GIL optional in CPython | A free-threaded build in which threads can run Python code in parallel on several cores |
| PEP 779 | Supported status for free-threaded Python | Made the free-threaded build officially supported but optional, from Python 3.14 |
| PEP 8 | Style guide for Python code | Formatting and naming enforced by a formatter and linter, so reviews focus on behavior |
Decision table
Our starting point for each workload type. Profiling decides the final answer.
| Workload type | Approach we usually choose | What to measure first |
|---|---|---|
| I/O-bound API with many requests waiting on databases or other services | asyncio with an async framework such as FastAPI, or threads in a synchronous framework | Time spent waiting on I/O per request, connection pool use |
| CPU-bound batch job, such as report generation or file processing | Several processes, or the free-threaded build once your libraries support it | CPU per core, time per item, memory per worker |
| Data pipeline over large files or tables | Vectorized libraries and chunked processing, with processes for parallel stages | Rows per second per stage, memory peak, time in Python versus native code |
| ML inference service | A process per model worker, with request batching where latency allows | Latency percentiles, model load time, memory per worker |
| Background jobs and queues | A task queue with worker processes sized to the job mix | Queue depth, job duration, retry counts |
Measure before changing the model. A profile often shows the time goes to a slow query or a serial loop, not to Python's concurrency limits.
Who it is for
Teams with Python in production and a problem they cannot schedule around.
Applications still running on a Python version that no longer receives fixes.
Batch and ETL code that takes longer every month as data grows.
Django or FastAPI services that slow down under load or block on I/O.
Teams taking over code from a vendor or an acquired company.
Models that moved on while the serving, packaging and deployment code stayed behind.
Upgrade path
Five steps that keep the application releasable the whole way through.
We list the interpreter, every direct and indirect dependency and the newest version of each that supports your target Python.
Characterization tests capture current behavior on the paths the upgrade touches, before anything changes.
Metadata moves to pyproject.toml and dependencies are locked, so the upgrade changes one thing at a time.
We step through Python versions and major dependency versions, fixing deprecations at each step with the test suite green.
CI builds on the old and new versions until the new one has run in production without regressions.
Failure points
Problems we plan for on every upgrade, and how we handle them.
Challenge
A pinned dependency has no release for the new Python version.
Agnotic approach
We find a maintained replacement or fork, or isolate the dependency behind an interface so it can be swapped later.
Challenge
Deprecation warnings were ignored for years and are now errors.
Agnotic approach
Warnings become test failures one module at a time, so they are fixed in a planned order.
Challenge
Builds differ between laptops, CI and production.
Agnotic approach
One lock file drives every environment, and the container image is built from it.
Challenge
Type checking the whole codebase at once produces thousands of errors.
Agnotic approach
Strict checking starts on new and changed modules and widens release by release.
Challenge
An async rewrite made the service slower.
Agnotic approach
One blocking call in an async path stalls every request; we find these by profiling and move them to threads or processes.
Tools and frameworks
The tools we use most on Python upgrade and architecture work.
Version status and standards behind this page, each linked to its primary source.
Python 3.10 reached end of life on 1 October 2026, and Python 3.11 now accepts security fixes only until its scheduled end of life in October 2027.
Source: Python Developer's Guide: status of Python versionsPEP 621 specifies how a project's core metadata is written in pyproject.toml, so packaging tools and build back ends read it in one tool-agnostic way.
Source: PEP 621 (peps.python.org)PEP 751 defines a standard lock file format, pylock.toml, that records the direct and indirect dependencies to install, so installers can reproduce an environment without resolving dependencies at install time.
Source: PEP 751 (peps.python.org)PEP 484 introduced the standard vocabulary for type hints and states that Python will remain a dynamically typed language, with no wish to make type hints mandatory, even by convention.
Source: PEP 484 (peps.python.org)PEP 779, accepted in June 2025 for Python 3.14, sets the criteria for phase II of free-threaded Python, in which the build without the global interpreter lock is officially supported but still optional.
Source: PEP 779 (peps.python.org)Scope and cost
We price Python work after looking at the code. These factors move the estimate most.
How much code there is and how far the current Python version is from a supported one.
Whether tests already guard the paths an upgrade touches, or need writing first.
How many dependencies are unmaintained, pinned to old versions or lack support for newer Python.
How many applications, workers and environments share the codebase.
Major Django, FastAPI or Celery versions that move with the interpreter.
Our engineers, your team guided by us, or both working together.