Python 3.15 has been released on October 1, 2026. The Python 3.15 new features list centers on explicit lazy imports, new frozendict and sentinel built-in types, UTF-8 as the default encoding, unpacking in comprehensions, and a sampling profiler named Tachyon. The JIT compiler also gets a major upgrade. Most changes are opt-in. Most apps can move over with small fixes.
Table of Contents
Introduction
Python 3.15 is here with updates that go beyond new syntax. The latest release focuses on how Python starts, runs, and handles modern application workloads, with changes such as lazy imports, a more capable JIT compiler, UTF-8 by default, and improved free-threading support.
These updates can influence everything from application startup and runtime performance to debugging and compatibility with native extensions.
This blog covers major Python 3.15 features in the release, what was removed or changed, how 3.15 compares with 3.14, and a step-by-step upgrade plan with a checklist. By the end, you will know which changes to adopt first and which can wait.
What's New in Python 3.15? Latest Features and Updates
Explicit Lazy Imports
Normally, when Python reaches an import line, it loads that module right away. If your file imports 30 libraries, Python loads all 30 before your code starts. That is why some apps feel slow to start.
A lazy import changes the timing. Python waits and loads the module only when your code first uses it. You turn this on with the new lazy keyword.
lazy import json
lazy from pathlib import Path
print("Starting up...") # json and pathlib are not loaded yet
data = json.loads('{"key": "value"}') # json loads here
p = Path(".") # pathlib loads here
For instance, if you run a command-line tool with –help. That command only prints text, and it does not need your data libraries. With lazy imports, those libraries never load, and the help text appears instantly.
Before this, developers moved imports inside functions to get the same effect. That worked but made code harder to read. Lazy imports let you keep everything at the top of the file.
You have four ways to control it:
The lazy keyword works with both import and from … import. You can use it on single imports.
-X lazy_imports or PYTHON_LAZY_IMPORTS sets the mode for the whole app. The value all makes every import lazy. The value normal is the default and follows the keyword only.
sys.set_lazy_imports_filter() lets you make only your own modules lazy. Third-party imports stay eager.
__lazy_modules__ lets a module opt in without the new syntax. This helps code that must also run on older Python versions.
3 things to keep in mind:
Side effects happen later. Some modules do work when imported, such as registering plugins. With a lazy import, that work moves to the first use. Test those paths.
lazy works only at module level. Inside a function, class, or try block, it raises a SyntaxError. Star imports and future imports cannot be lazy either.
Errors show up later. If a lazy import fails, you see the error at the first use. The traceback shows both places.2
2. Upgraded JIT Compiler and Interpreter Gains
A JIT (just-in-time) compiler watches your program while it runs. When it spots code that runs often, it converts that code into faster machine code. Python has had an experimental JIT since version 3.13. Python 3.15 upgrades it in 3 ways:
1. It can now handle far more kinds of bytecode and control flow. More of your code can benefit.
2. Basic register allocation can keep values in CPU registers and skip some slower stack operations.
3. More optimizations are required when the JIT can tell a value is constant, it simplifies the work.
Note: The official What’s New page reports a 7 to 8% average gain on x86-64 Linux, compared with a fully optimized standard interpreter build. The release notice reports 8 to 9% on x86-64 Linux and 12 to 13% on AArch64 macOS, compared with the tail-calling interpreter.
Those are averages. Individual benchmarks range from about 15 percent slower to over 100 percent faster. So your results depend on your code. The JIT is also still experimental and opt-in, so you can safely try it on staging.
2 more runtime changes:
Tail-calling interpreter on Windows. The official Windows 64-bit binaries now use this faster interpreter design.
Frame pointers on by default (PEP 831). Frame pointers help system-level tools read Python’s call stack. Profilers and eBPF tools can now do this faster and more reliably.
3. Tachyon Sampling Profiler and the profiling Package (PEP 799)
A profiler tells you where your program spends its time. There are 2 common types:
A tracing profiler records every function call, which is precise but slow
A sampling profiler checks regularly what the program is doing, which is lighter.
Python 3.15 adds a new profiling package with both types:
profiling.tracing is the tracing profiler that used to live in cProfile. cProfile stays as an alias, so old code keeps working.
profiling.sampling is Tachyon, the new high-frequency sampling profiler.
Tachyon can attach to a running process without code changes or a restart. That is useful when a production service is slow, and you cannot stop it. Starting it takes one line:
python -m profiling.sampling run my_script.py
What Tachyon can do:
Target modes: Run a script, run a module, attach to a live process by PID, or dump the stacks of a hung process.
Profiling modes: Wall-clock time, CPU time, GIL-holding time, and exception-handling time.
Output formats: Flame graph, heatmap, pstats, collapsed stacks, and Gecko format for Firefox Profiler.
Live mode: A top-style terminal view, plus support for async code.
Expert Note: The old profile module is deprecated and will be removed in Python 3.17.
4. UTF-8 as the Default Encoding
An encoding tells Python how to turn bytes in a file into text. Until now, if you opened a file without naming an encoding, Python used your operating system’s setting. That setting differs between machines. So the same code could read a file correctly on one computer and fail on another.
Python 3.15 removes this problem. Calling open(‘notes.txt’) now reads the file as UTF-8 on every platform.
When could this cause trouble? If your code reads old files saved in another encoding and never named it. In that case, specify the encoding explicitly:
with open('legacy_notes.txt', encoding='cp1252') as f:
text = f.read()
2 helpful options:
To keep the old behavior for now, set PYTHONUTF8=0 or use -X utf8=0.
To find risky code, run with -X warn_default_encoding. Python warns you about every file read that skips the encoding.
Unpacking in Comprehensions
Unpacking means spreading the items of a list or dict into a new one, using * or **. Until now, you could not use it inside a comprehension. Python 3.15 features allows it in list, set, and dict comprehensions, and in generator expressions. It also works in async generator expressions.
The most common use is flattening a list of lists:
lists = [[1, 2], [3, 4], [5]]
flat = [*items for items in lists]
# [1, 2, 3, 4, 5]
You can also merge dictionaries:
dicts = [{"a": 1}, {"b": 2}]
merged = {**d for d in dicts}
# {"a": 1, "b": 2}
Before, you needed a nested comprehension or itertools.chain for this. The new form is shorter and easier to read.
5. New Built-in Types: frozendict and sentinel
frozendict (PEP 814) is a dictionary that cannot change after you create it. Python already has tuple for lists and frozenset for sets. Now dictionaries have a frozen version too.
a = frozendict(x=1, y=2)
b = frozendict(y=2, x=1)
hash(a) == hash(b) # True
It keeps insertion order, but comparison ignores order.
It is hashable when all its keys and values are hashable. That means you can use it as a dict key or put it in a set.
It is not a subclass of dict. If your code checks isinstance(x, dict), it may need an update.
sentinel (PEP 661) creates a unique marker value. You can use markers when None is a valid input, and you need another way to say no value given. The old trick was object(). It works, but it prints as something unreadable. A sentinel prints with a clear name.
When you debug, you now see MISSING instead of an anonymous object.
6. Typing Improvements
Python 3.15 features have 3 typings and most of them help teams that use type checkers.
TypeForm (PEP 747) lets you annotate a value that is itself a type expression. For instance, a function that accepts a type as an argument.
TypedDict with typed extra items (PEP 728) adds the closed and extra_items class arguments. You can state exactly which keys are allowed. It helps when you validate API payloads.
Disjoint bases (PEP 800) add @typing.disjoint_base. It is an advanced tool aimed mainly at type checker authors.
from typing import TypedDict
class Movie(TypedDict, closed=True):
name: str
year: int
7. Package Startup Configuration Files
Some packages run code at startup by placing import lines in .pth files. Python 3.15 adds a cleaner option: package startup configuration files, saved as .start files. The old import lines in .pth files are now quietly deprecated. If you maintain a package that uses them, plan the move.
C API and Stable ABI Changes
These updates matter if you write or maintain C extensions.
Stable ABI for free-threaded builds: The new abi3t stable ABI covers free-threaded builds. It comes with a new export hook, PyModExport_*, and a PySlot structure. These replace the older PyInit_ pattern.
PyBytesWriter: A new C API for building Python bytes objects.
Finalization protection: The C API gains protection against finalization.
On the free-threading question: the standard Python build still has the GIL. The free-threaded build stays separate and opt-in.
Better Error Messages and Color Output
Python 3.15 gives clearer error messages. It can suggest nested attributes, such as .inner.area instead of .area. It can also recognize method names from other languages. If you call .push() on a list, Python suggests .append. The interpreter output also uses more color, which makes it easier to scan.
Need help with upgrading to Python 3.15?
Hire Python developers to assess your current stack, handle migration, validate dependencies, and prepare your application for the new release.
What's Removed or Changed in Python 3.15?
Most changes will not touch your code, but 8 areas deserve a quick check before you upgrade. The official What’s New page lists every removal and deprecation.
1. Default encoding: Code that relied on the locale default for file reads may behave differently. 2. __cached__ on modules: It is no longer set. Use __spec__.cached instead. 3. array.typecodes: It changed from a str to a tuple. 4. importlib.metadata: A metadata folder with no metadata file now raises MetadataNotFound. Before, it returned an empty object. 5. .pth files: import lines in .pth files are quietly deprecated. The new .start files (PEP 829) replace them. 6. tarfile: Several security fixes tighten how archives extract. Re-test any code that unpacks untrusted archives. 7. C extensions: Compiled packages need 3.15 wheels. Common build tools do not support abi3t yet, so build cp315t wheels separately for now. 8. Older Python versions: Python 3.9 is already end-of-life. Python 3.10 reaches end-of-life in October 2026. Teams on those versions face a bigger jump.
A simple habit helps here. Run your tests with warnings turned on in Python 3.14. Most of these issues show up as warnings before they become errors.
How Does Python 3.15 Compare With Python 3.14?
Here is a side-by-side view of the main differences between Python 3.15 vs 3.14.
Aspect
Python 3.15
Python 3.14
Imports
Explicit lazy imports
Eager only
JIT
Upgraded due to new tracing frontend and register allocation
Early experimental JIT
Profiling
Profiling package with Tachyon sampling profiler
cProfile only
Default Encoding
UTF-8
Locale-based
Built-in Types
frozendict and sentinel added
Not Available
Comprehensions
* and ** unpacking allowed
Invalid
Native Stack Unwinding
Frame pointers on by default
Not on by default
Free-threaded C extensions
abi3tstable ABI option
Version-specific builds only
How Do You Upgrade to Python 3.15?
The upgrade is safe if you go in order. The two biggest risks are third-party packages and file encoding, so the steps below check both early.
Step 1: Check your current version
python --version
This shows how big your jump is and which removals apply to you.
Step 2: Create a fresh virtual environment on 3.15
A clean environment stops old packages from hiding problems.
Step 3: Reinstall your dependencies
pip install -r requirements.txt
Watch for packages that fail to build. Those need a newer release before you can continue.
Step 4: Run your tests with warnings on
python -W error -m pytest
Fix deprecation warnings first, because they point to removed APIs. Also run with -X warn_default_encoding to find file reads that need an explicit encoding=.
Step 5: Benchmark, then test lazy imports and the JIT
Measure your startup time before and after. Try lazy import on your heaviest modules, and confirm nothing depends on import side effects. Then test the JIT with PYTHON_JIT=1 on staging and compare the results with your baseline.
A note on developer tools: Update debugpy and your editor’s Python debugger extension before you move to 3.15. Check the debugpy release notes for confirmed 3.15 support. Test attach-to-process on staging first.
Pre-Upgrade Checklist You Need to Follow Before Implementing New Python Version
All third-party packages have 3.15-compatible releases
C extensions and wheels are tested on 3.15
File reads that rely on locale encoding now set encoding= explicitly
Code that checks isinstance(x, dict) is reviewed for frozendict
Deprecation warnings from 3.14 are cleared
Packages that use .pth import lines are reviewed
debugpy and other dev tools are updated
CI pipeline runs on 3.15
Startup time and key endpoints are benchmarked
Docker base images are updated
Staging is tested end to end before production
Conclusion
Python 3.15 features give developers more room to rethink how Python apps are structured, loaded, profiled, and maintained. The bigger opportunities lie in applying these changes where they can improve startup behavior, simplify application design, or make performance analysis more efficient.
For businesses, upgrading should come down to more than adopting the latest version. It becomes worth assessing your current architecture, dependencies, workloads, and long-term development plans before making the move. If you are weighing where Python fits in your roadmap, see why businesses choose Python for app development.
If you’re planning a new Python application or modernizing an existing one, Bacancy Technology can help you evaluate where Python 3.15 fits into your project. As an experienced Python development company, we can support everything from architecture and development to modernization and migration.
Frequently Asked Questions (FAQs)
Python 3.15 introduces explicit lazy imports, new built-in frozendict and sentinel types, UTF-8 as the default encoding, unpacking in comprehensions, and a new sampling profiler. It also includes improvements across the interpreter, standard library, typing, and developer tooling.
The major Python 3.15 features include explicit lazy imports for deferred loading, immutable frozendict mappings, the built-in sentinel type for unique marker values, UTF-8 by default, unpacking in comprehensions, and a sampling profiler for performance analysis.
Python 3.15’s explicit lazy imports allow selected imports to load only when they are needed. For applications with large dependency trees, this can reduce work during startup by avoiding the immediate loading of modules that are not used during initialization.
frozendict is a new built-in immutable mapping type in Python 3.15. It is useful when a mapping should remain unchanged after creation, such as configuration data, constants, or values that need immutable mapping semantics.
Python 3.15 makes UTF-8 the default text encoding, reducing reliance on the operating system’s locale-specific encoding. It can make applications more predictable across development, testing, and production environments, particularly when handling files and text from different systems.
Yes, Python 3.15 adds unpacking support in comprehensions, allowing developers to incorporate unpacked values directly into comprehension expressions. This expands the ways developers can construct collections using concise Python syntax.
Python 3.15 includes performance-related improvements and introduces a sampling profiler for lower-overhead performance analysis. Actual performance gains vary by workload, so applications should be benchmarked on Python 3.15 rather than relying on a single expected speedup.
Many Python 3.14 applications should run on Python 3.15, but compatibility depends on the codebase, dependencies, and any behavior affected by changes in the new release. Your team should test their application and third-party packages before moving production workloads to Python 3.15.
Consider upgrading when your application and dependencies support Python 3.15 and its new capabilities provide a practical benefit. Before upgrading production systems, test dependencies, encoding-sensitive code, imports, performance, and the application’s existing test suite against Python 3.15.
Rajiv Mehta
AI/ML Developer at Bacancy
Versatile tech leader driving innovation across cloud, AI, and full-stack development.