<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>README — python</title>
<link>https://readme.news/tags/python/</link>
<atom:link href="https://readme.news/tags/python/feed.xml" rel="self" type="application/rss+xml"/>
<description>README pieces tagged python.</description>
<language>en-us</language>
<lastBuildDate>Thu, 01 Oct 2026 13:20:31 +0000</lastBuildDate>
<item><title>Python 3.15 and free-threading's second act</title><link>https://readme.news/python-315-and-free-threadings-second-act/</link><guid isPermaLink="true">https://readme.news/python-315-and-free-threadings-second-act/</guid><pubDate>Mon, 11 May 2026 09:00:00 +0000</pubDate><description>The GIL-free build is officially supported and the ecosystem work is the actual story. A progress report.</description><content:encoded><![CDATA[<p>Python 3.15 is in beta. The headline features are incremental. The story worth tracking is the free-threading migration, which is now in the phase where it succeeds or stalls based on ecosystem work rather than on CPython.</p>
<h2 id="where-free-threading-actually-stands">where free-threading actually stands<a class="anchor" href="#where-free-threading-actually-stands" aria-label="link to this section">#</a></h2>
<p>The GIL-free build has been officially supported since 3.14. The question was never whether CPython could do it — it demonstrably can — but whether the ecosystem would follow.</p>
<p><strong>What has gone well:</strong></p>
<p>The major numerical and data libraries did the work. NumPy, and the array and dataframe ecosystem around it, ship free-threaded wheels. That was the critical path, because those libraries are the reason a large fraction of Python's CPU-bound workload exists.</p>
<p>Single-threaded performance in the free-threaded build has improved substantially since 3.13. The gap against the default build has narrowed to something most applications would not notice.</p>
<p>Build infrastructure caught up. Producing free-threaded wheels is now a configuration change rather than a project.</p>
<p><strong>What has gone slowly:</strong></p>
<p>The long tail. Thousands of packages with C extensions have not been audited, and most of them will not be until someone hits a problem.</p>
<p>The subtler issue: <strong>pure-Python code that was accidentally thread-safe.</strong> The GIL made many operations effectively atomic. Code written under that assumption — a dictionary mutated from multiple threads, a counter incremented without a lock — worked by accident. Under free-threading it does not.</p>
<p>Nobody knows how much library code depends on this, because nobody ever had to think about it. It will be discovered one race condition at a time.</p>
<h2 id="the-practical-guidance">the practical guidance<a class="anchor" href="#the-practical-guidance" aria-label="link to this section">#</a></h2>
<p><strong>Do not switch production workloads casually.</strong> The performance win only exists for CPU-bound multithreaded work. If your workload is I/O-bound, <code>asyncio</code> was already handling it and free-threading gives you nothing.</p>
<p><strong>Do test your libraries against it.</strong> The compatibility work has to happen and it happens when people run into problems and report them. If you maintain a package with a C extension, this is your work to do.</p>
<p><strong>Do use it for the workloads it is for.</strong> Data processing, image and signal processing, simulation, anything numeric that currently uses <code>multiprocessing</code> and pays serialization and memory-duplication costs.</p>
<p>The migration from <code>multiprocessing</code> to threads for those workloads is frequently dramatic — not just faster, but simpler, because you delete the serialization layer.</p>
<h2 id="the-other-thing-in-315">the other thing in 3.15<a class="anchor" href="#the-other-thing-in-315" aria-label="link to this section">#</a></h2>
<p><strong>Subinterpreters continue maturing.</strong> <code>concurrent.interpreters</code> gives you isolated interpreter instances in one process, with much lower overhead than processes and much stronger isolation than threads.</p>
<p>This is the underrated option. For a workload where you want parallelism and do not want shared mutable state — which is most workloads — subinterpreters give you the process model's safety at closer to the thread model's cost.</p>
<p>It is the right default for a lot of the cases people currently reach for <code>multiprocessing</code> for, and it does not require any of the thread-safety auditing that free-threading does.</p>
<p><strong><a class="xref" href="/error-messages-are-a-user-interface/" title="Error messages are a user interface">Error messages</a> continue improving</strong>, which has been a multi-release trend and has made Python meaningfully friendlier for people learning it.</p>
<h2 id="the-assessment">the assessment<a class="anchor" href="#the-assessment" aria-label="link to this section">#</a></h2>
<p>Python is executing a decade-long project to stop being single-threaded, and it is doing it without breaking the ecosystem, on an opt-in basis, with a fallback.</p>
<p>That is the right way to do it and it is slower than anyone wants. The alternative — a hard break, a Python 4 — would have been faster and would have cost the community what Python 3 cost it, which nobody has the appetite for.</p>
<p>Ask again in three years. The trajectory is good and the destination is not in doubt; only the timeline is.</p>]]></content:encoded></item><item><title>Pattern matching arrives everywhere at once</title><link>https://readme.news/pattern-matching-arrives-everywhere-at-once/</link><guid isPermaLink="true">https://readme.news/pattern-matching-arrives-everywhere-at-once/</guid><pubDate>Mon, 23 Mar 2026 09:00:00 +0000</pubDate><description>Java, Python, C#, and JavaScript are all converging on the same feature from ML languages, thirty years late.</description><content:encoded><![CDATA[<p>Four mainstream languages have shipped or are shipping structural pattern matching in the last several years. The feature is forty years old and came from ML and Haskell, and it is worth understanding why it took this long and what it actually buys.</p>
<h2 id="what-it-is">what it is<a class="anchor" href="#what-it-is" aria-label="link to this section">#</a></h2>
<p>Destructuring and dispatch in one construct: match a value against a shape, bind its parts to names, and branch on which shape matched.</p>
<p>Java:</p>
<div class="code"><span class="code-lang">java</span><pre><code class="lang-java">String describe(Shape s) {
    return switch (s) {
        case Circle c when c.radius() &gt; 100 -&gt; "big circle";
        case Circle c                        -&gt; "circle r=" + c.radius();
        case Rect(int w, int h) when w == h  -&gt; "square " + w;
        case Rect(int w, int h)              -&gt; "rect " + w + "x" + h;
    };
}</code></pre></div>
<p>Python:</p>
<div class="code"><span class="code-lang">python</span><pre><code class="lang-python">match command.split():
    case ["move", direction]:
        move(direction)
    case ["take", *items] if items:
        for item in items: take(item)
    case _:
        unknown()</code></pre></div>
<p>Both are doing the same thing: testing structure, extracting components, and binding them, in one expression.</p>
<h2 id="why-it-matters-more-than-it-looks">why it matters more than it looks<a class="anchor" href="#why-it-matters-more-than-it-looks" aria-label="link to this section">#</a></h2>
<p><strong>Exhaustiveness checking.</strong> This is the actual payoff and it is easy to miss.</p>
<p>If your <a class="xref" href="/type-systems-and-the-cost-of-being-right/" title="Type systems and the cost of being right">type system</a> knows the complete set of possible shapes — a sealed hierarchy in Java, a union type in TypeScript, an enum in Rust — the compiler can verify that your match handles all of them.</p>
<p>Which means: <strong>add a new case to your data model, and the compiler tells you every place that needs updating.</strong></p>
<p>That is a qualitatively different maintenance experience. Without it, adding a new variant means grepping for every switch statement and hoping. With it, the build fails until you have handled it everywhere.</p>
<p>This is the single largest practical benefit of algebraic data types and most discussion of pattern matching skips it entirely in favor of syntax.</p>
<p><strong>It replaces the visitor pattern.</strong> An entire design pattern existed because object-oriented languages could not dispatch on structure. That pattern is a significant amount of ceremony — an <a class="xref" href="/the-interface-is-the-product/" title="The interface is the product">interface</a>, an accept method on every node, a visitor implementation — to express what pattern matching says in five lines.</p>
<p>If you have a visitor in your codebase and your language now has pattern matching, you can delete a lot of code.</p>
<p><strong>It makes illegal states harder to express.</strong> Combined with sum types, you can model "this is one of exactly these things" and have the compiler enforce it, rather than a class with six nullable fields where only certain combinations are valid.</p>
<h2 id="the-caveats">the caveats<a class="anchor" href="#the-caveats" aria-label="link to this section">#</a></h2>
<p><strong>Python's <code>match</code> is not exhaustiveness-checked.</strong> It is a runtime construct. Without a sealed type system there is nothing to check against. Type checkers can do some of this with <code>Literal</code> and <code>Union</code> types and it is not enforced by the language.</p>
<p>That makes Python's version considerably less valuable than Java's or Rust's — it is nicer destructuring syntax rather than a correctness tool.</p>
<p><strong>Capture semantics surprise people.</strong> In Python, a bare name in a pattern <em>binds</em>, it does not compare. This is the number one confusion:</p>
<div class="code"><span class="code-lang">python</span><pre><code class="lang-python">match color:
    case RED:      # binds `color` to RED — matches everything!
        ...
    case Color.RED:  # this compares
        ...</code></pre></div>
<p>A dotted name compares. A bare name binds. Everybody gets this wrong once.</p>
<p><strong>Overuse.</strong> Pattern matching is satisfying and it is possible to write a match statement where an if would be clearer. If you are matching on one condition, use an if.</p>
<h2 id="the-trend-it-is-part-of">the trend it is part of<a class="anchor" href="#the-trend-it-is-part-of" aria-label="link to this section">#</a></h2>
<p>Pattern matching is one of several ideas migrating from functional languages into the mainstream, along with immutability by default, expression-oriented syntax, <code>Option</code>/<code>Result</code> types instead of null and exceptions, and real sum types.</p>
<p>Each of these took decades to cross over. The reason is not that the ideas were unknown — it is that mainstream languages have compatibility obligations that make adding a feature to an existing type system extraordinarily hard, and it takes a generation of language designers who grew up with the ideas to do the work.</p>
<p>The next ones in the queue, visibly: effect systems, better concurrency abstractions in the type system, and some form of ownership tracking outside of Rust.</p>
<p>Give it ten years.</p>]]></content:encoded></item><item><title>Python 3.14 makes free-threading official</title><link>https://readme.news/python-314-makes-free-threading-official/</link><guid isPermaLink="true">https://readme.news/python-314-makes-free-threading-official/</guid><pubDate>Tue, 07 Oct 2025 09:00:00 +0000</pubDate><description>The GIL-free build graduates from experimental, template strings land, and annotations finally get lazy evaluation.</description><content:encoded><![CDATA[<p>Python 3.14 is out. Three things in it are structurally important and one of them has been thirty years coming.</p>
<h2 id="free-threading-is-officially-supported">free-threading is officially supported<a class="anchor" href="#free-threading-is-officially-supported" aria-label="link to this section">#</a></h2>
<p>The GIL-free build, introduced experimentally in 3.13, is now an officially supported configuration. Not the default — you install <code>python3.14t</code> alongside the standard build — but supported, with a commitment to maintain it.</p>
<p>This is the largest change to Python's execution model since the language existed. The global interpreter lock has meant that CPU-bound Python code cannot use multiple cores in one process, which is why every CPU-bound Python program either uses <code>multiprocessing</code> (with its serialization overhead and memory duplication) or drops into C.</p>
<div class="code"><span class="code-lang">bash</span><pre><code class="lang-bash">python3.14t -c "import sys; print(sys._is_gil_enabled())"
# False</code></pre></div>
<p><strong>The caveats are real and you should read them before getting excited.</strong></p>
<p>Single-threaded performance in the free-threaded build is slower — the reference counting has to be thread-safe, and that costs. The gap has narrowed a lot between 3.13 and 3.14 and it has not closed.</p>
<p>C extensions must be explicitly compatible. Anything using the C API with assumptions about GIL protection needs auditing. The major numerical and data libraries have been doing this work for two years; the long tail has not.</p>
<p>And the hard part: <strong>your Python code is now actually concurrent.</strong> Race conditions that the GIL was accidentally preventing are now possible. Code that was "thread-safe" because bytecode operations were effectively atomic may not be. Nobody knows how much library code depends on this implicitly, because nobody has ever had to know.</p>
<p>Do not switch production workloads casually. Do start testing your libraries against it, because the compatibility work has to happen somewhere.</p>
<h2 id="t-strings">t-strings<a class="anchor" href="#t-strings" aria-label="link to this section">#</a></h2>
<p>PEP 750 lands. <code>t"..."</code> produces a <code>Template</code> with the static parts and the interpolated values separated, rather than a concatenated string.</p>
<div class="code"><span class="code-lang">python</span><pre><code class="lang-python">from string.templatelib import Template
name = input()
query = t"select * from users where name = {name}"
query.strings   # static segments
query.values    # (name,)</code></pre></div>
<p>A library receiving a <code>Template</code> can parameterize the SQL, escape by HTML context, or quote for a shell — correctly, because it can still tell the difference between the template and the data.</p>
<p>The value depends entirely on library adoption. Watch the database drivers.</p>
<h2 id="deferred-annotation-evaluation">deferred annotation evaluation<a class="anchor" href="#deferred-annotation-evaluation" aria-label="link to this section">#</a></h2>
<p>PEP 649. Annotations are no longer evaluated at definition time; they are computed lazily when something asks for them.</p>
<div class="code"><span class="code-lang">python</span><pre><code class="lang-python">def process(items: list[Order]) -&gt; Report:   # Order and Report need not exist yet
    ...</code></pre></div>
<p>This kills <code>from __future__ import annotations</code>, kills most string-quoted forward references, and fixes the circular import problems that plague any codebase using type hints heavily across modules.</p>
<p>It also makes runtime introspection cheaper for the common case where nobody looks at annotations at all.</p>
<h2 id="the-rest">the rest<a class="anchor" href="#the-rest" aria-label="link to this section">#</a></h2>
<ul><li><strong>Multiple interpreters in the standard library</strong> (<code>concurrent.interpreters</code>), giving true isolation with lower overhead than processes.</li><li><strong>A new REPL</strong> with syntax highlighting and better multi-line editing.</li><li><strong><code>except</code> without parentheses</strong> for multiple exception types.</li><li><strong>Substantially better <a class="xref" href="/error-messages-are-a-user-interface/" title="Error messages are a user interface">error messages</a></strong>, continuing a multi-release trend that has made Python meaningfully friendlier to learn.</li></ul>
<h2 id="the-assessment">the assessment<a class="anchor" href="#the-assessment" aria-label="link to this section">#</a></h2>
<p>Python's response to being slow and single-threaded has, for two decades, been "call C." 3.14 is the release where the language stops accepting that as the answer.</p>
<p><a class="xref" href="/python-315-and-free-threadings-second-act/" title="Python 3.15 and free-threading&#x27;s second act">Free-threading</a> plus subinterpreters plus the ongoing specializing-interpreter work is a coherent strategy for making Python a reasonable choice for CPU-bound work. It will take several more releases and a lot of ecosystem effort.</p>
<p>It is genuinely happening, which is more than most people expected five years ago.</p>]]></content:encoded></item><item><title>PEP 750 lands: Python gets template strings</title><link>https://readme.news/pep-750-lands-python-gets-template-strings/</link><guid isPermaLink="true">https://readme.news/pep-750-lands-python-gets-template-strings/</guid><pubDate>Fri, 14 Mar 2025 09:00:00 +0000</pubDate><description>A new `t` prefix produces a Template object instead of a string. It&#x27;s f-strings without the injection vulnerability.</description><content:encoded><![CDATA[<p>PEP 750 has been accepted for <a class="xref" href="/python-314-makes-free-threading-official/" title="Python 3.14 makes free-threading official">Python 3.14</a>. It adds a <code>t</code> string prefix that produces a <code>Template</code> object rather than a <code>str</code>, giving library authors access to the interpolated values before they are stringified.</p>
<p>This is a small feature with a large security implication.</p>
<h2 id="the-problem-it-solves">the problem it solves<a class="anchor" href="#the-problem-it-solves" aria-label="link to this section">#</a></h2>
<p>F-strings are wonderful and they are the most common source of injection bugs in modern Python, because they make the wrong thing effortless:</p>
<div class="code"><span class="code-lang">python</span><pre><code class="lang-python"># this is a SQL injection and it looks completely normal
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")

# so is this, for HTML
return f"&lt;div&gt;{user_bio}&lt;/div&gt;"</code></pre></div>
<p>The f-string evaluates and concatenates before the function ever sees it. By the time <code>execute</code> gets the string, the distinction between the query template and the user data is gone permanently. There is no way for a library to help you.</p>
<h2 id="what-t-strings-do">what t-strings do<a class="anchor" href="#what-t-strings-do" aria-label="link to this section">#</a></h2>
<p>A t-string does not produce a string. It produces a <code>Template</code> containing the static text segments and the interpolations separately:</p>
<div class="code"><span class="code-lang">python</span><pre><code class="lang-python">from string.templatelib import Template

name = "Robert'); DROP TABLE students;--"
t = t"SELECT * FROM users WHERE name = '{name}'"

type(t)          # &lt;class 'string.templatelib.Template'&gt;
t.strings        # ("SELECT * FROM users WHERE name = '", "'")
t.values         # ("Robert'); DROP TABLE students;--",)</code></pre></div>
<p>Now a library can do the right thing. A SQL driver can turn the static parts into a parameterized query and bind the values. An HTML library can escape each interpolation according to its context — attribute versus text node versus URL — which is something no escaping function can do correctly without knowing where the value landed.</p>
<div class="code"><span class="code-lang">python</span><pre><code class="lang-python"># a hypothetical driver that accepts templates
await conn.execute(t"SELECT * FROM users WHERE name = {name}")
# becomes: SELECT * FROM users WHERE name = $1, with name bound</code></pre></div>
<p>The syntax is identical to an f-string. Format specs and conversions work. Nesting works. The only difference is the prefix and the type.</p>
<h2 id="why-this-is-the-right-design">why this is the right design<a class="anchor" href="#why-this-is-the-right-design" aria-label="link to this section">#</a></h2>
<p>The alternative approaches all failed for the same reason: they required the developer to do extra work at the call site, and developers under deadline do the easy thing.</p>
<p><code>t"..."</code> is exactly as easy as <code>f"..."</code>. It is one character. And crucially, a function that expects a <code>Template</code> will <em>reject</em> an f-string with a type error — so a library can make the safe path the only path, and the unsafe call site becomes a build failure rather than a pentest finding.</p>
<p>That is the property that makes this work. Security features that depend on diligence do not scale. Security features that are enforced by the <a class="xref" href="/type-systems-and-the-cost-of-being-right/" title="Type systems and the cost of being right">type system</a> do.</p>
<h2 id="what-to-watch">what to watch<a class="anchor" href="#what-to-watch" aria-label="link to this section">#</a></h2>
<p>The value of this feature is entirely downstream: it depends on library authors adopting it. Watch for template support in the major database drivers, the templating engines, the shell-command helpers, and the logging libraries.</p>
<p>If that adoption happens over the next couple of release cycles, a whole category of Python vulnerability quietly stops being writable. If it does not, this is a nice piece of syntax nobody uses.</p>
<p>Bet on adoption. The ergonomics are too good and the security argument is too easy to make to a security team.</p>]]></content:encoded></item>
</channel>
</rss>
