Python 3.15 arrived Thursday, October 1, 2026. The change most likely to affect existing code is the new default encoding: text I/O now uses UTF-8 regardless of system locale, so code that opens files without an explicit encoding= argument may read them differently, especially on Windows. The release also adds a lazy keyword that defers heavy imports until first use, which shortens startup for notebooks and command-line tools, and a sampling profiler in the standard library that attaches to a running process without restarting it or adding instrumentation. If your code opens files without specifying an encoding, audit it before you upgrade.

As maintainers of the Anaconda main and main-x channels, we work through what a new interpreter means for thousands of packages. Learn more about Anaconda main-x. Here is what we think matters for this release.

Adopting Python 3.15 depends on the readiness of the packages and platforms developers rely on. Our collaboration with Microsoft and Qualcomm will extend that ecosystem work to Windows Arm64, helping developers build with native Python packages on modern hardware.

“Moving Windows Arm64 into mainstream Python development requires more than a native runtime. It requires a strong package ecosystem. Anaconda has been an important partner in that work, bringing thousands of native packages to Windows Arm64 and making it easier for developers to build, test, and deploy with confidence.” —Chris Anderson, Distinguished Engineer, Windows Platform + Developer

That collaboration also connects the Python package ecosystem to the hardware developers use, including Snapdragon-powered Windows devices.

“Developers thrive when their tools just work, which is why a strong and comprehensive Python ecosystem is so important for Snapdragon X Series platforms. Working closely with Microsoft, Anaconda, and ecosystem partners, we continue to advance native support across the Python stack, making it easier for developers to build, test, and deploy applications on Windows on Snapdragon. This further enables developers to take advantage of superior performance, power efficiency, and AI capabilities of Snapdragon-powered Windows devices.” —Milan Plavsich, VP, Engineering, Qualcomm Technologies, Inc.

What changes, and what to do about it

ChangeWho it affectsWhat to do
UTF-8 is the default encoding (PEP 686)Anyone opening files without an explicit encoding, especially on WindowsRun your tests with -X warn_default_encoding and add explicit encoding= arguments
datetime.strptime is stricterDate parsing that uses %d without a year directiveAdd a year directive, or handle the new ValueError
sre_compile, sre_constants, sre_parse removedOlder libraries that import CPython’s regex internalsUpgrade the dependency. There is no shim
NamedTuple keyword syntax removedCode that builds NamedTuple classes with keyword argumentsSwitch to the class-based or functional syntax
codeobject.co_lnotab removedProfilers, debuggers, and coverage tools, rather than your own codeUpgrade your tooling before the interpreter
.pth import lines deprecated (PEP 829)Environments where packages install path hooks, including editable installsInventory your .pth files. Expect maintainers to migrate to .start
Free-threaded wheels remain scarceTeams evaluating free-threaded PythonProfile with the new --mode gil first to find out whether it would help you
Lazy imports (PEP 810)Slow-starting notebooks and command-line toolsOptional. Mark heavy top-level imports lazy, but keep anything with import-time side effects eager

(A PEP is a Python Enhancement Proposal, the mechanism by which the language gets new features. PEP 8, the style guide, is the one most people meet first.)

Five changes that matter for data work

1. UTF-8 is now the default encoding (PEP 686)

This is the change with the widest blast radius. Opening a file without an explicit encoding used to fall back to the system locale, so the same script could read the same file differently on a Linux continuous integration (CI) runner and a colleague’s Windows laptop. In 3.15, text I/O defaults to UTF-8 regardless of environment.

Worth knowing what this touches. Anything that goes through a plain open() is affected, including pathlib, csv, and numpy.loadtxt. pandas is the exception: read_csv already defaults to UTF-8, so it behaves the same before and after 3.15.

For most people this is a correctness improvement. But if you have files genuinely encoded in something else, or code that depends on a locale default to read them, behavior changes:

# Before 3.15, the encoding came from the system locale.
# In 3.15, this reads as UTF-8 everywhere.
with open("customer_records.csv") as f:
    data = f.read()

# The durable fix: always be explicit.
with open("customer_records.csv", encoding="utf-8") as f:
    data = f.read()

Audit your codebase before you upgrade:

python -X warn_default_encoding -m pytest

Every warning marks a file operation whose result depends on the locale it runs under. That is exactly the code 3.15 makes deterministic. Adding explicit encoding= arguments is safe on every Python version you currently support, so you can do this today, before you upgrade anything. It is the single highest-value action in this post. If you need the old behavior meanwhile, PYTHONUTF8=0 restores locale-based encoding globally and encoding="locale" does it call by call.

2. Explicit lazy imports (PEP 810)

Anyone who has waited on a notebook kernel, or a command-line tool that imports the entire scientific stack before printing --help, knows this problem. The usual workaround is burying imports inside functions, which scatters them through the file and hides your dependencies.

Python 3.15 adds a lazy soft keyword that defers the work until the name is first used:

lazy import scipy
lazy from sklearn.ensemble import RandomForestClassifier

print("CLI ready")          # neither has loaded yet

def train(X, y):
    model = RandomForestClassifier()   # sklearn loads here
    return model.fit(X, y)

You keep every import declared at the top of the file and pay only for what a run actually uses. If a lazy module fails to load, the error surfaces at first use, with a traceback pointing at both the access site and the original import.

One caveat before you reach for this. lazy defers a module’s import-time side effects along with the import itself, so anything that registers a backend, plugin, or codec when it loads should keep a plain import. A module imported purely for its side effect, where the bound name is never referenced, will never load at all.

lazy works at module scope only, and not with star or __future__ imports. To enable it without editing source, both -X lazy_imports and PYTHON_LAZY_IMPORTS take a value: all makes every import lazy, normal is the default and respects the keyword. So it is -X lazy_imports=all, not the bare flag. Code that must also run on 3.14 can declare __lazy_modules__ instead.

3. A production-grade sampling profiler (PEP 799)

Python’s profiling tools now live in one profiling package: profiling.tracing for deterministic call tracing, relocated from cProfile, and profiling.sampling, a new statistical sampling profiler called Tachyon.

Deterministic profilers instrument every call, which both slows the program and distorts the measurement. Tachyon samples stack traces from a running process instead. The release notes describe it as having virtually zero overhead, and separately as reaching sampling rates of up to 1,000,000 Hz. You can attach to a training job already in flight, by process ID (PID), without restarting it or adding a line of instrumentation:

# Attach to a running process and write an interactive flame graph
python -m profiling.sampling attach --flamegraph -o profile.html 48213

# Is the job stuck? Dump every thread's stack 
python -m profiling.sampling dump 48213

Attaching reads the target process’s memory, so it needs elevated permissions on most systems. The platform requirements cover what each OS asks for.

Four modes cover the questions data teams actually ask:

ModeMeasuresUse it when
--mode wall (default)Elapsed time, including I/O and network waitsA pipeline takes an hour and you want to know why
--mode cpuActive CPU execution onlyYou need to isolate compute-bound work
--mode gilTime holding the interpreter lockYou are deciding whether free-threaded Python would help you
--mode exceptionThreads with an active exceptionYou suspect exception handling overhead

That third row deserves emphasis. Before investing in a free-threading migration, --mode gil tells you whether lock contention is your bottleneck.

Output can be pstats tables, flame graphs, a line-level heatmap, Gecko format for the Firefox Profiler, or a live top-style terminal view. cProfile remains as an alias; the older profile module is deprecated for removal in 3.17.

4. A stable ABI for free-threaded builds (PEP 803)

Python 3.14 made free-threaded mode officially supported. C extensions targeting the stable application binary interface (ABI) had no free-threading-compatible target, so every package needed a separate build for every Python version.

Python 3.15 introduces abi3t, a stable ABI for free-threaded builds. Extensions compiled against it work with free-threaded CPython without a per-version rebuild, which is what makes broad wheel coverage sustainable rather than heroic.

Keep in mind these two caveats. Moving to abi3t is not a recompile: it needs source changes, including the PEP 697 APIs from 3.12 and the new PyModExport_* export hook from PEP 793. And as of the 3.15 release the major build backends did not yet support it, so check your tool before assuming otherwise and keep compiling cp315t separately meanwhile. CPython ships an abi3t migration guide for maintainers ready to start.

At Anaconda, we ship a free-threaded interpreter variant today, and we are working to extend free-threaded builds beyond the interpreter to packages, starting with Python 3.14 and continuing into 3.15, with no committed dates yet.

5. Startup configuration files replace .pth import lines (PEP 829)

.pth files, loaded by the site module at startup, can extend sys.path and also execute arbitrary code when a line begins with import. That second behavior is widespread: setuptools ships distutils-precedence.pth, and some editable installs do the same. It is hard to audit, because nothing tells you what runs when the interpreter starts.

Python 3.15 introduces .start files holding entry point specifications of the form pkg.mod:callable. You can read one and know exactly what it invokes. Two consequences follow:

  • import lines in .pth files are silently deprecated, without any warnings. Path extension lines are unaffected.
  • When a matching .start file exists, import lines in the corresponding .pth file are ignored.

As maintainers migrate, an environment can hold both file types for the same package, and the .start file wins. If you run environments with custom path hooks or vendored build tooling, inventory your .pth files now and find out which ones execute code.

Also worth knowing

  • The incremental garbage collector has been reverted. Python 3.14.0 through 3.14.4 shipped it; after reports of significant memory pressure in production it was reverted to the generational collector from 3.13. That is the collector in 3.14.5 and later, and in 3.15. If you saw unexplained memory growth on an early 3.14, this is likely why.
  • The just-in-time (JIT) compiler has been significantly upgraded, with a new tracing frontend, basic register allocation, and better machine code generation. It is still experimental and still off by default, but it is compiled into the official builds rather than absent from them.
  • Unpacking in comprehensions (PEP 798), so [*batch for batch in batches] replaces itertools.chain.from_iterable() for flattening.
  • frozendict and sentinel builtins (PEP 814, PEP 661). Note that frozendict does not subclass dict, so isinstance(x, dict) checks need updating to isinstance(x, (dict, frozendict)).
  • ctypes complex types changed. The _type_ of c_float_complex, c_double_complex, and c_longdouble_complex changed from F, D, and G to Zf, Zd, and Zg, for NumPy compatibility.
  • Frame pointers on by default (PEP 831), plus better AttributeError suggestions and color across the standard library command-line interfaces (CLIs).
  • Further removals. Beyond those in the table above: ctypes.SetPointerType, glob.glob0 and glob.glob1, pathlib.PurePath.is_reserved, platform.java_ver, and zero-field TypedDict construction. typing.ByteString and collections.abc.ByteString leave their modules’ __all__ ahead of full removal in 3.17. The full list is in the release notes.

When to move

PostureTimingBest for
Conservative6 to 12 months out, so April to October 2027. Stay on 3.13 or 3.14 and test in isolation meanwhileProduction systems, mission-critical applications, complex dependency graphs
Early adopterTwo to four months. Test the core scientific stack first, keep a fallback environmentResearch teams, individual developers, experimental projects
Bleeding edgeNow, with 3.15.0 final in an isolated environmentPackage maintainers, core contributors

Python 3.10 reaches end of support

Anaconda is ending support for Python 3.10 at the end of October 2026, matching the Python Software Foundation (PSF)’s five-year window. After that date, 3.10 interpreter and variant packages remain available in the main channel, but neither the PSF nor Anaconda will ship further patch or security releases, and we stop building new 3.10 variant packages.

A 3.10 environment keeps working. It stops receiving security fixes while the ecosystem around it moves on. Details are in our Python version support policy.

Getting started

The migration rewards preparation over speed. Audit your encodings now, test in isolated environments, and give the ecosystem time before you move production. Packages with compiled code can’t be built reliably until the interpreter’s internals stop changing, and that only happened eight weeks before release.

Our support policy provides our expected cadence, so you can plan against it rather than guess: once the PSF makes an official release, we typically publish the interpreter to the main channel within two weeks, the first packages within a month, and the majority of remaining packages within four months. We expect Python 3.15 will follow that pattern.

If you maintain packages, add 3.15 to your CI matrix and keep your conda-forge feedstock current. One note that is easy to miss: if your build system compiles C, C++, or Rust without inheriting Python’s sysconfig flags, enable the frame-pointer flags yourself, because a single component built without them breaks stack unwinding for the whole process.

Download Anaconda Distribution or Miniconda to set up your test environments.

Questions about Python 3.15 compatibility? Reach out to our support team or start a thread in our community forums.