Python 3.15: Lazy Imports, UTF-8 by Default and a Faster JIT

<!– wp:paragraph –>
<p>Python 3.15 adds explicit lazy imports, makes UTF-8 the default encoding, ships a new sampling profiler, and improves the JIT. Here’s what’s changing and what to test before you upgrade.</p>
<!– /wp:paragraph –>

<!– wp:heading –>
<h2 class=”wp-block-heading”>Python 3.15 at a glance</h2>
<!– /wp:heading –>

<!– wp:paragraph –>
<p>Python 3.15 has worked through its release candidates, with the final build due around now (timing slipped slightly to allow more testing of some late lazy-import issues, so check python.org for the current status). The headline changes are lazy imports, UTF-8 as the default encoding, a new sampling profiler, and a faster JIT.</p>
<!– /wp:paragraph –>

<!– wp:heading –>
<h2 class=”wp-block-heading”>Explicit lazy imports (PEP 810)</h2>
<!– /wp:heading –>

<!– wp:paragraph –>
<p>A new <code>lazy</code> soft keyword defers loading a module until it’s actually used. Ordinary imports are unaffected – you opt in line by line:</p>
<!– /wp:paragraph –>

<!– wp:code –>
<pre class=”wp-block-code”><code>lazy import json
lazy from json import dumps

def export(data):
return dumps(data) # json only loads here, not at startup</code></pre>
<!– /wp:code –>

<!– wp:paragraph –>
<p>The payoff is start-up time for command-line tools and applications with large dependency graphs, where most imports are never touched on a typical run. There’s also a global switch that can disable lazy imports for every module in a process, which is handy when you suspect one is hiding an import-time side effect you depend on.</p>
<!– /wp:paragraph –>

<!– wp:heading –>
<h2 class=”wp-block-heading”>UTF-8 becomes the default encoding (PEP 686)</h2>
<!– /wp:heading –>

<!– wp:paragraph –>
<p>UTF-8 mode is now on by default regardless of the system locale. On Linux and macOS this is mostly invisible. On Windows with a legacy code page such as cp1252, code that relied on the locale encoding when opening files can behave differently. The escape hatch is the <code>PYTHONUTF8=0</code> environment variable while you fix things, but the better long-term fix is to pass <code>encoding=</code> explicitly wherever you open text files.</p>
<!– /wp:paragraph –>

<!– wp:heading –>
<h2 class=”wp-block-heading”>Tachyon: the new sampling profiler (PEP 799)</h2>
<!– /wp:heading –>

<!– wp:paragraph –>
<p>A new <code>profiling</code> package ships with Tachyon, a sampling profiler that periodically captures stack traces from a running process instead of instrumenting every call. Documentation describes very low overhead at high sampling rates, which makes it realistic to point at a live service rather than only at test runs. The older <code>cProfile</code> tracing functionality moves under <code>profiling.tracing</code>.</p>
<!– /wp:paragraph –>

<!– wp:heading –>
<h2 class=”wp-block-heading”>A faster JIT, with caveats</h2>
<!– /wp:heading –>

<!– wp:paragraph –>
<p>The experimental JIT introduced in 3.14 improves again in 3.15. The core team reports a mean speed-up of roughly 8-9% on x86-64 Linux and 12-13% on Apple silicon, though some code can still run slower, and the JIT remains opt-in rather than default. Those figures shifted during the beta cycle, so it’s worth benchmarking your own workload rather than taking them as guaranteed.</p>
<!– /wp:paragraph –>

<!– wp:heading –>
<h2 class=”wp-block-heading”>Other changes worth knowing about</h2>
<!– /wp:heading –>

<!– wp:list –>
<ul class=”wp-block-list”><!– wp:list-item –>
<li><code>frozendict</code> and sentinel built-ins</li>
<!– /wp:list-item –><!– wp:list-item –>
<li>Unpacking inside comprehensions</li>
<!– /wp:list-item –><!– wp:list-item –>
<li>A stable ABI for free-threaded builds, which affects how binary wheels get built</li>
<!– /wp:list-item –><!– wp:list-item –>
<li>Frame pointers enabled by default, which helps native profilers and debuggers</li>
<!– /wp:list-item –></ul>
<!– /wp:list –>

<!– wp:heading –>
<h2 class=”wp-block-heading”>A short upgrade checklist</h2>
<!– /wp:heading –>

<!– wp:list {“ordered”:true} –>
<ol class=”wp-block-list”><!– wp:list-item –>
<li>Run your test suite against the release candidate or final build in CI, with warnings treated as errors.</li>
<!– /wp:list-item –><!– wp:list-item –>
<li>Search for <code>open()</code> calls with no <code>encoding</code> argument and fix them.</li>
<!– /wp:list-item –><!– wp:list-item –>
<li>Try lazy imports on your slowest-starting CLI tool and measure the difference with <code>python -X importtime</code>.</li>
<!– /wp:list-item –><!– wp:list-item –>
<li>If you build C extensions, check your wheel tags against the free-threaded ABI change.</li>
<!– /wp:list-item –><!– wp:list-item –>
<li>Keep production on 3.14 until your key dependencies publish 3.15 wheels.</li>
<!– /wp:list-item –></ol>
<!– /wp:list –>

<!– wp:paragraph –>
<p>One note from the release-candidate coverage: tkinter apps on macOS 27 may have issues, so test any GUI code there separately before relying on it.</p>
<!– /wp:paragraph –>


Leave a Reply