<?xml version="1.0"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">

<channel>
	<title>Planet Python</title>
	<link>http://planetpython.org/</link>
	<language>en</language>
	<description>Planet Python - http://planetpython.org/</description>

<item>
	<title>Ari Lamstein: How Remote Work Has Grown — and Shrunk — Since Covid</title>
	<guid>https://arilamstein.com/blog/2026/05/18/how-remote-work-has-grown-and-shrunk-since-covid/</guid>
	<link>https://arilamstein.com/blog/2026/05/18/how-remote-work-has-grown-and-shrunk-since-covid/</link>
	<description>&lt;p&gt;Remote work surged during Covid — and while it has declined since, it’s still far above pre‑pandemic levels. I just updated my &lt;a href=&quot;https://census-explorer.streamlit.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Covid Demographics Explorer&lt;/a&gt; with the latest ACS data, and the national trend is striking:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arilamstein.com/wp-content/uploads/2026/05/wfh-national-2024.png&quot;&gt;&lt;img /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Remote work more than tripled between 2019 and 2021, rising to nearly 28 million people at the height of the pandemic. Since then it has edged down each year, but only modestly. Even today, at about 22 million, it remains roughly 2.5 times the pre‑Covid level.&lt;/p&gt;
&lt;p&gt;The app now lets you generate this same graph for every state, as well as for counties and cities with populations of at least 65,000. &lt;a href=&quot;https://census-explorer.streamlit.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;See how the trend looks where you live&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Exploring Local Trends&lt;/h2&gt;
&lt;p&gt;I also added a &amp;#8220;Compare Years&amp;#8221; tab that lets you see which locations saw the biggest change in remote work between any two years. The national trend tells one story, but the local data tells another: the rise and fall of remote work played out very unevenly across the country. Below I run this analysis twice: first for the national increase from 2019-2021, and then for the gradual decline between 2021 and 2024.&lt;/p&gt;
&lt;h3&gt;The Remote Work Spike: 2019-2021&lt;/h3&gt;
&lt;p&gt;Between 2019 and 2021, the location that increased the number of remote workers the most was Sunnyvale, California. The number of remote workers there increased almost 11x in two years, from an estimated 3,235 to 38,319. Sunnyvale is in the heart of Silicon Valley, and tech companies were among the fastest to adopt remote work, which helps explain this result:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arilamstein.com/wp-content/uploads/2026/05/wfh-scattter-2019-2021-sunnyvale.png&quot;&gt;&lt;img /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The scatterplot also shows the broader pattern: most locations cluster between a 150% and 300% increase in remote work during this period. That makes Sunnyvale’s nearly 1,100% jump stand out even more — it’s an order of magnitude beyond the national norm.&lt;/p&gt;
&lt;p&gt;Interestingly, only one location in the entire dataset saw a decrease in remote work during this period: Rice County, Minnesota (-7.5%). It’s the lone point below zero on the chart, and I don’t have a clear explanation for it.&lt;/p&gt;
&lt;h3&gt;The Remote Work Decline: 2021-2024&lt;/h3&gt;
&lt;p&gt;When we run this same analysis for 2021–2024, we see a very different result: Sunnyvale’s remote workforce shrank by 67.2%, the largest drop in the dataset. This means that Sunnyvale saw both the largest increase between 2019 and 2021 and the largest decrease between 2021 and 2024:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arilamstein.com/wp-content/uploads/2026/05/wfh-scattter-2021-2024-sunnyvale.png&quot;&gt;&lt;img /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The scatterplot also shows how different the overall pattern is in this period. Instead of large increases, most locations cluster between a 10% and 30% decline in remote work — a sharp contrast with the 2019–2021 graph, where nearly every location saw a substantial increase.&lt;/p&gt;
&lt;p&gt;Against this backdrop, Sunnyvale’s 67% drop stands out as an outlier. The likely explanation is the wave of return‑to‑office mandates that swept through the tech industry during this period. The two other largest decreases also happened in Silicon Valley: the city of Fremont (–61%) and Santa Clara County (–56%).&lt;/p&gt;
&lt;p&gt;At the other end of the distribution, the few places that saw increases tend to be warm‑weather, high‑amenity destinations: Marion County, Florida (69%), Collier County, Florida (65%), and Maui County, Hawaii (57%) saw the largest gains. These increases may reflect people with remote‑work jobs relocating to places with natural beauty and a high quality of life — a very different dynamic from the employer‑driven declines we see in Silicon Valley.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Three years after the peak, roughly 22 million Americans still work from home — more than double the pre-pandemic baseline. But the story is more complex than a single national number: a dramatic surge, an uneven retreat, and striking differences across the country. How does your corner of the country fit in?&lt;/p&gt;
&lt;p&gt;The new version of the Covid Demographics Explorer makes it easy to explore these patterns yourself. In addition to remote‑work trends, you can examine changes in population, median household income, median rent, and public assistance. &lt;a href=&quot;https://census-explorer.streamlit.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Analyze your own location&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This app was built in Python with the Streamlit framework. I teach Streamlit for O&amp;#8217;Reilly — and if you&amp;#8217;d like to learn to build apps like this yourself, I offer a free 7-day email course. Sign up in the form below.&lt;/p&gt;</description>
	<pubDate>Mon, 18 May 2026 20:00:30 +0000</pubDate>
</item>
<item>
	<title>Real Python: Python Built-in Functions: A Complete Guide</title>
	<guid>https://realpython.com/python-built-in-functions/</guid>
	<link>https://realpython.com/python-built-in-functions/</link>
	<description>&lt;div&gt;&lt;p&gt;Python’s &lt;strong&gt;built-in functions&lt;/strong&gt; are predefined functions you can use anywhere in your code without any imports. They handle common tasks across math, data type creation, iterable processing, and input and output. Knowing which ones to reach for makes your code shorter and more &lt;strong&gt;Pythonic&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you’ll:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recognize Python’s &lt;strong&gt;built-in functions&lt;/strong&gt; and the &lt;strong&gt;built-in scope&lt;/strong&gt; they live in&lt;/li&gt;
&lt;li&gt;Use the right built-in for &lt;strong&gt;math&lt;/strong&gt;, &lt;strong&gt;data types&lt;/strong&gt;, &lt;strong&gt;iterables&lt;/strong&gt;, and &lt;strong&gt;I/O&lt;/strong&gt; tasks&lt;/li&gt;
&lt;li&gt;Tell apart true functions and &lt;strong&gt;classes&lt;/strong&gt; that look like functions&lt;/li&gt;
&lt;li&gt;Apply built-ins to solve &lt;strong&gt;practical problems&lt;/strong&gt; without reinventing the wheel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get the most out of this tutorial, you’ll need to be familiar with Python programming, including topics like working with built-in &lt;a href=&quot;https://realpython.com/python-data-types/&quot;&gt;data types&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/defining-your-own-python-function/&quot;&gt;functions&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/python-classes/&quot;&gt;classes&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/primer-on-python-decorators/&quot;&gt;decorators&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/python-scope-legb-rule/&quot;&gt;scopes&lt;/a&gt;, and the &lt;a href=&quot;https://realpython.com/python-import/&quot;&gt;import&lt;/a&gt; system.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Get Your Code:&lt;/strong&gt; &lt;a href=&quot;https://realpython.com/bonus/python-built-in-functions-code/&quot; class=&quot;alert-link&quot;&gt;Click here to download the free sample code&lt;/a&gt; that shows you how to use Python’s built-in functions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;alert alert-warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Get the PDF Guide:&lt;/strong&gt; &lt;a href=&quot;https://realpython.com/bonus/python-built-in-functions-cheatsheet/&quot; class=&quot;alert-link&quot;&gt;Click here to download&lt;/a&gt; a free PDF guide that gives you a complete overview of Python’s built-in functions and how to use them.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;container border rounded text-wrap-pretty my-3&quot;&gt;

  &lt;p class=&quot;my-3&quot;&gt;&lt;strong&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “Python Built-in Functions: A Complete Guide” quiz. You’ll receive a score upon completion to help you track your learning progress:&lt;/p&gt;

  &lt;hr /&gt;

  &lt;div class=&quot;row my-3&quot;&gt;
    &lt;div class=&quot;col-xs-12 col-sm-4 col-md-3 align-self-center&quot;&gt;

      &lt;a href=&quot;https://realpython.com/quizzes/python-built-in-functions/&quot; tabindex=&quot;-1&quot;&gt;
        &lt;div class=&quot;embed-responsive embed-responsive-16by9&quot;&gt;

            &lt;img class=&quot;card-img-top m-0 p-0 embed-responsive-item rounded&quot; alt=&quot;Python's Built-in Functions: A Complete Exploration&quot; src=&quot;https://files.realpython.com/media/Pythons-Built-in-Functions-A-Quick-Exploration_Watermarked.44a8e102943b.jpg&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;


          &lt;div class=&quot;card-img-overlay d-flex align-items-center&quot;&gt;
            &lt;div class=&quot;mx-auto&quot;&gt;
              &lt;span class=&quot;text-light&quot;&gt;&lt;span class=&quot;icon baseline scale2x&quot;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/a&gt;

    &lt;/div&gt;
    &lt;div class=&quot;col&quot;&gt;
      &lt;div class=&quot;mt-3 d-md-none&quot;&gt;&lt;/div&gt; 
      &lt;p class=&quot;small text-muted mb-0&quot;&gt;&lt;strong&gt;Interactive Quiz&lt;/strong&gt;&lt;/p&gt;
      &lt;a href=&quot;https://realpython.com/quizzes/python-built-in-functions/&quot; class=&quot;stretched-link&quot;&gt;&lt;span class=&quot;my-0 h4&quot;&gt;Python Built-in Functions: A Complete Guide&lt;/span&gt;&lt;/a&gt; 
      &lt;p class=&quot;text-muted mb-0 small&quot;&gt;Test your understanding of Python's built-in functions for math, data types, iterables, and I/O—and when to reach for each one.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;

&lt;h2 id=&quot;built-in-functions-in-python&quot;&gt;Built-in Functions in Python&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#built-in-functions-in-python&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Python has several &lt;a href=&quot;https://realpython.com/defining-your-own-python-function/&quot;&gt;functions&lt;/a&gt; available for you to use directly from anywhere in your code. These functions are known as &lt;a href=&quot;https://docs.python.org/3/library/functions.html&quot;&gt;built-in functions&lt;/a&gt; and they cover many common programming problems, from mathematical computations to Python-specific features.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; All these functions live in the &lt;a href=&quot;https://docs.python.org/3/library/builtins.html&quot;&gt;&lt;code&gt;builtins&lt;/code&gt;&lt;/a&gt; module, which Python loads at startup and exposes through the built-in scope, so you can use them anywhere without importing the module. Importing the module explicitly is useful if you know that you’ll shadow a built-in name with one of your own variables or functions. Doing so keeps the original within reach as &lt;code&gt;builtins.name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Among these built-ins, you’ll also find classes with function-style names like &lt;a href=&quot;https://realpython.com/ref/builtin-types/str/&quot; class=&quot;ref-link&quot;&gt;&lt;code&gt;str&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/ref/builtin-types/tuple/&quot; class=&quot;ref-link&quot;&gt;&lt;code&gt;tuple&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/ref/builtin-types/list/&quot; class=&quot;ref-link&quot;&gt;&lt;code&gt;list&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://realpython.com/ref/builtin-types/dict/&quot; class=&quot;ref-link&quot;&gt;&lt;code&gt;dict&lt;/code&gt;&lt;/a&gt;, which define built-in data types. These classes are listed in the Python documentation as &lt;em&gt;built-in functions&lt;/em&gt;, so they’re covered in this tutorial too.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In this tutorial, you’ll learn the basics of Python’s built-in functions. By the end, you’ll know what their use cases are and how they work. You’ll start with the built-in functions for math computations.&lt;/p&gt;
&lt;h2 id=&quot;using-math-related-built-in-functions&quot;&gt;Using Math-Related Built-in Functions&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#using-math-related-built-in-functions&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In Python, you’ll find a few built-in functions that take care of common math operations, like computing the absolute value of a &lt;a href=&quot;https://realpython.com/python-numbers/&quot;&gt;number&lt;/a&gt;, calculating powers, and more. Here’s a summary of the math-related built-in functions in Python:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://realpython.com/python-absolute-value/&quot;&gt;&lt;code&gt;abs()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Calculates the absolute value of a number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.python.org/3/library/functions.html#divmod&quot;&gt;&lt;code&gt;divmod()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Computes the quotient and remainder of integer division&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://realpython.com/python-min-and-max/&quot;&gt;&lt;code&gt;max()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Finds the largest of the given arguments or items in an iterable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://realpython.com/python-min-and-max/&quot;&gt;&lt;code&gt;min()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Finds the smallest of the given arguments or items in an iterable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.python.org/3/library/functions.html#pow&quot;&gt;&lt;code&gt;pow()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Raises a number to a power&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://realpython.com/python-rounding/#pythons-built-in-round-function&quot;&gt;&lt;code&gt;round()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Rounds a floating-point value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://realpython.com/python-sum-function/&quot;&gt;&lt;code&gt;sum()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Sums the values in an iterable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;In the following sections, you’ll learn how these functions work and how to use them in your Python code.&lt;/p&gt;
&lt;h3 id=&quot;getting-the-absolute-value-of-a-number-abs&quot;&gt;Getting the Absolute Value of a Number: &lt;code&gt;abs()&lt;/code&gt;&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#getting-the-absolute-value-of-a-number-abs&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Absolute_value&quot;&gt;absolute value&lt;/a&gt; or &lt;strong&gt;modulus&lt;/strong&gt; of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Real_number&quot;&gt;real number&lt;/a&gt; is its non-negative value. In other words, the absolute value is the number without its &lt;a href=&quot;https://en.wikipedia.org/wiki/Sign_(mathematics)&quot;&gt;sign&lt;/a&gt;. For example, the absolute value of &lt;em&gt;-5&lt;/em&gt; is &lt;em&gt;5&lt;/em&gt;, and the absolute value of &lt;em&gt;5&lt;/em&gt; is also &lt;em&gt;5&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To learn more about &lt;code&gt;abs()&lt;/code&gt;, check out the &lt;a href=&quot;https://realpython.com/python-absolute-value/&quot;&gt;How to Find an Absolute Value in Python&lt;/a&gt; tutorial.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Python’s built-in &lt;code&gt;abs()&lt;/code&gt; function allows you to quickly compute the absolute value of a &lt;a href=&quot;https://realpython.com/python-numbers/&quot;&gt;number&lt;/a&gt;. Here’s its signature:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--blue&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;&lt;span class=&quot;sr-only&quot;&gt;Language: &lt;/span&gt;Python Syntax&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;The &lt;code&gt;number&lt;/code&gt; argument can be any numeric value, including integers, floating-point numbers, complex numbers, fractions, and decimals. Take a look at a few examples:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--blue&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;&lt;span class=&quot;sr-only&quot;&gt;Language: &lt;/span&gt;Python&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;decimal&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;fractions&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fraction&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;42&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;42&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;42.42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;42.42&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;42.42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;42.42&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;complex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-2+3j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3.605551275463989&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;complex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2+3j&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3.605551275463989&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fraction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-1/2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Fraction(1, 2)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fraction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1/2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Fraction(1, 2)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-0.5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Decimal('0.5')&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Decimal('0.5')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;In these examples, you compute the absolute value of different numeric types using the &lt;code&gt;abs()&lt;/code&gt; function. First, you use integer numbers, then floating-point and complex numbers, and finally, fractional and decimal numbers. In all cases, when you call the function with a negative value, the final result removes the sign.&lt;/p&gt;
&lt;p&gt;For a practical example, say that you need to compute the total profits and losses of your company from a month’s transactions:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--blue&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;&lt;span class=&quot;sr-only&quot;&gt;Language: &lt;/span&gt;Python&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transactions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;incomes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;income&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;income&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transactions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;income&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expenses&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expense&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expense&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transactions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expense&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Total incomes: $&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;incomes&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total incomes: $800&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Total expenses: $&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expenses&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total expenses: $300&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Total profit: $&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;incomes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expenses&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Total profit: $500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;/div&gt;&lt;h2&gt;&lt;a href=&quot;https://realpython.com/python-built-in-functions/?utm_source=realpython&amp;utm_medium=rss&quot;&gt;Read the full article at https://realpython.com/python-built-in-functions/ »&lt;/a&gt;&lt;/h2&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Mon, 18 May 2026 14:00:00 +0000</pubDate>
</item>
<item>
	<title>Artem Golubin: PyPI packages are increasing rapidly</title>
	<guid>https://rushter.com/blog/pypi-packages/</guid>
	<link>https://rushter.com/blog/pypi-packages/</link>
	<description>&lt;p&gt;PyPI is the main repository for Python packages.
One thing that I've noticed recently is the number of published packages per week.&lt;/p&gt;
&lt;p&gt;Let's look at published counts of new package versions per week:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://rushter.com/static/uploads/img/2026/pypi_stats_weekly.png&quot; class=&quot;ui centered image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are some dips in the data, but that's because of how the &lt;a href=&quot;https://clickpy.clickhouse.com/&quot; target=&quot;_blank&quot;&gt;data&lt;/a&gt; was collected.
We can see a clear increase in the number of published packages, especially in the last few months.&lt;/p&gt;
&lt;p&gt;Because of AI, the number of packages published per week has increased by 30% since 2025.&lt;/p&gt;
&lt;p&gt;I'm working on &lt;a href=&quot;https://github.com/rushter/hexora&quot; target=&quot;_blank&quot;&gt;hexora&lt;/a&gt;, a library that detects malicious Python code in packages.[......]&lt;/p&gt;</description>
	<pubDate>Sun, 17 May 2026 13:37:15 +0000</pubDate>
</item>
<item>
	<title>Kay Hayen: Nuitka Release 4.1</title>
	<guid>https://nuitka.net/posts/nuitka-release-41.html</guid>
	<link>https://nuitka.net/posts/nuitka-release-41.html</link>
	<description>&lt;p&gt;This is to inform you about the new stable release of &lt;a class=&quot;reference external&quot; href=&quot;https://nuitka.net&quot;&gt;Nuitka&lt;/a&gt;. It is the extremely compatible Python compiler,
&lt;a class=&quot;reference external&quot; href=&quot;https://nuitka.net/doc/download.html&quot;&gt;“download now”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This release adds many new features and corrections with a focus on
async code compatibility, missing generics features, and Python 3.14
compatibility and Python compilation scalability yet again.&lt;/p&gt;

&lt;h2&gt;Bug Fixes&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Fix, decorators were breaking when disabling
deferred annotations. (Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, nested loops could have wrong traces lead to mis-optimization.
(Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Fix, run-time check of package configuration was
incorrect. (Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compatibility:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;__builtins__&lt;/span&gt;&lt;/code&gt; lacked necessary
compatibility in compiled functions. (Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distutils:&lt;/strong&gt; Fix, incorrect UTF-8 decoding was used for TOML input
file parsing. (Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, multiple hard value assignments could cause compile time
crashes. (Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, string concatenation was not properly annotating exception
exits. (Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--verbose-output&lt;/span&gt;&lt;/code&gt; and &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--show-modules-output&lt;/span&gt;&lt;/code&gt;
did not work with forward slashes. (Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Fix, there were various compatibility issues
including dictionary watchers and inline values. (Fixed in 4.0.2
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Fix, stack pointer initialization to &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;localsplus&lt;/span&gt;&lt;/code&gt;
was incorrect to avoid garbage collection issues. (Fixed in 4.0.2
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12+:&lt;/strong&gt; Fix, generic type variable scoping in classes was
incorrect. (Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12+:&lt;/strong&gt; Fix, there were various issues with function
generics. (Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.8+:&lt;/strong&gt; Fix, names in named expressions were not mangled.
(Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Fix, module checksums were not robust against quoting
style of module-name entry in YAML configurations. (Fixed in 4.0.2
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Fix, doing imports in queried expressions caused
corruption. (Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Fix, support for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;uv_build&lt;/span&gt;&lt;/code&gt; in the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--project&lt;/span&gt;&lt;/code&gt; option was
broken. (Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compatibility:&lt;/strong&gt; Fix, names assigned in assignment expressions were
not mangled. (Fixed in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12+:&lt;/strong&gt; Fix, there were still various issues with function
generics. (Fixed in 4.0.3 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clang:&lt;/strong&gt; Fix, debug mode was disabled for clang generally, but only
ClangCL and macOS Clang didn’t want it. (Fixed in 4.0.3 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zig:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--windows-console-mode=attach|disable&lt;/span&gt;&lt;/code&gt; was not
working when using Zig. (Fixed in 4.0.3 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Fix, yet another way self dependencies can look like,
needed to have support added. (Fixed in 4.0.3 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12+:&lt;/strong&gt; Fix, generic types in classes had bugs with
multiple type variables. (Fixed in 4.0.3 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scons:&lt;/strong&gt; Fix, repeated builds were not producing binary identical
results. (Fixed in 4.0.3 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scons:&lt;/strong&gt; Fix, compiling with newer Python versions did not fall
back to Zig when the developer prompt MSVC was unusable, and error
reporting could crash. (Fixed in 4.0.4 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zig:&lt;/strong&gt; Fix, the workaround for Windows console mode &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;attach&lt;/span&gt;&lt;/code&gt; or
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;disable&lt;/span&gt;&lt;/code&gt; was incorrectly applied on non-Windows platforms. (Fixed
in 4.0.4 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Fix, linking with Python Build Standalone failed
because &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;libHacl_Hash_SHA2&lt;/span&gt;&lt;/code&gt; was not filtered out unconditionally.
(Fixed in 4.0.4 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.6+:&lt;/strong&gt; Fix, exceptions like &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;CancelledError&lt;/span&gt;&lt;/code&gt; thrown into
an async generator awaiting an inner awaitable could be swallowed,
causing crashes. (Fixed in 4.0.4 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, not all ordered set modules accepted generators for update.
(Fixed in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Disabled warning about rebuilding the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pytokens&lt;/span&gt;&lt;/code&gt;
extension module. (Fixed in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Filtered &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;libHacl_Hash_SHA2&lt;/span&gt;&lt;/code&gt; from link libs
unconditionally. (Fixed in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Disabled unusable unicode consistency checks for
Python versions 3.4 to 3.6. (Fixed in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python3.12+&lt;/strong&gt; Avoided cloning call nodes on class level which
caused issues with generic functions in combination with decorators.
(Added in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12+:&lt;/strong&gt; Added support for generic type variables in &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;async&lt;/span&gt;
&lt;span class=&quot;pre&quot;&gt;def&lt;/span&gt;&lt;/code&gt; functions. (Added in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Fix, flushing outputs for prompts was not working in all
cases when progress bars were enabled. (Fixed in 4.0.6 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Fix, unused variable warnings were missing at C compile time
when using &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;zig&lt;/span&gt;&lt;/code&gt; as a C compiler. (Fixed in 4.0.6 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scons:&lt;/strong&gt; Fix, forced stdout and stderr paths as a feature was
broken. (Fixed in 4.0.6 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, replacing a branch did not accurately track shared active
variables causing optimization crashes. (Fixed in 4.0.7 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Fix, failed to remove extended attributes because files
need to be made writable first. (Fixed in 4.0.7 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, dict &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pop&lt;/span&gt;&lt;/code&gt; and &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;setdefault&lt;/span&gt;&lt;/code&gt; using with &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;:=&lt;/span&gt;&lt;/code&gt; rewrites
lacked exception-exit annotations for un-hashable keys. (Fixed in
4.0.8 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.13:&lt;/strong&gt; Fix, the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;__parameters__&lt;/span&gt;&lt;/code&gt; attribute of generic
classes was not working. (Fixed in 4.0.8 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.11+:&lt;/strong&gt; Fix, starred arguments were not working as type
variables. (Fixed in 4.0.8 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python2:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;FileNotFoundError&lt;/span&gt;&lt;/code&gt; compatibility fallback
handling was not working properly. (Fixed in 4.0.8 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compatibility:&lt;/strong&gt; Fix, loop ownership check in value traces was
missing, causing issues with nested loops.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt; Improved &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--windows-console-mode=attach&lt;/span&gt;&lt;/code&gt; to properly
handle console handles, enabling cases like &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;os.system&lt;/span&gt;&lt;/code&gt; to work
nicely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python2:&lt;/strong&gt; Fix, there was a compatibility issue where providing
default values to the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;mkdtemp&lt;/span&gt;&lt;/code&gt; function was failing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt; Fix, there were spurious issues with C23 embedding in
32-bit MinGW64 by switching to &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;coff_obj&lt;/span&gt;&lt;/code&gt; resource mode for it as
well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Fix, the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;post-import-code&lt;/span&gt;&lt;/code&gt; execution could fail
because the triggering sub-package was not yet available in
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;sys.modules&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Fix, listing package DLLs with &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--list-package-dlls&lt;/span&gt;&lt;/code&gt; was
broken due to recent plugin lifecycle changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--list-package-exe&lt;/span&gt;&lt;/code&gt; was not working properly on
non-Windows platforms failing to detect executable files correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Handled paths starting with &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;{PROGRAM_DIR}&lt;/span&gt;&lt;/code&gt; the same as a
relative path when parsing the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--onefile-tempdir-spec&lt;/span&gt;&lt;/code&gt; option.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Followed multiprocessing &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;forkserver&lt;/span&gt;&lt;/code&gt; changes for
newer Python versions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12+:&lt;/strong&gt; Fix, generic class type parameters handling was
incorrect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12:&lt;/strong&gt; Fix, deferred evaluation of type aliases was
failing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.12+:&lt;/strong&gt; Aligned &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;sum&lt;/span&gt;&lt;/code&gt; built-in float summation with
CPython’s compensated sum for better accuracy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.10+:&lt;/strong&gt; Fix, uncompiled coroutine &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;throw()&lt;/span&gt;&lt;/code&gt; return
handling was incorrect, restoring completed coroutine results via
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;StopIteration.value&lt;/span&gt;&lt;/code&gt; rather than exposing them as ordinary return
values to the outer await chain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.13+:&lt;/strong&gt; Fix, uncompiled coroutine &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;cancel()/await&lt;/span&gt;&lt;/code&gt;
suspension handling was incorrect, improved to ensure integration
compatibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Made finding &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;create-dmg&lt;/span&gt;&lt;/code&gt; more robustly by also checking
the Homebrew path for Intel and from &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;PATH&lt;/span&gt;&lt;/code&gt; properly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compatibility:&lt;/strong&gt; Fix, class frames were not exposing frame locals.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Detected &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;static-libpython&lt;/span&gt;&lt;/code&gt; problems, which affected some
forms of Anaconda.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distutils:&lt;/strong&gt; Rejected &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--project&lt;/span&gt;&lt;/code&gt; mixed with &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--main&lt;/span&gt;&lt;/code&gt; arguments
as it is not useful.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;zig&lt;/span&gt;&lt;/code&gt; from &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;PATH&lt;/span&gt;&lt;/code&gt; or from &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;ziglang&lt;/span&gt;&lt;/code&gt; was not
being used.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distutils:&lt;/strong&gt; Fix, the wrong &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;module-root&lt;/span&gt;&lt;/code&gt; config value was being
checked for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;uv&lt;/span&gt;&lt;/code&gt; build backend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Fix, was attempting to change removed (rejected) DLLs,
which of course failed and errored out.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Fix, tuple reuse was not fully compatible,
potentially causing crashes due to outdated hash caches.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, fake modules were still being attempted to located when imported
by other code, which could conflict with existing modules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.5+:&lt;/strong&gt; Fix, failed to send uncompiled coroutines the sent
in value in &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;pre&quot;&gt;from&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, older &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;gcc&lt;/span&gt;&lt;/code&gt; compilers lacking newer intrinsic methods had
compilation issues that needed to be addressed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Fix, multiphase module extension modules with
post-load code were not working properly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, Avoid using the non-inline copy of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pkg_resources&lt;/span&gt;&lt;/code&gt; with the
inline copy of Jinja2. These could mismatch and cause errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, loops could make releasing of previous values very unclear,
causing optimization errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;incbin&lt;/span&gt;&lt;/code&gt; resource mode was not working with old &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;gcc&lt;/span&gt;&lt;/code&gt; C++
fallback.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.4 to 3.6:&lt;/strong&gt; Fix, bytecode demotion was not working
properly for these versions, also bytecode only files not working.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Added a check for the broken &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;patchelf&lt;/span&gt;&lt;/code&gt; versions 0.10
and 0.11 to prevent breaking Qt plugins.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Android:&lt;/strong&gt; Allowed &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;patchelf&lt;/span&gt;&lt;/code&gt; version 0.18 on Android.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt; Fix, the header path for self uninstalled Python was not
detected correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; Fix, inclusion of the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pkg_resources&lt;/span&gt;&lt;/code&gt; inline copy for
Python 2 to source distributions was missing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Detected the OBS versions of SUSE Linux better.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Suse:&lt;/strong&gt; Allowed using &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;patchelf&lt;/span&gt;&lt;/code&gt; 0.18.0 there too.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.11:&lt;/strong&gt; Fix, package and module dicts were not aligned close
enough to avoid a CPython bug.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, unbound compiled methods could crash when called without an
object passed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Fix, multiphase module extension modules with
postload. (Fixed in 4.0.8 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Onefile:&lt;/strong&gt; Fix, while waiting for the child, it may already be
terminated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Removed existing absolute rpaths for Homebrew and
MacPorts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Avoided warning in CPython headers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Followed allocator changes more closely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compatibility:&lt;/strong&gt; Avoided using &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pkg_resources&lt;/span&gt;&lt;/code&gt; for Jinja2
template location for loading.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No-GIL:&lt;/strong&gt; Applied some bug fixes to get basic things to work.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Package Support&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Add support for newer &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;paddle&lt;/span&gt;&lt;/code&gt; version. (Added in
4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Add workaround for refcount checks of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pandas&lt;/span&gt;&lt;/code&gt;.
(Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Add support for newer &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;h5py&lt;/span&gt;&lt;/code&gt; version. (Added in
4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Add support for newer &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;scipy&lt;/span&gt;&lt;/code&gt; package. (Added in
4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Revert accidental &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;os.getenv&lt;/span&gt;&lt;/code&gt; over &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;os.environ.get&lt;/span&gt;&lt;/code&gt;
changes in anti-bloat configurations that stopped them from working.
Affected packages are &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;networkx&lt;/span&gt;&lt;/code&gt;, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;persistent&lt;/span&gt;&lt;/code&gt;, and
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;tensorflow&lt;/span&gt;&lt;/code&gt;. (Fixed in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Added missing DLLs for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;openvino&lt;/span&gt;&lt;/code&gt;. (Added in 4.0.7
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enhanced the package configuration YAML schema by adding the
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;relative_to&lt;/span&gt;&lt;/code&gt; parameter for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;from_filenames&lt;/span&gt;&lt;/code&gt; DLL specification,
avoiding error-prone purely relative paths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;flet_desktop&lt;/span&gt;&lt;/code&gt; app assets were missing, now
preserving the packaged runtime and sidecar DLLs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Added support for the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;tyro&lt;/span&gt;&lt;/code&gt; package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Added data files for the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;perfetto&lt;/span&gt;&lt;/code&gt; package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Added support for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;anyio&lt;/span&gt;&lt;/code&gt; process forking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Added support for the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;plotly.graph&lt;/span&gt;&lt;/code&gt; package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anaconda:&lt;/strong&gt; Fix, dependencies for the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;numpy&lt;/span&gt;&lt;/code&gt; conda package on
Windows were incorrect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Enhanced the auto-icon hack in PySide6 to use compatible
class names.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standalone:&lt;/strong&gt; Fix, Qt libraries were duplicated with &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;PySide6&lt;/span&gt;&lt;/code&gt;
WebEngine framework support on macOS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Fix, automatic detection of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;mypyc&lt;/span&gt;&lt;/code&gt; runtime
dependencies was including all top level modules of the containing
package by accident. (Fixed in 4.0.5 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anaconda:&lt;/strong&gt; Fix, &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;delvewheel&lt;/span&gt;&lt;/code&gt; plugin was not working with Python
3.8+. This enhances compatibility with installed PyPI packages that
use it for their DLLs. (Fixed in 4.0.6 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Fix, our protection workaround could confuse methods
used with &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;PySide6&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;New Features&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Added the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--recommended-python-version&lt;/span&gt;&lt;/code&gt; option to display
recommended Python versions for supported, working, or commercial
usage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Add message to inform users about &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;Nuitka[onefile]&lt;/span&gt;&lt;/code&gt; if
compression is not installed. (Added in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Add support for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;uv_build&lt;/span&gt;&lt;/code&gt; in the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--project&lt;/span&gt;&lt;/code&gt; option.
(Added in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Onefile:&lt;/strong&gt; Allow extra includes as well. (Added in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Add &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;nuitka-project-set&lt;/span&gt;&lt;/code&gt; feature to define project
variables, checking for collisions with reserved runtime variables.
(Added in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scons:&lt;/strong&gt; Added new option to select &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--reproducible&lt;/span&gt;&lt;/code&gt; builds or
not. (Added in 4.0.6 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.10+:&lt;/strong&gt; Added support for
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;importlib.metadata.package_distributions()&lt;/span&gt;&lt;/code&gt;. (Added in 4.0.8
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Added support for the multiprocessing &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;forkserver&lt;/span&gt;&lt;/code&gt;
context. (Added in 4.0.8 already, for 4.1 Python 3.6 and earlier, as
well as 3.14 support were added too.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reports:&lt;/strong&gt; Added structured resource usage (&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;rusage&lt;/span&gt;&lt;/code&gt;) performance
information to compilation reports.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reports:&lt;/strong&gt; Included individual module-level C compiler caching
(&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;ccache&lt;/span&gt;&lt;/code&gt;/&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;clcache&lt;/span&gt;&lt;/code&gt;) statistics in compilation reports.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added support for detecting and correctly resolving the Python prefix
for the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;PyEnv&lt;/span&gt; &lt;span class=&quot;pre&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;pre&quot;&gt;Homebrew&lt;/span&gt;&lt;/code&gt; Python flavor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Added support for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;rusage&lt;/span&gt;&lt;/code&gt; information for Scons.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Added the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;__compiled__.extension_filename&lt;/span&gt;&lt;/code&gt; attribute to
give the real filename of the containing extension module.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt; Added support for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--clang&lt;/span&gt;&lt;/code&gt; or ARM. (Added in 4.0.8
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt; Added support for resources names as not just integers,
important when we copy them from template files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MacPorts:&lt;/strong&gt; Added basic support for this Python flavor. More work
will be needed to get it to work fully though.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Optimization&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;Avoid including &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;importlib._bootstrap&lt;/span&gt;&lt;/code&gt; and
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;importlib._bootstrap_external&lt;/span&gt;&lt;/code&gt;. (Added in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linux:&lt;/strong&gt; Cached the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;syscall&lt;/span&gt;&lt;/code&gt; used for time keeping during
compilation to avoid loading &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;libc&lt;/span&gt;&lt;/code&gt; for each trace. (Added in 4.0.8
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Output a warning for modules that remain unfinished after the
third optimization pass.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added an extra micro pass trigger when new variables are introduced
or variable usage changes severely, ensuring optimizations are fully
propagated, avoiding unnecessary extra full passes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provided scripts to compile Python statically with PGO tailored for
Nuitka on Linux, Windows, and macOS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added support for running the Data Composer tool from a compiled
Nuitka binary without spawning an uncompiled Python process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enhanced the usage of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;vectorcall&lt;/span&gt;&lt;/code&gt; for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;PyCFunction&lt;/span&gt;&lt;/code&gt; objects by
directly checking for its presence instead of relying purely on
flags, allowing more frequent use of this faster execution path.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cached frequently used declarations for top-level variables to speed
up C code generation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sped up trace collection merging by avoiding unnecessary set creation
and using a set instead of a list for escaped traces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimized plugin hook execution by tracking overloaded methods and
added an option to show plugin usage statistics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved performance of module location by avoiding unnecessary
module name reconstruction and redundant filesystem checks for
pre-loaded packages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved the caching of distribution name lookups to effectively
avoid repeated IO operations across all package types.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; Cached callback plugin dispatch for
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;onFunctionBodyParsing&lt;/span&gt;&lt;/code&gt; and &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;onClassBodyParsing&lt;/span&gt;&lt;/code&gt; to skip argument
computation when no plugin overrides them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.13:&lt;/strong&gt; Handled sub-packages of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pathlib&lt;/span&gt;&lt;/code&gt; as hard modules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handled hard attributes through merge traces as well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Made constant blobs more compact by avoiding repeated identifiers and
unnecessary fields.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enhanced Python compilation scripts further. (Fixed in 4.0.8
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Recognized late incomplete variables better. (Fixed in 4.0.8
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Made constant blobs more compact. (Fixed in 4.0.8 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimized calls with only constant keywords and variable posargs too.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Anti-Bloat&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;Fix, memory bloat occurred when C compiling &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;sqlalchemy&lt;/span&gt;&lt;/code&gt;. (Fixed in
4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid using &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pydoc&lt;/span&gt;&lt;/code&gt; in &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;PySimpleGUI&lt;/span&gt;&lt;/code&gt;. (Added in 4.0.2 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoided using &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;doctest&lt;/span&gt;&lt;/code&gt; from &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;zodbpickle&lt;/span&gt;&lt;/code&gt;. (Added in 4.0.5
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoided inclusion of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;cython&lt;/span&gt;&lt;/code&gt; when using &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;pyav&lt;/span&gt;&lt;/code&gt;. (Added in 4.0.7
already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoided including &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;typing_extensions&lt;/span&gt;&lt;/code&gt; when using &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;numpy&lt;/span&gt;&lt;/code&gt;. (Added
in 4.0.7 already.)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Organizational&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Relocated the warning about the available source code of
extension modules to be evaluated at a more appropriate time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debian:&lt;/strong&gt; Remove recommendation for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;libfuse2&lt;/span&gt;&lt;/code&gt; package as it is
no longer useful.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debian:&lt;/strong&gt; Used &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;platformdirs&lt;/span&gt;&lt;/code&gt; instead of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;appdirs&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Removed Python 3.11+ restriction for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;clang-format&lt;/span&gt;&lt;/code&gt;
as it is available everywhere, even Python 2.7, and we still want
nicely formatted code when we read things. (Added in 4.0.6 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Removed no longer useful inline copy of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;wax_off&lt;/span&gt;&lt;/code&gt;. We have our own
stubs generator project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; Added missing package to the CI container for building
Nuitka Debian packages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Developer:&lt;/strong&gt; Updated AI instructions for creating Minimal
Reproducible Examples (MRE) to skip unneeded C compilation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Added an internal function for checking if a string is
a valid Python identifier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI:&lt;/strong&gt; Added a task in Visual Studio Code to export the currently
selected Python interpreter path to a file, making it available as
“python” and “pip” matching the selected interpreter. This makes it
easier to use a specific version with no instructions needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI:&lt;/strong&gt; Updated the rules to instruct AI to only generate useful
comments that add context not present in the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Containers:&lt;/strong&gt; Added template rendering support for Jinja2 (&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;.j2&lt;/span&gt;&lt;/code&gt;)
container files in our internal Podman tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Projects:&lt;/strong&gt; Clarified the current status and rationale of Python
2.6 support in the developer manual.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Added experimental flag
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--experimental=ignore-extra-micro-pass&lt;/span&gt;&lt;/code&gt; to allow ignoring extra
micro pass detection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Visual Code:&lt;/strong&gt; Added integration scripts for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;bash&lt;/span&gt;&lt;/code&gt; and &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;zsh&lt;/span&gt;&lt;/code&gt;
autocompletion of Nuitka CLI options. These are now also integrated
into Visual Studio Code terminal profiles and the Debian package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RPM:&lt;/strong&gt; Included the Python compile script for Linux.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RPM:&lt;/strong&gt; Removed the requirement for &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;distutils&lt;/span&gt;&lt;/code&gt; in the spec.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Tests&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;Install only necessary build tools for test cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoided spurious failures in reference counting tests due to Python
internal caching differences. (Fixed in 4.0.3 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, the parsing of the compilation report for reflected tests was
incorrect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Ignored a syntax error message change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python 3.14:&lt;/strong&gt; Added test execution support options to the main
test runner to use this version as well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, the runner binary path was mishandled for the third pass of
reflected compilations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Removed the usage of obsolete plugins in reflected compilation tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Prevented boolean testing of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;namedtuples&lt;/span&gt;&lt;/code&gt; to avoid
unexpected bugs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;Test&lt;/span&gt;&lt;/code&gt; suffix to syntax test files and disabled “python”
mode and spell checking for them to resolve issues reported in IDEs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix, newline handling in diff outputs from the output comparison tool
was incorrect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Covered &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;post-import-code&lt;/span&gt;&lt;/code&gt; functionality with a new subpackage test
case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prevented the program test suite from running an unnecessary variant
to save execution time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Ignored differences from GUI framework error traces in
headless runs in output comparisons.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reflected test for Nuitka, where it compiles itself and compares its
operation has been restored to functional state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Used the new method to clear internal caches if available for
reference counts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disabled running nested loops test with Python 2.6.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Containers:&lt;/strong&gt; Detected Python 2 defaulting containers in Podman
tooling.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Cleanups&lt;/h2&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Fix, there was a double space in the Windows Runtime DLLs
inclusion message. (Fixed in 4.0.1 already.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Onefile:&lt;/strong&gt; Separated files and defines for extra includes for
onefile boot and Python build.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scons:&lt;/strong&gt; Provided nicer errors in case of “unset” variables being
used, so we can tell it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refactored the process execution results to correctly utilize our
&lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;namedtuples&lt;/span&gt;&lt;/code&gt; variant, that makes it easier to understand what code
does with the results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Enabled automatic conversion of em-dashes and en-dashes
in code comments to the autoformat tool. AI won’t stop producing them
and they can cause &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;SyntaxError&lt;/span&gt;&lt;/code&gt; for older Python versions, nor is
unnecessarily using UTF-8 welcome.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensured that cloned outline nodes are assigned their correct names
immediately upon creation, that avoids inconsistencies during their
creation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Updated to the latest versions of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;black&lt;/span&gt;&lt;/code&gt; and adopted
a faster &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;isort&lt;/span&gt;&lt;/code&gt; execution by caching results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Modified the PyLint wrapper to exit gracefully instead
of raising an error when no matching files require checking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Avoided checking YAML package configuration files twice,
since autoformat already handles them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Ensured that YAML package configuration checks output
the original filename instead of the temporary one when a failure
occurs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Prevented pushing of tags from triggering git pre-push
quality checks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Silenced the output of &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;optipng&lt;/span&gt;&lt;/code&gt; and &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;jpegoptim&lt;/span&gt;&lt;/code&gt;
during image optimization auto-formatting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Visual Code:&lt;/strong&gt; Added the generated Python alias path file to the
ignore list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Enabled auto-formatting for the Nuitka devcontainer
configuration file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Watch:&lt;/strong&gt; Avoided absolute paths in compilation to make reports more
comparable across machines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Changed &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;mdformat&lt;/span&gt;&lt;/code&gt; checks to run only once and
silently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scons:&lt;/strong&gt; Disabled format security errors in debug mode and moved
Python-related warning disables into common build setup code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality:&lt;/strong&gt; Updated to the latest &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;deepdiff&lt;/span&gt;&lt;/code&gt; version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scons:&lt;/strong&gt; Avoided MSVC telemetry since it can produce outputs that
break CI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Enhanced non-deployment handler for importing excluded
modules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Split import module finding functionality into more pieces for
enhanced readability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Added more assertions for constants loading and
checking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Dropped the &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;universal&lt;/span&gt;&lt;/code&gt; target arch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging:&lt;/strong&gt; Added more traces for deep hash verification.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This release builds on the scalability improvements established in 4.0,
with enhanced Python 3.14 support, expanded package compatibility, and
significant optimization work.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;docutils literal notranslate&quot;&gt;&lt;span class=&quot;pre&quot;&gt;--project&lt;/span&gt;&lt;/code&gt; option seems usable now.&lt;/p&gt;
&lt;p&gt;Python 3.14 support remains experimental, but only barely made the cut,
and probably will get there in hotfixes. Some of the corrections came in
so late before the release, that it was just not possible to feel good
about declaring it fully supported just yet.&lt;/p&gt;</description>
	<pubDate>Sat, 16 May 2026 22:00:00 +0000</pubDate>
</item>
<item>
	<title>PyCon: Welcome Back, NVIDIA: Visionary Sponsor of PyCon US 2026</title>
	<guid>https://pycon.blogspot.com/2026/05/welcome-back-nvidia-visionary-sponsor.html</guid>
	<link>https://pycon.blogspot.com/2026/05/welcome-back-nvidia-visionary-sponsor.html</link>
	<description>&lt;p&gt;NVIDIA is excited to once again support PyCon US 2026 as a &lt;a href=&quot;https://us.pycon.org/2026/sponsorship/sponsors/&quot; target=&quot;_blank&quot;&gt;Visionary Sponsor&lt;/a&gt;, and to sponsor the &lt;a href=&quot;https://us.pycon.org/2026/tracks/ai/&quot; target=&quot;_blank&quot;&gt;Future of AI with Python Conference Track&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Python is a “first-class” language at NVIDIA CUDA, and NVIDIA is committed to bringing our technology to Python developers in close alignment with C++ upon new releases of our hardware. We’re also happy to announce the general availability of &lt;a href=&quot;https://github.com/nvidia/cuda-python&quot; target=&quot;_blank&quot;&gt;CUDA Python 1.0&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;NVIDIA’s commitment to Python goes well beyond just our own tech stack. NVIDIA’s Python engineers contribute across a broad swath of the Python ecosystem, from the core interpreter itself, to packaging and PyPI, to the Python community at large. NVIDIA is inspired by the energy of, and privileged to collaborate with, people across the open source Python community.&lt;br /&gt;&lt;br /&gt;Since PyCon last year, NVIDIA Pythonistas – in collaboration with many others in the Python community – have made great progress on the evolution of various packaging standards, including working with community partners on the implementation of &lt;a href=&quot;https://peps.python.org/pep-0825/&quot; target=&quot;_blank&quot;&gt;wheel variants&lt;/a&gt; and the &lt;a href=&quot;https://peps.python.org/pep-0772/&quot; target=&quot;_blank&quot;&gt;establishment of a Packaging Council&lt;/a&gt; to better govern the evolution of packaging standards and &lt;a href=&quot;https://pypi.org/&quot; target=&quot;_blank&quot;&gt;PyPI&lt;/a&gt;. NVIDIA Python engineers are also engaged in implementation, testing, and porting work for the &lt;a href=&quot;https://docs.python.org/3/howto/free-threading-python.html&quot; target=&quot;_blank&quot;&gt;free-threaded&lt;/a&gt; build of the interpreter. NVIDIA Python engineers are driving the early exploratory work for adopting Rust for CPython, work on Python performance benchmarking, and are actively involved in many enhancements for Python 3.14 and 3.15, including providing built-in &lt;a href=&quot;https://peps.python.org/pep-0784/&quot; target=&quot;_blank&quot;&gt;Zstandard support in Python 3.14&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;At NVIDIA, we are excited to work with our partners and the open source Python community to help bring the best developer experience for users of high performance computing and AI. Come see NVIDIA at the &lt;a href=&quot;https://pycon-assets.s3.amazonaws.com/2026/media/documents/floor_plan.png&quot; target=&quot;_blank&quot;&gt;Anaconda and PyTorch booths&lt;/a&gt;, and at the AI Track.&lt;br /&gt;&lt;br /&gt;Barry Warsaw&lt;br /&gt;May 2026&lt;br /&gt;Principal System Software Engineer, NVIDIA&lt;br /&gt;Python Core Developer since 1994&lt;br /&gt;Python Steering Council member in 2026&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
	<pubDate>Sat, 16 May 2026 14:30:37 +0000</pubDate>
</item>
<item>
	<title>Anarcat: The Four Horsemen of the LLM Apocalypse</title>
	<guid>https://anarc.at/blog/2026-05-16-four-horsemen/</guid>
	<link>https://anarc.at/blog/2026-05-16-four-horsemen/</link>
	<description>&lt;p&gt;I have been battling Large Language Models (LLM&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;https://anarc.at/tag/python-planet/#fn:1&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;) for the past
couple of weeks and have struggled to think about what it means and
how to deal with its fallout.&lt;/p&gt;

&lt;p&gt;Because the fight has come from many fronts, I've come to articulate
this in terms of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Four_Horsemen_of_the_Apocalypse&quot;&gt;Four Horsemen of the Apocalypse&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Sound track: Metallica's &lt;a href=&quot;https://www.metallica.com/songs/the-four-horsemen.html&quot;&gt;The Four Horsemen&lt;/a&gt;, preferably
&lt;a href=&quot;https://en.wikipedia.org/wiki/Metallica_v._Napster,_Inc.&quot;&gt;downloaded from Napster around 2000&lt;/a&gt;, but now I guess you &lt;a href=&quot;https://www.youtube.com/watch?v=-zKOhVSERS8&quot;&gt;get
it on YouTube&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;

&lt;h1 id=&quot;war-bot-armies&quot;&gt;War: bot armies&lt;/h1&gt;

&lt;p&gt;Let's start with War. We've been battling bot armies for control of
our GitLab server &lt;a href=&quot;https://gitlab.torproject.org/tpo/tpa/team/-/work_items/42152&quot;&gt;for a while&lt;/a&gt;. Bots crawl virtually infinite
endpoints on our Git repositories (as opposed to downloading an
archive or shallow clone), including our fork of Firefox, Tor Browser,
a massive repository.&lt;/p&gt;

&lt;p&gt;At first, we've tried various methods: &lt;a href=&quot;https://www.robotstxt.org/&quot;&gt;robots.txt&lt;/a&gt;, blocking user
agents, and finally blocking entire networks. I &lt;a href=&quot;https://anarc.at/blog/2025-05-30-asncounter/&quot;&gt;wrote
asncounter&lt;/a&gt;. It worked for a while.&lt;/p&gt;

&lt;p&gt;But now, blocking entire networks doesn't work: they come back some
other way, typically through &lt;a href=&quot;https://acid.vegas/blog/the-shady-world-of-ip-leasing/&quot;&gt;shady proxy networks&lt;/a&gt;, which is kind
of ironic considering we're essentially running the largest proxy
network of the world.&lt;/p&gt;

&lt;p&gt;Out of desperation, we've forced users to &lt;a href=&quot;https://gitlab.torproject.org/tpo/tpa/team/-/wikis/policy/0108-gitlab-cookie-and-javascript-enforcement&quot;&gt;use cookies&lt;/a&gt; when
visiting our site. We haven't deployed &lt;a href=&quot;https://anubis.techaro.lol/&quot;&gt;Anubis&lt;/a&gt; yet, as we worry
that &lt;a href=&quot;https://social.anoxinon.de/@Codeberg/115033790447125787&quot;&gt;bots have broken Anubis anyways&lt;/a&gt; and that it &lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html&quot;&gt;does not really
defend against a well-funded attacker&lt;/a&gt;, something which &lt;a href=&quot;https://behind.pretix.eu/2025/05/23/captchas-are-over/&quot;&gt;Pretix
warned against in 2025 already&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(We have a whole &lt;a href=&quot;https://gitlab.torproject.org/tpo/tpa/team/-/work_items/42229&quot;&gt;discussion regarding those tools here&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;But even that, predictably, has failed. I suspect what we consider
bots are now really agents. They run full web browsers, JavaScript
included, so a feeble cookie is no match for the massive bot armies.&lt;/p&gt;

&lt;h2 id=&quot;side-note-on-llm-order-of-battle&quot;&gt;Side note on LLM &quot;order of battle&quot;&lt;/h2&gt;

&lt;p&gt;We often underestimate the size of that army. The cloud was huge even
before LLMs, serving about two thirds of the web. Even larger swaths of
clients like government and corporate databases have all moved to the
cloud, in shared, but private infrastructure with massive spare
capacity that is readily available to anyone who pays.&lt;/p&gt;

&lt;p&gt;LLMs have made the problem worse by dramatically expanding the
capacity of the &quot;cloud&quot;. We now have data centers that defy
imagination with &lt;a href=&quot;https://epoch.ai/data/data-centers&quot;&gt;millions of cores&lt;/a&gt;, petabytes of memory, exabytes
of storage.&lt;/p&gt;

&lt;p&gt;I thought that &lt;a href=&quot;https://sschueller.github.io/posts/the-free-market-lie/&quot;&gt;25 gigabit residential internet in Switzerland&lt;/a&gt;
could bring balance, but this is nothing compared to the scale of
those data centers.&lt;/p&gt;

&lt;p&gt;Those companies can launch thousands, if not millions of fully
functional web browsers at our servers. Computing power or bandwidth
are not a limitation for them, our primitive infrastructure is. No one
but hyperscalers can deal with this kind of load, and I suspect that
they are also struggling, as even &lt;a href=&quot;https://www.androidauthority.com/google-recaptcha-play-services-requirement-3664806/&quot;&gt;Google is deploying extreme
mechanisms in reCAPTCHA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the largest attack on the internet since the &lt;a href=&quot;https://en.wikipedia.org/wiki/Morris_worm&quot;&gt;Morris
worm&lt;/a&gt; but while &lt;a href=&quot;https://en.wikipedia.org/wiki/Robert_Tappan_Morris&quot;&gt;Robert Tappan Morris&lt;/a&gt; went to jail on a felony,
LLM companies are celebrated as innovators and will soon be too big to
fail.&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;https://anarc.at/tag/python-planet/#fn:2&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Which brings us to the second horsemen, famine.&lt;/p&gt;

&lt;h1 id=&quot;famine-shortages&quot;&gt;Famine: shortages&lt;/h1&gt;

&lt;p&gt;All that computing power doesn't come out of thin air: it needs
massive amounts of hardware, power, and cooling.&lt;/p&gt;

&lt;p&gt;Earlier this year, I've heard from a colleague that their Dell
supplier refused to even provide a &lt;em&gt;quote&lt;/em&gt; before August. Dell!&lt;/p&gt;

&lt;p&gt;In February, &lt;a href=&quot;https://www.techspot.com/news/111346-western-digital-hdd-production-capacity-2026-already-sold.html&quot;&gt;Western Digital's hard drive production for 2026 was
already sold out&lt;/a&gt;. Hard drives essentially &lt;a href=&quot;https://gitlab.torproject.org/tpo/tpa/team/-/work_items/42465&quot;&gt;doubled in price within
a year&lt;/a&gt;, and some have now tripled. A server quote we had in
November has now &lt;em&gt;quadrupled&lt;/em&gt;, going from 10 thousand to &lt;em&gt;FORTY&lt;/em&gt;
thousand dollars for a single server.&lt;/p&gt;

&lt;p&gt;But regular folks are facing real-life shortages as well, as
&lt;a href=&quot;https://www.theguardian.com/us-news/2026/may/13/utah-approves-datacenter-backlash&quot;&gt;city-size data centers&lt;/a&gt; are being built at neck-breaking speed,
stealing fresh water and energy from human beings to feed the war
machine.&lt;/p&gt;

&lt;p&gt;We've been scared of losing our jobs, but it seems that Apocalypse has
yet to fully materialize. Regardless for engineers, the market feels
tighter than it was a couple years ago, and everyone feels on edge
that they will just have to learn to operate LLMs to keep their jobs.&lt;/p&gt;

&lt;p&gt;Which brings us, of course, to Death.&lt;/p&gt;

&lt;h1 id=&quot;death-security-and-copyright&quot;&gt;Death: security and copyright&lt;/h1&gt;

&lt;p&gt;Our third horseman is one I did &lt;em&gt;not&lt;/em&gt; expect a couple of months
ago. Back at FOSDEM, &lt;code&gt;curl&lt;/code&gt;'s maintainer Daniel Stenberg famously
&lt;a href=&quot;https://lwn.net/Articles/1058266/&quot;&gt;complained about the poor quality of LLM-generated reports&lt;/a&gt; but
then, a few months later, everyone is &lt;a href=&quot;https://lwn.net/Articles/1066581/&quot;&gt;scrambling to deal with floods
of good reports&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the past two weeks, this culminated in a significant number of
critical security issues across multiple projects. Chained
together, remote code execution vulnerabilities in &lt;a href=&quot;https://depthfirst.com/nginx-rift&quot;&gt;Nginx&lt;/a&gt; and
&lt;a href=&quot;https://www.cve.org/CVERecord?id=CVE-2026-23918&quot;&gt;Apache&lt;/a&gt; and &lt;em&gt;two&lt;/em&gt; local privilege escalations in the Linux kernel
(&lt;a href=&quot;https://github.com/V4bel/dirtyfrag/&quot;&gt;dirtyfrag&lt;/a&gt; and &lt;a href=&quot;https://github.com/v12-security/pocs/tree/main/fragnesia#fragnesia&quot;&gt;fragnesia&lt;/a&gt;) essentially gave anyone root access to any unpatched server to the web.&lt;/p&gt;

&lt;p&gt;As I write this, &lt;a href=&quot;https://github.com/0xdeadbeefnetwork/ssh-keysign-pwn&quot;&gt;another vulnerability dropped&lt;/a&gt;, which gives read
access to any file to a local user, compromising TLS and SSH private
keys.&lt;/p&gt;

&lt;p&gt;All those vulnerabilities were released without any significant
coordination while people scrambled to mitigate.&lt;/p&gt;

&lt;p&gt;Many people &lt;a href=&quot;https://lwn.net/Articles/1072007/#Comments&quot;&gt;including Linus Torvalds&lt;/a&gt; are now considering issues
discovered through LLMs to be essentially public. This puts &lt;a href=&quot;https://lwn.net/Articles/1071499/&quot;&gt;some
debates about disclosure processes&lt;/a&gt; in perspective, to say the
least.&lt;/p&gt;

&lt;p&gt;But this is not merely the death of the traditional coordinated disclosure
process, the C programming language, or the Linux kernel: remember
that those bots are trained on a large corpus of copyrighted
material. Facebook has &lt;a href=&quot;https://www.theguardian.com/technology/2025/jan/10/mark-zuckerberg-meta-books-ai-models-sarah-silverman&quot;&gt;trained their models on pirated books&lt;/a&gt; and
&lt;a href=&quot;https://torrentfreak.com/nvidia-contacted-annas-archive-to-secure-access-to-millions-of-pirated-books/&quot;&gt;Nvidia has done deals with Anna's Archive&lt;/a&gt; to secure access to
large swaths of copyrighted material. The &lt;a href=&quot;https://www.congress.gov/crs-product/LSB10922&quot;&gt;US Congress seems to think
LLM outputs are not copyrightable&lt;/a&gt;, like any other machine outputs.&lt;/p&gt;

&lt;p&gt;With many people now vibe coding their way out of learning or
remembering how computers work, is this the Death of Copyright?&lt;/p&gt;

&lt;p&gt;And that, of course, brings us to the final horseman: Pestilence.&lt;/p&gt;

&lt;h1 id=&quot;pestilence-slop&quot;&gt;Pestilence: slop&lt;/h1&gt;

&lt;p&gt;There is a growing meme that programming is essentially over as we
know it. That you can simply vibe-code applications from scratch and
it's pretty good.&lt;/p&gt;

&lt;p&gt;Maybe that's true.&lt;/p&gt;

&lt;p&gt;So far, most of my attempts at resolving any complex problem with a
LLM have often failed with bizarre failures. &lt;a href=&quot;https://gitlab.com/anarcat/scripts/-/blob/main/transmodify.py?ref_type=heads&quot;&gt;Some worked surprisingly
well.&lt;/a&gt; Maybe, of course, I am holding it wrong.&lt;/p&gt;

&lt;p&gt;I personally don't believe LLMs will ever be good enough to produce
and maintain software at scale. They're surprisingly good at finding
security flaws right now. But what I see is also a lot of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Bullshit&quot;&gt;Bullshit&lt;/a&gt;, with a capital B. It's not lying: it does not &quot;know&quot;
anything, so it &lt;em&gt;can't&lt;/em&gt; lie. It's misleadingly cohesive and
deliberate, but it lacks meaning, intent, will.&lt;/p&gt;

&lt;p&gt;I have not been confronted with much slop, apart from the lobster
Jesus or the yellow man atrocities, and particularly not in my
work. But I see what it is doing to my profession: beyond
&lt;a href=&quot;https://en.wikipedia.org/wiki/Vibe_coding&quot;&gt;vibe-coding&lt;/a&gt;, people are now &lt;a href=&quot;https://www.forbes.com/sites/timkeary/2026/04/13/is-the-cult-of-tokenmaxxingjust-another-fad-or-the-new-normal/&quot;&gt;token-maxxing&lt;/a&gt;, and
&lt;a href=&quot;https://leehanchung.github.io/blogs/2026/04/05/the-ai-great-leap-forward/#let-a-hundred-skills-bloom&quot;&gt;land-grabbing their colleagues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don't like what LLMs do to our communities, or the fabric of
software we live with.&lt;/p&gt;

&lt;p&gt;Software does not evolve in a void. It is a team effort, be it free
software or a corporate product. Generations of humans have carefully
built the scaffolding of technology required for modern networks and
software to operate, in a convoluted contraption that no single human
fully understands anymore.&lt;/p&gt;

&lt;p&gt;The idea of simply giving up on that understanding entirely and
delegating it to an unproven model is not only chilling, it feels just
plain stupid. Not stupid as in &lt;a href=&quot;https://en.wikipedia.org/wiki/Skynet_(Terminator)&quot;&gt;Skynet&lt;/a&gt;, stupid as in &quot;I can't get
inside the data center because the authentication system is
down&quot;. Except we're in a &quot;the power plant doesn't reboot&quot; or &quot;their
LLM found an 0day in our slop&quot; kind of stupid.&lt;/p&gt;

&lt;h1 id=&quot;the-fifth-horsemen&quot;&gt;The fifth horsemen&lt;/h1&gt;

&lt;p&gt;Researching for this article, I looked up the four horsemen and found
out they original seems to have been:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Famine&lt;/li&gt;
&lt;li&gt;War&lt;/li&gt;
&lt;li&gt;Death&lt;/li&gt;
&lt;li&gt;Conquest (??)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I was surprised. I grew up thinking about the horsemen being Famine,
War, Pestilence, and Death. So I went back to &lt;a href=&quot;https://www.metallica.com/songs/the-four-horsemen.html&quot;&gt;my original source&lt;/a&gt;
which actually claims the horsemen are:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Time has taken its toll on you, the lines that crack your face.
Famine, your body, it has torn through, withered in every place.
Pestilence for what you've had to endure, and what you have put others through
Death, deliverance for you, for sure, now there's nothing you can do
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So I guess that makes no sense either, which, fair enough, I shouldn't
rely on Metallica for theological references. Especially since that
song was originally called &lt;a href=&quot;https://en.wikipedia.org/wiki/Mechanix&quot;&gt;Mechanix&lt;/a&gt; and was &quot;about having sex at
a gas station&quot;.&lt;/p&gt;

&lt;p&gt;Anyways.&lt;/p&gt;

&lt;p&gt;The point is, there are actually five horsemen, and the fifth one is,
in my opinion, Conquest.&lt;/p&gt;

&lt;p&gt;Those companies (and &lt;em&gt;not&lt;/em&gt; &quot;AI&quot;, mind you) are taking over the
world. I sense a strong connection with the &quot;post-truth&quot; world imposed
on us by fascists like Trump and Putin. It's not an accident, it's a
power grab part of the &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Californian_Ideology&quot;&gt;Californian Ideology&lt;/a&gt;&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;https://anarc.at/tag/python-planet/#fn:3&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. Just like Airbnb
broke housing, Uber destroyed the transportation and Amazon is taking
over retail and server hosting, LLM companies are essentially trying
to take over if not everything, at least &lt;a href=&quot;https://en.wikipedia.org/wiki/Cognition&quot;&gt;Cognition&lt;/a&gt; as a whole.&lt;/p&gt;

&lt;p&gt;But the capitalization of those companies (OpenAI and Nvidia in particular)
are so far beyond reason that their inevitable collapse will likely
lead to a global financial collapse of biblical proportions.&lt;/p&gt;

&lt;p&gt;Because they will inevitably fail like previous bubbles they are built
on. And when they fail, I hope it zips all the way back through the
blockchain scam, the ad surveillance system, and the dot com then git
me back my internet.&lt;/p&gt;

&lt;h1 id=&quot;the-tower-of-babel&quot;&gt;The Tower of Babel&lt;/h1&gt;

&lt;p&gt;While I'm off in the woods hallucinating (ha!) on biblical allegories,
I feel there's another sign that the apocalypse is coming.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Tower_of_Babel&quot;&gt;Tower of Babel&lt;/a&gt; myth says that humans tried to create a big
tower up to heaven and become god. God confounds their speech and
scatters the human race. End of utopia.&lt;/p&gt;

&lt;p&gt;This is what is happening to our human translators now. LLMs being,
after all, Language Models, they are excellent at translation work. So
much that the only translators not replaced by LLMs right now are
&lt;a href=&quot;https://en.wikipedia.org/wiki/Language_interpretation&quot;&gt;interpreters&lt;/a&gt;, who translate vocally in real time. But
interpreters are worried about their jobs as well.&lt;/p&gt;

&lt;p&gt;This concretely means we will lose the human capacity, as a
civilization, to translate between each other. It is still an &lt;a href=&quot;https://revues.imist.ma/index.php/JALCS/article/view/59018&quot;&gt;open
question&lt;/a&gt; whether the remaining revision work will be enough for
translators to avoid deskilling, but other research has shown that LLM
use &lt;a href=&quot;https://publichealthpolicyjournal.com/mit-study-finds-artificial-intelligence-use-reprograms-the-brain-leading-to-cognitive-decline/&quot;&gt;leads to cognitive decline&lt;/a&gt;, &lt;a href=&quot;https://dl.acm.org/doi/full/10.1145/3706598.3713778&quot;&gt;impacts critical thinking&lt;/a&gt;,
and generally, that &lt;a href=&quot;https://publicera.kb.se/ir/article/view/47143&quot;&gt;deskilling is a common outcome&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ultimately, I think this is where LLMs bring us. Towards collapse.&lt;/p&gt;

&lt;p&gt;So this is a call to arms. Fight back!&lt;/p&gt;

&lt;p&gt;Poison bots. Build local real-world communities.&lt;/p&gt;

&lt;p&gt;Go low tech. &lt;a href=&quot;https://en.wikipedia.org/wiki/Moore%27s_law&quot;&gt;Moore's law&lt;/a&gt; is dead, &lt;a href=&quot;https://spectrum.ieee.org/the-death-of-moores-law-will-spur-innovation&quot;&gt;make use of it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Patch your shit. &lt;a href=&quot;https://anginedepoitrine.com/&quot;&gt;Go weird&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Refuse slop. Train your brain.&lt;/p&gt;

&lt;p&gt;The horsemen will collapse, but let's not go down with them.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dune.fandom.com/wiki/Butlerian_Jihad&quot;&gt;Butlerian Jihad&lt;/a&gt;!&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;This article was written without the use of a large language model
and should not be used to train one.&lt;/p&gt;&lt;/blockquote&gt;



&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&quot;fn:1&quot;&gt;
I prefer &quot;LLM&quot; to Artificial Intelligence, as I don't consider
models to have &quot;Intelligence&quot; which goes far beyond the analytical
traits we train models for. Intelligence &lt;em&gt;requires&lt;/em&gt; embodiment
and social interaction; machines lack the innate human skills of
empathy, feeling and care, which explains a lot of the evils
behind the current trends.&lt;a href=&quot;https://anarc.at/tag/python-planet/#fnref:1&quot; rev=&quot;footnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
&lt;li id=&quot;fn:2&quot;&gt;
It should be noted that Morris also happened to be one of the
founder of &lt;a href=&quot;https://en.wikipedia.org/wiki/Y_Combinator&quot;&gt;Y Combinator&lt;/a&gt; where he is in good company with
other techno-fascists like Peter Thiel, Sam Altman, and so
on. Crime, after all, pays.&lt;a href=&quot;https://anarc.at/tag/python-planet/#fnref:2&quot; rev=&quot;footnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
&lt;li id=&quot;fn:3&quot;&gt;
Probably a good time to watch &lt;a href=&quot;https://en.wikipedia.org/wiki/All_Watched_Over_by_Machines_of_Loving_Grace_(TV_series)&quot;&gt;All Watched Over by Machines of Loving Grace&lt;/a&gt;.&lt;a href=&quot;https://anarc.at/tag/python-planet/#fnref:3&quot; rev=&quot;footnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
	<pubDate>Fri, 15 May 2026 21:25:27 +0000</pubDate>
</item>
<item>
	<title>PyCharm: Pyrefly LSP Integration with Type Engine in PyCharm 2026.1.2</title>
	<guid>https://blog.jetbrains.com/pycharm/2026/05/pyrefly-lsp-integration-in-pycharm-2026-1-2/</guid>
	<link>https://blog.jetbrains.com/pycharm/2026/05/pyrefly-lsp-integration-in-pycharm-2026-1-2/</link>
	<description>&lt;p&gt;In PyCharm 2026.1.2, you can enable &lt;a href=&quot;https://pyrefly.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pyrefly&lt;/a&gt; as an external type provider, dramatically increasing the speed of the IDE’s code insight features.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What is the Pyrefly LSP?&lt;/h2&gt;



&lt;p&gt;“LSP” stands for the &lt;strong&gt;Language Server Protocol&lt;/strong&gt; – a standardized protocol that allows code editors and IDEs to communicate with &lt;strong&gt;language servers&lt;/strong&gt;. The LSP enables language servers to provide code intelligence features, such as:&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;Code completion&lt;/li&gt;



&lt;li&gt;Information on hover (for example, quick documentation)&lt;/li&gt;



&lt;li&gt;&lt;em&gt;Go to definition &lt;/em&gt;and other actions&lt;/li&gt;



&lt;li&gt;Error checking and type-related diagnostics&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;/p&gt;



&lt;p&gt;The key benefit of the LSP is that it allows a single language server to be used across multiple tools. This means that language-specific intelligence does not have to be implemented separately in every editor, IDE, or CI pipeline.&lt;/p&gt;



&lt;p&gt;Pyrefly is Meta’s next-generation Python type checker, engineered from the ground up in Rust to replace its predecessor, Pyre (written in OCaml). With the move to Rust, Pyrefly achieves significantly faster performance and improved cross-platform portability. More than just a rewrite, it is designed to be more capable and robust, offering an efficient toolset for maintaining large-scale Python codebases with high precision and minimal overhead.&lt;/p&gt;



&lt;p&gt;Pyrefly provides the following benefits:&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Higher performance and efficiency&lt;/strong&gt; – Thanks to its Rust-based architecture, Pyrefly achieves significantly faster speeds and improves cross-platform portability.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Enhanced code intelligence&lt;/strong&gt; – As an external type provider, Pyrefly powers essential code insight features in the IDE, including type inference, type-related diagnostics, quick documentation, and inlay hints.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt; – Pyrefly is designed to handle &lt;strong&gt;large-scale Python codebases&lt;/strong&gt; with high precision and minimal overhead.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Pyrefly is highly beneficial for projects and developers dealing with &lt;strong&gt;large, complex Python codebases&lt;/strong&gt; that prioritize performance and robust typing. Integrating Pyrefly via the LSP is part of our ongoing work to enhance code insight performance in PyCharm.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Using Pyrefly in PyCharm&lt;/h2&gt;



&lt;p&gt;Once enabled, Pyrefly powers all code insight functionality in PyCharm, including type inference and type-related diagnostics, quick documentation, and inlay hints. Delegating analysis to this faster engine delivers significantly improved performance.&lt;/p&gt;



&lt;p&gt;To start using Pyrefly in your PyCharm project, go to the &lt;em&gt;Type&lt;/em&gt; widget at the bottom of the window. By default, the IDE uses the built-in type engine. Click on the widget and select the option to use Pyrefly. If you do not have Pyrefly installed yet, PyCharm will install it automatically.&amp;nbsp;&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/install-pyrefly.gif&quot; alt=&quot;&quot; class=&quot;wp-image-706921&quot; /&gt;



&lt;p&gt;Once you’ve switched to the Pyrefly type engine, you will see a Pyrefly icon at the bottom, which you can hover over to check the version being used.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/image-15.png&quot; alt=&quot;&quot; class=&quot;wp-image-706874&quot; /&gt;



&lt;p&gt;Please note that the integration currently works for local interpreter configurations. Support for Docker, Docker Compose, WSL, SSH, and multi-module projects is planned for future releases.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Pyrefly vs. the built-in type engine&lt;/h2&gt;



&lt;p&gt;Now let’s look at how Pyrefly and the built-in type engine behave in a complex Python project. In this FastAPI example, multiple files are typed, but in this file, the variable &lt;em&gt;ref &lt;/em&gt;is incorrectly typed, causing four errors. When using the built-in type engine, the IDE identifies that something is wrong, but it suggests running further analysis to fix the problem, which requires an extra step.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/image.jpeg&quot; alt=&quot;&quot; class=&quot;wp-image-706886&quot; /&gt;



&lt;p&gt;Using Pyrefly as the type engine, the IDE reports errors immediately and highlights where they originate. However, it is worth noting that, in our example, there are four errors, but Pyrefly picks up only three of them. It misses the one in &lt;code&gt;self._storage[ref]&lt;/code&gt;.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/pyrefly-checking.gif&quot; alt=&quot;&quot; class=&quot;wp-image-706934&quot; /&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Download the latest version of PyCharm and try it out&lt;/h2&gt;



&lt;p&gt;Ready to experience a dramatic leap in Python development performance? The Pyrefly type engine in PyCharm 2026.1.2 delivers the next generation of type checking. Engineered in Rust for unparalleled speed, it resolves files in as little as 0.5–1 seconds, significantly faster than the built-in engine. If you maintain large, complex Python codebases and prioritize robust typing, this feature is essential, as it allows you to delegate analysis to a faster engine and receive immediate type-related diagnostics. Download the latest version of PyCharm (2026.1.2) to unlock superior efficiency, scalability, and code insight.&lt;/p&gt;</description>
	<pubDate>Fri, 15 May 2026 15:31:27 +0000</pubDate>
</item>
<item>
	<title>Real Python: The Real Python Podcast – Episode #295: Agentic Architecture: Why Files Aren't Always Enough</title>
	<guid>https://realpython.com/podcasts/rpp/295/</guid>
	<link>https://realpython.com/podcasts/rpp/295/</link>
	<description>&lt;p&gt;What are the limitations of using a file-based agent workflow? Why do massive context windows tend to collapse? This week on the show, Mikiko Bazeley from MongoDB joins us to discuss agentic architecture and context engineering.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Fri, 15 May 2026 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Real Python: Quiz: Python's Array: Working With Numeric Data Efficiently</title>
	<guid>https://realpython.com/quizzes/python-array/</guid>
	<link>https://realpython.com/quizzes/python-array/</link>
	<description>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of
&lt;a href=&quot;https://realpython.com/python-array/&quot;&gt;Python&amp;rsquo;s Array: Working With Numeric Data Efficiently&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By working through this quiz, you&amp;rsquo;ll revisit the differences between Python&amp;rsquo;s &lt;code&gt;array&lt;/code&gt; module and the built-in &lt;code&gt;list&lt;/code&gt;, the meaning of type codes, how to create and manipulate arrays as mutable sequences, and the performance trade-offs of using a low-level numeric container.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Fri, 15 May 2026 12:00:00 +0000</pubDate>
</item>
<item>
	<title>EuroPython: May Newsletter: Sessions, Speakers, Sprints</title>
	<guid>https://blog.europython.eu/may-newsletter-sessions-speakers-sprints/</guid>
	<link>https://blog.europython.eu/may-newsletter-sessions-speakers-sprints/</link>
	<description>&lt;p&gt;Hi all Pythonistas! &amp;#x1F44B;&amp;#xA0;&lt;/p&gt;&lt;p&gt;Hope you&amp;#x2019;ve been enjoying these last few weeks, and hopefully planning your trip to Krak&amp;#xF3;w in July! With &lt;strong&gt;two months left&lt;/strong&gt; before the conference, the EuroPython organising team has been firing on all cylinders to create a conference to remember. Here&amp;#x2019;s the latest from us:&lt;/p&gt;&lt;h2 id=&quot;%F0%9F%93%8B-session-and-speaker-lists-are-available&quot;&gt;&lt;strong&gt;&amp;#x1F4CB; Session and Speaker Lists Are Available&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Our Programme Team is busy preparing a detailed schedule for you. We plan to release it in the upcoming days, but in the meantime we&amp;#x2019;ve got the list of sessions and speakers for you to check out. It&amp;#x2019;s going to be an exciting conference!&lt;/p&gt;&lt;img src=&quot;https://blog.europython.eu/content/images/2026/05/sessions.png&quot; class=&quot;kg-image&quot; alt=&quot;alt&quot; width=&quot;1500&quot; height=&quot;1500&quot; /&gt;&lt;span&gt;Lists of sessions and speakers are available at &lt;/span&gt;&lt;a href=&quot;https://ep2026.europython.eu/?ref=blog.europython.eu&quot;&gt;&lt;span&gt;https://ep2026.europython.eu/&lt;/span&gt;&lt;/a&gt;&lt;p&gt;&amp;#x1F449; All conference sessions: &lt;a href=&quot;https://ep2026.europython.eu/sessions/?ref=blog.europython.eu&quot;&gt;https://ep2026.europython.eu/sessions/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;#x1F449; Speakers and tutorial leads: &lt;a href=&quot;https://ep2026.europython.eu/speakers/?ref=blog.europython.eu&quot;&gt;https://ep2026.europython.eu/speakers/&lt;/a&gt;&amp;#xA0;&lt;/p&gt;&lt;h2 id=&quot;%F0%9F%97%BB-language-rust-summits&quot;&gt;&amp;#x1F5FB;&lt;strong&gt; Language &amp;amp; Rust Summits&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Summits are an opportunity for project contributors to come together during EuroPython. These are &lt;strong&gt;invite-only events&lt;/strong&gt; with limited capacity at the venue, so &lt;strong&gt;registration is required&lt;/strong&gt;. &lt;/p&gt;&lt;h3 id=&quot;%F0%9F%90%8D-language-summit&quot;&gt;&amp;#x1F40D; Language Summit&lt;/h3&gt;&lt;p&gt;The Python Language Summit is an &lt;strong&gt;event for the developers of Python implementations&lt;/strong&gt; (CPython, PyPy, MicroPython, GraalPython, IronPython, and so on) to share information, discuss our shared problems, and &amp;#x2014; hopefully &amp;#x2014; solve them. &lt;/p&gt;&lt;p&gt;These issues might be related to the language itself, the standard library, the development process, the status of Python 3.15 (and plans for 3.16), the documentation, packaging, the website, and so forth. The Summit focuses on discussions and consensus-seeking, more than merely on presentations.&lt;/p&gt;&lt;p&gt;&amp;#x1F449; Register for the Language Summit: &lt;a href=&quot;https://ep2026.europython.eu/language-summit/?ref=blog.europython.eu&quot;&gt;https://ep2026.europython.eu/language-summit/&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;%E2%9A%99%EF%B8%8F-rust-summit&quot;&gt;&amp;#x2699;&amp;#xFE0F; Rust Summit&lt;/h3&gt;&lt;p&gt;This full-day summit is dedicated to &lt;strong&gt;exploring the intersection of Rust and the Python ecosystem&lt;/strong&gt;. Attendees can expect an intensive schedule focused specifically on integrating Rust into Python projects and the development of high-performance Python tools (e.g., using technologies like PyO3, Maturin, or writing performant native extensions).&amp;#xA0;&lt;/p&gt;&lt;p&gt;This summit is designed for developers who already possess some practical experience in these topics and are looking to deepen their expertise, share lessons learned, and contribute to the community&amp;amp;aposs collective knowledge.&lt;/p&gt;&lt;p&gt;&amp;#x1F449; Register for the Rust Summit: &lt;a href=&quot;https://ep2026.europython.eu/session/rust-summit-at-europython?ref=blog.europython.eu&quot;&gt;https://ep2026.europython.eu/session/rust-summit-at-europython&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;%F0%9F%97%A3%EF%B8%8F-keynote-speakers&quot;&gt;&lt;strong&gt;&amp;#x1F5E3;&amp;#xFE0F; Keynote Speakers&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;We are excited to announce a new keynote:&amp;#xA0;&lt;/p&gt;&lt;img src=&quot;https://blog.europython.eu/content/images/2026/05/4.png&quot; class=&quot;kg-image&quot; alt=&quot;Cover image of Leah Wasser, Executive Director and Founder of pyOpenSci, as a keynoter at EuroPython&quot; width=&quot;900&quot; height=&quot;600&quot; /&gt;&lt;span&gt;Leah Wasser will deliver a keynote at EuroPython 2026&lt;/span&gt;&lt;p&gt;&lt;strong&gt;Leah Wasser&lt;/strong&gt; is the Executive Director and founder of pyOpenSci, a community of 400+ researchers, engineers, and maintainers working to make developing and maintaining research software more accessible, sustainable, and human. She organizes the Maintainers Summit at PyCon US and believes the communities behind research software matter as much as the code itself.&lt;/p&gt;&lt;p&gt;Leah has built nationally recognized programs at the National Ecological Observatory Network (NEON) and the University of Colorado Boulder. Leah holds a PhD in ecology and is an active open source maintainer.&lt;/p&gt;&lt;div class=&quot;kg-card kg-button-card kg-align-center&quot;&gt;&lt;a href=&quot;https://ep2026.europython.eu/tickets/?ref=blog.europython.eu&quot; class=&quot;kg-btn kg-btn-accent&quot;&gt;Buy Your EuroPython 2026 Ticket&lt;/a&gt;&lt;/div&gt;&lt;h2 id=&quot;%E2%9C%8B-upcoming-call-for-volunteers&quot;&gt;&lt;strong&gt;&amp;#x270B; Upcoming Call for Volunteers&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;We&amp;amp;aposre opening our Call for Volunteers next week! Want to be part of the team and help make EuroPython 2026 awesome? Keep an eye on the website, the signup form drops in just a few days. We&amp;amp;aposll be reviewing applications on a rolling basis, so don&amp;amp;apost wait &amp;#x2013; apply as soon as it goes live! Whether you&amp;amp;aposre a first-timer or a returning volunteer, we&amp;amp;aposd love to have you.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;In my opinion, &lt;strong&gt;volunteering enriches the enjoyment of the whole event even further&lt;/strong&gt;. There are many different roles to suit different personalities and abilities &amp;#x2014; one of them could suit you very well. Also, volunteering is about the team; you will not be left alone in any case.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Jake Balas&lt;/strong&gt;, Onsite Volunteers Team Lead at EuroPython 2025 and this year&amp;#x2019;s Operations Team Lead&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;#x1F499; Read our full interview with Jake &lt;a href=&quot;https://blog.europython.eu/humans-of-ep-jake/&quot;&gt;https://blog.europython.eu/humans-of-ep-jake/&lt;/a&gt; &lt;/p&gt;&lt;h2 id=&quot;%F0%9F%92%B0-sponsorship-diamond-platinum-silver-available&quot;&gt;&lt;strong&gt;&amp;#x1F4B0; Sponsorship: Diamond, Platinum, Silver Available&amp;#xA0;&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;If you&amp;amp;aposre passionate about supporting EuroPython and helping make this conference accessible to a diverse, global Python community, consider becoming a sponsor or asking your employer to join us in this effort.&lt;/p&gt;&lt;p&gt;By sponsoring EuroPython, you&amp;#x2019;re not just backing an event &amp;#x2013; you&amp;amp;aposre gaining highly targeted visibility that will present your company or personal brand to one of the largest and most diverse Python communities in the world! Here&amp;#x2019;s what one of our sponsors said about their experience at EuroPython 2025:&lt;/p&gt;&lt;p&gt;&lt;span&gt;The Apify team shares their experience sponsoring EuroPython 2025&lt;/span&gt;&lt;/p&gt;&lt;p&gt;We still have some Diamond, Platinum, and Silver slots available. Along with our main packages, there are optional add-ons and extras to craft your brand messaging in exactly the way that you need.&amp;#xA0;&lt;/p&gt;&lt;p&gt;&amp;#x1F449; More information at:&lt;a href=&quot;https://ep2025.europython.eu/sponsorship/sponsor/?ref=blog.europython.eu&quot;&gt; &lt;/a&gt;&lt;a href=&quot;https://ep2026.europython.eu/sponsorship/sponsor/?ref=blog.europython.eu&quot;&gt;https://ep2026.europython.eu/sponsorship/sponsor/&lt;/a&gt;&amp;#xA0;&lt;/p&gt;&lt;p&gt;&amp;#x1F449; Contact us at &lt;a href=&quot;mailto:sponsoring@europython.eu&quot;&gt;sponsoring@europython.eu&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;%F0%9F%9A%A7-speaker-orientation&quot;&gt;&lt;strong&gt;&amp;#x1F6A7; Speaker Orientation&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Anyone interested in receiving speaker training from our experienced mentors is invited to an online workshop on the &lt;strong&gt;3rd June 2026, at 18:00 CEST&lt;/strong&gt;. We&amp;#x2019;ve designed the session for people of all experience levels, from first time speakers to seasoned presenters, and we still have spots for you.&lt;/p&gt;&lt;p&gt;&amp;#x1F449; Register now to confirm your place: &lt;a href=&quot;https://forms.gle/uZKwuAiBkUSmx7gn7?ref=blog.europython.eu&quot;&gt;https://forms.gle/uZKwuAiBkUSmx7gn7&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;%F0%9F%A4%9D-community-partners&quot;&gt;&lt;strong&gt;&amp;#x1F91D; Community Partners&lt;/strong&gt;&lt;/h2&gt;&lt;h3 id=&quot;%F0%9F%87%AA%F0%9F%87%B8pycones&quot;&gt;&amp;#x1F1EA;&amp;#x1F1F8;PyConES&amp;#xA0;&lt;/h3&gt;&lt;p&gt;Barcelona is calling, Pythonistas! PyConES 2026 has extended its CFP. New deadline: &lt;strong&gt;17 May, 23:59 CEST&lt;/strong&gt;. If you&amp;#x2019;re still thinking about submitting a talk, workshop, or idea to the community which will meet up in that gorgeous city, you have last days.&lt;/p&gt;&lt;p&gt;&amp;#x1F449; Submit the proposal for PyConES 2026 &lt;a href=&quot;https://pretalx.com/pycones-2026/cfp?ref=blog.europython.eu&quot;&gt;https://pretalx.com/pycones-2026/cfp&lt;/a&gt;&amp;#xA0;&lt;/p&gt;&lt;h3 id=&quot;%F0%9F%A6%ACpystok&quot;&gt;&amp;#x1F9AC;PyStok&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;PyStok #82&lt;/strong&gt; meetup lands on &lt;strong&gt;20 May, 18:00&lt;/strong&gt; at Zmiana Klimatu in Bia&amp;#x142;ystok, Poland, and free registration is officially live. Grab your spot at&lt;a href=&quot;https://pystok.org/najblizsze-wydarzenie?ref=blog.europython.eu&quot;&gt; https://pystok.org/najblizsze-wydarzenie&lt;/a&gt; to dive deep into RAG/LLM Wiki and the PLLuM (Polish Large Language Model) project. Between the &amp;quot;speed dating&amp;quot; networking, JetBrains giveaways and the legendary &amp;quot;Podlaskie afterparty&amp;quot;, it&amp;#x2019;s the perfect spot to soak up those unique North-East Polish vibes and talk Python and AI with the local crowd.&lt;/p&gt;&lt;h2 id=&quot;%F0%9F%93%A3-community-outreach&quot;&gt;&lt;strong&gt;&amp;#x1F4E3; Community Outreach&lt;/strong&gt;&lt;/h2&gt;&lt;h3 id=&quot;%F0%9F%8F%96%EF%B8%8Fpycon-us&quot;&gt;&amp;#x1F3D6;&amp;#xFE0F;PyCon US&lt;/h3&gt;&lt;p&gt;Several members of the EuroPython Society have traveled across the ocean to join the biggest gathering of Pythonistas, which this year takes place in Long Beach, California. If you&amp;#x2019;re there this weekend, make sure to&lt;strong&gt; look up the EuroPython booth and say &amp;#x201C;hi&amp;#x201D; to the team!&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;%F0%9F%8E%81-sponsor-spotlight&quot;&gt;&lt;strong&gt;&amp;#x1F381; Sponsor Spotlight&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;We&amp;amp;aposd like to thank Manychat for sponsoring EuroPython.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Manychat builds AI-powered chat automation for 1M+ creators and brands at real production scale.&lt;/strong&gt;&lt;/p&gt;&lt;a href=&quot;https://careers.manychat.com/?ref=blog.europython.eu&quot;&gt;&lt;img src=&quot;https://blog.europython.eu/content/images/2026/05/manychat_clearspace_white_bg.png&quot; class=&quot;kg-image&quot; alt=&quot;alt&quot; width=&quot;2000&quot; height=&quot;547&quot; /&gt;&lt;/a&gt;&lt;span&gt;View job openings at Manychat&lt;/span&gt;&lt;h2 id=&quot;%F0%9F%91%8B-stay-connected&quot;&gt;&lt;strong&gt;&amp;#x1F44B; Stay Connected&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Follow us on social media and subscribe to our newsletter for all the updates:&lt;/p&gt;&lt;p&gt;&amp;#x1F449; Sign up for the newsletter: &lt;a href=&quot;https://blog.europython.eu/#/portal/signup&quot;&gt;https://blog.europython.eu/portal/signup&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;:&lt;a href=&quot;https://www.linkedin.com/company/europython/?ref=blog.europython.eu&quot;&gt; https://www.linkedin.com/company/europython/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;X/Twitter&lt;/strong&gt;:&lt;a href=&quot;https://x.com/europython?ref=blog.europython.eu&quot;&gt; https://x.com/europython&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Mastodon&lt;/strong&gt;:&lt;a href=&quot;https://fosstodon.org/@europython?ref=blog.europython.eu&quot;&gt; https://fosstodon.org/@europython&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Bluesky&lt;/strong&gt;:&lt;a href=&quot;https://bsky.app/profile/europython.eu?ref=blog.europython.eu&quot;&gt; https://bsky.app/profile/europython.eu&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Instagram&lt;/strong&gt;:&lt;a href=&quot;https://www.instagram.com/europython/?ref=blog.europython.eu&quot;&gt; https://www.instagram.com/europython/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;YouTube&lt;/strong&gt;:&lt;a href=&quot;https://www.youtube.com/@EuroPythonConference?ref=blog.europython.eu&quot;&gt; https://www.youtube.com/@EuroPythonConference&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We&amp;#x2019;ll be announcing more keynotes in the upcoming days, and the detailed schedule will be available soon, so you can plan your conference experience. Just eight weeks are left before we all meet in the City of Castles and Dragons. See you there! &amp;#x1F40D;&amp;#x2764;&amp;#xFE0F;&lt;/p&gt;&lt;p&gt;Cheers,&lt;/p&gt;&lt;p&gt;The EuroPython Team&lt;/p&gt;&lt;div class=&quot;kg-card kg-signup-card kg-width-wide &quot;&gt;
            
            &lt;div class=&quot;kg-signup-card-content&quot;&gt;
                
                &lt;div class=&quot;kg-signup-card-text &quot;&gt;
                    &lt;h2 class=&quot;kg-signup-card-heading&quot;&gt;&lt;span&gt;Sign up for EuroPython Blog&lt;/span&gt;&lt;/h2&gt;
                    &lt;p class=&quot;kg-signup-card-subheading&quot;&gt;&lt;span&gt;The official blog of everything &amp;amp; anything EuroPython! EuroPython 2026 13-19 July, Krak&amp;#xF3;w&lt;/span&gt;&lt;/p&gt;
                    
        &lt;form class=&quot;kg-signup-card-form&quot;&gt;
            
            &lt;div class=&quot;kg-signup-card-fields&quot;&gt;
                &lt;input class=&quot;kg-signup-card-input&quot; id=&quot;email&quot; type=&quot;email&quot; /&gt;
                &lt;button class=&quot;kg-signup-card-button kg-style-accent&quot; type=&quot;submit&quot;&gt;
                    &lt;span class=&quot;kg-signup-card-button-default&quot;&gt;Subscribe&lt;/span&gt;
                    &lt;span class=&quot;kg-signup-card-button-loading&quot;&gt;
        
            
                
                
                
            
            
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            
        
    &lt;/span&gt;
                &lt;/button&gt;
            &lt;/div&gt;
            &lt;div class=&quot;kg-signup-card-success&quot;&gt;
                Email sent! Check your inbox to complete your signup.
            &lt;/div&gt;
            &lt;div class=&quot;kg-signup-card-error&quot;&gt;&lt;/div&gt;
        &lt;/form&gt;
        
                    &lt;p class=&quot;kg-signup-card-disclaimer&quot;&gt;&lt;span&gt;No spam. Unsubscribe anytime.&lt;/span&gt;&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;</description>
	<pubDate>Fri, 15 May 2026 06:00:54 +0000</pubDate>
</item>
<item>
	<title>Real Python: Quiz: Cursor vs Windsurf: Which AI Code Editor Is Best for Python?</title>
	<guid>https://realpython.com/quizzes/cursor-vs-windsurf-python/</guid>
	<link>https://realpython.com/quizzes/cursor-vs-windsurf-python/</link>
	<description>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of
&lt;a href=&quot;https://realpython.com/cursor-vs-windsurf-python/&quot;&gt;Cursor vs Windsurf: Which AI Code Editor Is Best for Python?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By working through these questions, you&amp;rsquo;ll revisit how the two editors differ across code completion, agentic multi-file editing, and debugging.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll also reconnect with the audit points worth applying whenever an AI agent writes Python on your behalf.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Thu, 14 May 2026 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Real Python: Quiz: Python Metaclasses</title>
	<guid>https://realpython.com/quizzes/python-metaclasses/</guid>
	<link>https://realpython.com/quizzes/python-metaclasses/</link>
	<description>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of
&lt;a href=&quot;https://realpython.com/python-metaclasses/&quot;&gt;Python Metaclasses&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Metaclasses sit behind every class you write in Python, and they&amp;rsquo;re one of the language&amp;rsquo;s deeper object-oriented concepts. By working through this quiz, you&amp;rsquo;ll revisit how classes are themselves objects, how &lt;code&gt;type&lt;/code&gt; creates them, and how a custom metaclass lets you customize class creation.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll also reflect on when a custom metaclass is actually the right tool and when a simpler technique does the job better.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Thu, 14 May 2026 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Python Engineering at Microsoft: PyCon US 2026</title>
	<guid>https://devblogs.microsoft.com/python/pycon-us-2026/</guid>
	<link>https://devblogs.microsoft.com/python/pycon-us-2026/</link>
	<description>&lt;h1&gt;Come See Us at PyCon US 2026!&lt;/h1&gt;
&lt;p&gt;Microsoft and GitHub will be at &lt;strong&gt;PyCon US 2026, May 14–17 in Long Beach, CA&lt;/strong&gt;. Stop by our booth, say hello, and tell us about your experience with our tools and services. We&amp;#8217;d love to meet you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&amp;#8217;t miss the Meta booth on Saturday at 1 p.m.&lt;/strong&gt;, where we&amp;#8217;ll be showing off the integration of Pylance with Meta&amp;#8217;s new Pyrefly type checker. The integration is currently in early preview in our Insiders build, and we can&amp;#8217;t wait to bring it to all our users later this year.&lt;/p&gt;
&lt;h2&gt;Hands-on Labs at the Booth&lt;/h2&gt;
&lt;p&gt;Drop in for &lt;strong&gt;10-minute interactive labs&lt;/strong&gt; covering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Copilot&lt;/li&gt;
&lt;li&gt;Azure DocumentDB&lt;/li&gt;
&lt;li&gt;Microsoft Foundry&lt;/li&gt;
&lt;li&gt;Microsoft Agent Framework&lt;/li&gt;
&lt;li&gt;Azure PostgreSQL&lt;/li&gt;
&lt;li&gt;Azure AI Search&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Talks and Sessions&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date &amp;amp; Time&lt;/th&gt;
&lt;th&gt;Room&lt;/th&gt;
&lt;th&gt;Session&lt;/th&gt;
&lt;th&gt;Speaker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wed, May 13 · 9:00 a.m.–12:30 p.m.&lt;/td&gt;
&lt;td&gt;101A&lt;/td&gt;
&lt;td&gt;Build your first MCP server in Python&lt;/td&gt;
&lt;td&gt;Pamela Fox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wed, May 13 · 1:30 p.m.–2:30 p.m.&lt;/td&gt;
&lt;td&gt;201B&lt;/td&gt;
&lt;td&gt;Dungeons and Databases: Build NPC agents to work with data in DocumentDB and Postgres &lt;em&gt;(Microsoft Sponsor session)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Marko Hotti, Patty Chow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thu, May 14 · 2:40 p.m.–3:05 p.m.&lt;/td&gt;
&lt;td&gt;104C&lt;/td&gt;
&lt;td&gt;Education Summit: Big Lessons from Small Models, Teaching Python AI with SLMs&lt;/td&gt;
&lt;td&gt;Gwyneth Peña-Siguenza&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thu, May 14 · 3:40 p.m.–4:05 p.m.&lt;/td&gt;
&lt;td&gt;104C&lt;/td&gt;
&lt;td&gt;Education Summit: Your Slides, But Faster, Building an AI-powered presentation workflow&lt;/td&gt;
&lt;td&gt;Pamela Fox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fri, May 15 · 3:30 p.m.–4:00 p.m.&lt;/td&gt;
&lt;td&gt;104C&lt;/td&gt;
&lt;td&gt;PyCharlas: Cómo pasé de perdida a enseñar Python + IA a miles, en un año&lt;/td&gt;
&lt;td&gt;Gwyneth Peña-Siguenza&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sat, May 16 · 2:30 p.m.–3:45 p.m.&lt;/td&gt;
&lt;td&gt;201A&lt;/td&gt;
&lt;td&gt;Maintainer Summit Tools Track: Dev Containers&lt;/td&gt;
&lt;td&gt;Sarah Kaiser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sun, May 17 · 1:00 p.m.–1:30 p.m.&lt;/td&gt;
&lt;td&gt;Grand Ballroom A&lt;/td&gt;
&lt;td&gt;A bridge over (not) troubled waters: Collecting marine data from your couch&lt;/td&gt;
&lt;td&gt;Sarah Kaiser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Can&amp;#8217;t wait to see you there!&lt;/p&gt;
&lt;p&gt;The post &lt;a href=&quot;https://devblogs.microsoft.com/python/pycon-us-2026/&quot;&gt;PyCon US 2026&lt;/a&gt; appeared first on &lt;a href=&quot;https://devblogs.microsoft.com/python&quot;&gt;Microsoft for Python Developers Blog&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Thu, 14 May 2026 00:18:13 +0000</pubDate>
</item>
<item>
	<title>Bob Belderbos: Learn agentic AI in Python with 10 small exercises</title>
	<guid>https://belderbos.dev/blog/learn-agentic-ai-python-10-step-journey/</guid>
	<link>https://belderbos.dev/blog/learn-agentic-ai-python-10-step-journey/</link>
	<description>&lt;p&gt;Most &quot;build an AI agent&quot; tutorials hand you a framework and skip the part where you actually understand what it's doing under the hood. When the abstraction breaks, you can't debug it because you never built the layer underneath. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://juanjoseexposito.com&quot;&gt;Juanjo&lt;/a&gt; and I think that gap is worth closing.&lt;/p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;/span&gt;
&lt;p&gt;Yesterday we shipped &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pythonagenticai.com/exercises/&quot;&gt;10 small browser-based exercises&lt;/a&gt; that walk through that layer one pattern at a time (more on how we run them in the browser with Pyodide &lt;a href=&quot;https://belderbos.dev/blog/python-exercises-browser-pyodide/&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This article is the conceptual journey behind them: how you get from &quot;I can call Claude&quot; to a complete agent loop with a testable architecture and a human-in-the-loop workflow. Each stage builds on the previous one.&lt;/p&gt;
&lt;h2 id=&quot;stage-1-make-a-model-reply-exercise-1&quot;&gt;Stage 1: make a model reply (exercise 1)&lt;/h2&gt;
&lt;p&gt;Every agent app starts with the same 3-line skeleton. Build a client, call &lt;code&gt;messages.create&lt;/code&gt;, read &lt;code&gt;content[0].text&lt;/code&gt;. The shape doesn't change much. Only what wraps around it does.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;/span&gt;&lt;span&gt; anthropic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;client&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; anthropic.Anthropic()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    model&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;claude-sonnet-4-6&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    max_tokens&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;256&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    messages&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt;[{&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;user&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;content&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;Say hi&amp;quot;&lt;/span&gt;&lt;span&gt;}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;/span&gt;&lt;span&gt;(msg.content[&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;/span&gt;&lt;span&gt;].text)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why &lt;code&gt;content[0].text&lt;/code&gt; and not &lt;code&gt;.text&lt;/code&gt;? Because &lt;code&gt;content&lt;/code&gt; is a list of blocks (text, tool_use, and others). That list is how tool use plugs in later without breaking the response shape. Get this mental model before anything else.&lt;/p&gt;
&lt;h2 id=&quot;stage-2-make-the-reply-machine-readable-exercises-2-3&quot;&gt;Stage 2: make the reply machine-readable (exercises 2, 3)&lt;/h2&gt;
&lt;p&gt;Raw LLM strings are unreliable. The fix is two paired habits: a specific system prompt that locks the output shape, and a Pydantic model that validates it on the way back in.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;/span&gt;&lt;span&gt; pydantic&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;/span&gt;&lt;span&gt; BaseModel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ExpenseResult&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-entity&quot;&gt;BaseModel&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    category:&lt;/span&gt;&lt;span class=&quot;z-support&quot;&gt; str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    confidence:&lt;/span&gt;&lt;span class=&quot;z-support&quot;&gt; float&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;result&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; ExpenseResult.model_validate_json(msg.content[&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;/span&gt;&lt;span&gt;].text)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Treat the system prompt like an API contract. Say &quot;JSON only&quot;, show the literal shape, forbid improvisation (&quot;no punctuation, no explanation, nothing else&quot;). The phrase &quot;nothing else&quot; is doing real work; without it, models love to append a friendly sentence that breaks your parser.&lt;/p&gt;
&lt;h2 id=&quot;stage-3-make-it-remember-exercise-4&quot;&gt;Stage 3: make it remember (exercise 4)&lt;/h2&gt;
&lt;p&gt;LLMs don't remember anything. They have no state, no memory, no context beyond the current call. The &quot;conversation&quot; is a fiction we create by sending the whole message history every time.&lt;/p&gt;
&lt;p&gt;To get a continuous conversation, &lt;em&gt;you&lt;/em&gt; keep the list of &lt;code&gt;{&quot;role&quot;: ..., &quot;content&quot;: ...}&lt;/code&gt; dicts and send the whole thing every turn. Append the user message before the call, the assistant reply after. Roles must alternate.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;history&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ask&lt;/span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;(user_msg):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    history.append({&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;user&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;content&amp;quot;&lt;/span&gt;&lt;span&gt;: user_msg})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    reply&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        model&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;claude-sonnet-4-6&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        max_tokens&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;512&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        messages&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt;history,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ).content[&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;/span&gt;&lt;span&gt;].text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    history.append({&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;assistant&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;content&amp;quot;&lt;/span&gt;&lt;span&gt;: reply})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;/span&gt;&lt;span&gt; reply&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;State lives in your code, not the model. That single realization clears up most of the confusion students have about context windows and &quot;memory.&quot;&lt;/p&gt;
&lt;h2 id=&quot;stage-4-give-the-model-hands-exercise-5&quot;&gt;Stage 4: give the model hands (exercise 5)&lt;/h2&gt;
&lt;p&gt;Tool use turns a chatbot into something that can act. The loop is dumber than people think:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt; True&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    response&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; client.messages.create(&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;...&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-variable&quot;&gt; tools&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;TOOLS&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-variable&quot;&gt; messages&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt;messages)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;/span&gt;&lt;span&gt; response.stop_reason&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;end_turn&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;/span&gt;&lt;span&gt; response.content[&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;/span&gt;&lt;span&gt;].text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    # else: run the tool the model asked for, append the result, loop again&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two gotchas: append the &lt;em&gt;full&lt;/em&gt; &lt;code&gt;response.content&lt;/code&gt; as the assistant turn (it contains the &lt;code&gt;tool_use&lt;/code&gt; blocks the model needs to see), and tool results come back wrapped in a &lt;code&gt;user&lt;/code&gt; message, not assistant.&lt;/p&gt;
&lt;h2 id=&quot;stage-5-make-it-swappable-and-testable-exercises-6-7-8&quot;&gt;Stage 5: make it swappable and testable (exercises 6, 7, 8)&lt;/h2&gt;
&lt;p&gt;By exercise 6 the chatbot works, but it's also often a highly coupled mess importing external dependencies like &lt;code&gt;anthropic&lt;/code&gt; and &lt;code&gt;sqlite3&lt;/code&gt; into the business logic. Time for three common patterns, applied to LLM apps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;Protocol&lt;/code&gt; for the LLM provider, so tests can pass a &lt;code&gt;MockProvider&lt;/code&gt; with a &lt;code&gt;.calls&lt;/code&gt; list instead of an API key.&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://belderbos.dev/blog/repository-pattern-swappable-data-sources/&quot;&gt;Repository pattern&lt;/a&gt; for the persistence layer, so an in-memory dict satisfies the same interface as a database backend.&lt;/li&gt;
&lt;li&gt;A service layer that accepts both via &lt;code&gt;__init__&lt;/code&gt; and orchestrates: call provider, parse, save, return.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's the &lt;a href=&quot;https://belderbos.dev/blog/ai-agent-architecture-python/&quot;&gt;four-layer agent architecture&lt;/a&gt;, built piece by piece instead of dumped on you all at once.&lt;/p&gt;
&lt;h2 id=&quot;stage-6-keep-a-human-in-the-loop-exercise-9&quot;&gt;Stage 6: keep a human in the loop (exercise 9)&lt;/h2&gt;
&lt;p&gt;When the model returns a confidence score, use it. Above the threshold: auto-accept. Below: show the suggestion and let the user confirm or override.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; process&lt;/span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;(result, threshold&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.8&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;/span&gt;&lt;span&gt; result.confidence&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;/span&gt;&lt;span&gt; threshold:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;/span&gt;&lt;span&gt; result.category&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    answer&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-support&quot;&gt; input&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;Accept '&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;/span&gt;&lt;span&gt;result.category&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-string&quot;&gt;'? (Enter to confirm): &amp;quot;&lt;/span&gt;&lt;span&gt;).strip()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;/span&gt;&lt;span&gt; answer&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; or&lt;/span&gt;&lt;span&gt; result.category&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make the accept path the cheapest action (empty input or &lt;code&gt;y&lt;/code&gt;). Users pay the manual handling cost only when overriding. This is what separates a trusted assistant from one that quietly mislabels things, and it's the gap between &quot;AI demo&quot; and &lt;a href=&quot;https://belderbos.dev/blog/production-ai-agents-real-workflows/&quot;&gt;production-ready workflow&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;stage-7-generalize-the-loop-exercise-10&quot;&gt;Stage 7: generalize the loop (exercise 10)&lt;/h2&gt;
&lt;p&gt;The agent is exercise 5 with one change: replace the hardcoded function call with a &lt;code&gt;TOOL_FUNCTIONS[name]&lt;/code&gt; lookup.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;TOOL_FUNCTIONS&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;    &amp;quot;add&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; lambda&lt;/span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; a, b: a&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;/span&gt;&lt;span&gt; b,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;    &amp;quot;multiply&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; lambda&lt;/span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; a, b: a&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;/span&gt;&lt;span&gt; b,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# inside the loop:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;content&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-support&quot;&gt; str&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;TOOL_FUNCTIONS&lt;/span&gt;&lt;span&gt;[block.name](&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;**&lt;/span&gt;&lt;span&gt;block.input))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now adding a tool is one schema entry plus one dict entry. Swap &lt;code&gt;add&lt;/code&gt;/&lt;code&gt;multiply&lt;/code&gt; for &lt;code&gt;search_web&lt;/code&gt;, &lt;code&gt;query_db&lt;/code&gt;, &lt;code&gt;send_email&lt;/code&gt; and the loop is identical. Look at agent frameworks under the hood (LangChain, OpenAI Assistants) and you'll see this same pattern.&lt;/p&gt;
&lt;h2 id=&quot;what-the-journey-teaches&quot;&gt;What the journey teaches&lt;/h2&gt;
&lt;p&gt;Frameworks make sense once you can write the layer underneath. Skip that, and you are stuck the first time the abstraction leaks. After coaching many developers through this, the dividing line is clear: have they ever written the loop themselves?&lt;/p&gt;
&lt;p&gt;The 10 exercises are deliberately small. The arc matters more than any single one. Once you've done them, &quot;agentic AI&quot; stops being &quot;magic&quot; and starts being a loop, schema, and some patterns you might already know.&lt;/p&gt;
&lt;p&gt;Try them out:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the browser: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pythonagenticai.com/exercises/&quot;&gt;pythonagenticai.com/exercises&lt;/a&gt;. No install, no API key, no dependencies. Loads fast.&lt;/li&gt;
&lt;li&gt;Locally: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/bbelderbos/ai_bites&quot;&gt;clone the repo&lt;/a&gt; and work through them in your IDE.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;keep-reading&quot;&gt;Keep reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://belderbos.dev/blog/ai-agent-architecture-python/&quot;&gt;How an AI expense agent is actually structured&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://belderbos.dev/blog/production-ai-agents-real-workflows/&quot;&gt;What production AI agents actually require&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://belderbos.dev/blog/build-data-layer-before-llm/&quot;&gt;Build the data layer before you touch the LLM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Book I was recommended and I am going through: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.amazon.com/Build-Your-Own-Coding-Agent-ebook/dp/B0GN8NQL9R&quot;&gt;Build Your Own Coding Agent: The Zero-Magic Guide to AI Agents in Pure Python&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
	<pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate>
</item>
<item>
	<title>&quot;Michiel's Blog&quot;: httpx2!</title>
	<guid>https://tildeweb.nl/~michiel/httpx2.html</guid>
	<link>https://tildeweb.nl/~michiel/httpx2.html</link>
	<description>&lt;p&gt;It&amp;rsquo;s six weeks after we forked &lt;a href=&quot;https://github.com/encode/httpx&quot;&gt;httpx&lt;/a&gt; and
named our package &lt;a href=&quot;https://httpxyz.org&quot;&gt;httpxyz&lt;/a&gt;. Yesterday, the Pydantic
people started their own fork, &lt;a href=&quot;https://github.com/pydantic/httpx2&quot;&gt;httpx2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;TL;DR: while we think httpxyz was definitely needed, we welcome httpx2 and
think it should be the &amp;lsquo;blessed&amp;rsquo; fork.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;httpxyz logo&quot; src=&quot;https://tildeweb.nl/~michiel/tags/python/images/httpxyz.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;About httpx2&lt;/h2&gt;
&lt;h3&gt;Our fork&lt;/h3&gt;
&lt;p&gt;We did a bunch of work on httpx, merging old open pull requests, forking
httpcore, and making serious improvements fixing performance and other issues.&lt;/p&gt;
&lt;h3&gt;The Pydantic fork&lt;/h3&gt;
&lt;p&gt;Straight after we made our fork, I contacted Kludex, who is among other things
maintainer of Starlette, about our fork. He said that he had also been thinking
about doing a fork, but that he might prefer to do one himself, and also that
he thought that ours could not get popular because it&amp;rsquo;s on Codeberg instead of
on GitHub.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not really sure about that last one. While it&amp;rsquo;s true that there are still
no big examples of popular Python packages on Codeberg, more and more projects
are currently moving there. Also, even though we are on Codeberg, every single
day we were still gaining &amp;lsquo;stars&amp;rsquo; and if the Pydantic team would have backed
our fork, with their power we definitely could have made it a success. The
majority of users don&amp;rsquo;t care at what forge the code is hosted, they install
from PyPI, via &lt;code&gt;pip&lt;/code&gt; or &lt;code&gt;uv&lt;/code&gt;. Where the code is hosted is not really a factor
in the popularity.&lt;/p&gt;
&lt;h3&gt;The way forward&lt;/h3&gt;
&lt;p&gt;The reason I started httpxyz was because of the impasse httpx was in, and that
I felt something had to be done. It&amp;rsquo;s not that I wanted to be the maintainer of
an HTTP library per se ;-)&lt;/p&gt;
&lt;p&gt;So now that Pydantic, with their skillful team and their powerful ecosystem of
packages, is creating their own fork, there is no point really in trying to
&lt;em&gt;compete&lt;/em&gt; with them. We&amp;rsquo;ll keep httpxyz up; but we will support httpx2 and will
urge anyone who is trying to switch away from httpx to consider httpx2.&lt;/p&gt;
&lt;h3&gt;The current situation&lt;/h3&gt;
&lt;p&gt;As it stands, httpx2 is lacking the performance improvements we added to
httpxyz. But it will not be long before they will add those, too.&lt;/p&gt;
&lt;p&gt;Also they already made some smart decisions I had been unsure about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;they are switching from certifi to
  &lt;a href=&quot;https://github.com/pydantic/httpx2/pull/209&quot;&gt;truststore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;they are switching to
  &lt;a href=&quot;https://github.com/pydantic/httpx2/pull/933&quot;&gt;compression.zstd&lt;/a&gt; on Python
  3.14+, enabling zstd compression by default&lt;/li&gt;
&lt;li&gt;they
  &lt;a href=&quot;https://github.com/pydantic/httpx2/commit/160c7f59d7942efe0133516c161d39139780eb45&quot;&gt;merged httpcore&lt;/a&gt;
  and vendored it in their repository&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have great trust in their stewardship of the module. We don&amp;rsquo;t need
&amp;lsquo;competing&amp;rsquo; forks; we&amp;rsquo;ll fully support httpx2 and will encourage the community
to do the same!&lt;/p&gt;
&lt;p&gt;Thanks, and have fun!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=48127570&quot;&gt;Discussion on Hacker News&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Wed, 13 May 2026 19:00:00 +0000</pubDate>
</item>
<item>
	<title>Python Software Foundation: PSF Welcomes Hudson River Trading (HRT) as a Visionary Sponsor</title>
	<guid>https://pyfound.blogspot.com/2026/05/psf-welcomes-hudson-river-trading-hrt.html</guid>
	<link>https://pyfound.blogspot.com/2026/05/psf-welcomes-hudson-river-trading-hrt.html</link>
	<description>&lt;p&gt;[May 13, 2026] – The Python Software Foundation (PSF) is excited to announce that Hudson River Trading (HRT), a global leader in quantitative trading, has made a commitment to support Python and the PSF as a Visionary Sponsor.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;HRT’s &quot;Visionary&quot; sponsorship—our highest tier—will help to support the foundation’s core work of advancing and protecting the Python programming language and supporting a diverse and international community of Python programmers. HRT is the first quantitative trading firm to become a PSF Visionary Sponsor, alongside companies including NVIDIA, Google, Fastly, Bloomberg, Meta, and Anthropic. Contributions at this level directly fund the critical work that keeps Python thriving, including:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;CPython Development: &lt;/b&gt;Ensuring the core language remains fast, stable, and modern.&lt;/li&gt;&lt;li&gt;&lt;b&gt;PyPI Infrastructure: &lt;/b&gt;Maintaining the Python Package Index, which serves billions of downloads to developers worldwide.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Community Programs:&lt;/b&gt; Supporting Python workshops, events, and user groups globally, as well as hosting PyCon US each year.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Security Initiatives:&lt;/b&gt; Hardening the ecosystem against supply chain vulnerabilities.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;A Shared Commitment to Python&lt;/h3&gt;&lt;p&gt;Hudson River Trading is no stranger to the power of Python. As a leading multi-asset class quantitative trading firm, HRT relies on Python for research, data analysis, and engineering workflows. With this donation, HRT is giving back to the tools that empower their engineers and helping to ensure that Python remains flexible, effective, and welcoming in the ways that have made it one of the most popular programming languages in the world. Read more about Open Source at HRT &lt;a href=&quot;https://www.hudsonrivertrading.com/open-source-at-hrt/&quot; target=&quot;_blank&quot;&gt;on this page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;i&gt;“Python is a cornerstone of HRT’s research and trading infrastructure. Our engineers use Python extensively to build cutting-edge tooling that enhances our developer workflows, and we believe strongly in contributing to the open source software that makes our work possible. We are proud to support the PSF as a Visionary Sponsor helping to safeguard Python as a robust, accessible, and community-driven language for years to come.”&lt;/i&gt;&amp;nbsp; – Prashant Lal, Partner at Hudson River Trading&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;“Part of HRT's edge is our engineering, and one of our core values is 'Make It Better'. Our support of the Python Software Foundation – alongside our contributions to many other open source projects – reflects our desire to remain active, collaborative participants in the OSS engineering community over the long term, for the benefit of all.”&lt;/i&gt; – Hashem, Lead Software Engineer at Hudson River Trading&lt;/p&gt;&lt;p&gt;&lt;i&gt;“At HRT, we’ve always believed that the best way to advance Python is by working hand-in-hand with the community. Our internal work on &lt;a href=&quot;https://www.hudsonrivertrading.com/hrtbeat/inside-hrts-python-fork/&quot; target=&quot;_blank&quot;&gt;lazy imports&lt;/a&gt; gave us deep expertise in the problem space, and we channeled that experience directly into open collaboration by contributing to the development of &lt;a href=&quot;https://peps.python.org/pep-0810/&quot; target=&quot;_blank&quot;&gt;PEP 810&lt;/a&gt;. We pride ourselves on being exemplary participants in both the trading markets and the open source community, and our sponsorship of the Python Software Foundation reflects that genuine spirit of collaboration.”&lt;/i&gt; – Pablo Galindo Salgado, Lead Software Engineer at Hudson River Trading&lt;br /&gt;&lt;/p&gt;&lt;p&gt;As part of its ongoing participation in the Python ecosystem, HRT will be open sourcing some of its own projects and announcing additional OSS contributions later this year. To learn more about HRT’s open engineering, research, and data science roles, visit https://www.hudsonrivertrading.com/careers/.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;The PSF is grateful for Hudson River Trading’s support, alongside that of each of our Visionary Sponsors, and we hope you will join us in thanking them for their commitment to&amp;nbsp; the PSF and the Python community!&lt;/p&gt;&lt;h3&gt;About Hudson River Trading (HRT)&lt;/h3&gt;&lt;p&gt;Hudson River Trading (HRT) is a leading quantitative trading firm at the forefront of technical innovation in global financial markets. Every day, we bring together the world’s sharpest minds to collaboratively solve challenging problems and build technology that will drive the future of trading. Leveraging one of the world’s most sophisticated computing environments for research and development, we trade across asset classes and time horizons on more than 200 markets worldwide. We are a leading voice advocating for fair and transparent markets everywhere and dedicated to creating a better trading landscape for all. For more information, visit www.hudsonrivertrading.com.&amp;nbsp;&lt;/p&gt;&lt;h3&gt;About the Python Software Foundation (PSF)&lt;/h3&gt;&lt;p&gt;The Python Software Foundation is a US non-profit whose mission is to promote, protect, and advance the Python programming language, and to support and facilitate the growth of a diverse and international community of Python programmers. The PSF supports the Python community using corporate sponsorships, grants, and donations. Are you interested in sponsoring or donating to the PSF so we can continue supporting Python and its community? Check out &lt;a href=&quot;https://www.python.org/sponsors/application/&quot; target=&quot;_blank&quot;&gt;our sponsorship program&lt;/a&gt;, &lt;a href=&quot;https://psfmember.org/civicrm/contribute/transact/?reset=1&amp;id=2&quot; target=&quot;_blank&quot;&gt;donate directly&lt;/a&gt;, or contact our team at sponsors@python.org!&lt;/p&gt;</description>
	<pubDate>Wed, 13 May 2026 17:19:47 +0000</pubDate>
</item>
<item>
	<title>Real Python: How to Use OpenCode for AI-Assisted Python Coding</title>
	<guid>https://realpython.com/opencode-guide/</guid>
	<link>https://realpython.com/opencode-guide/</link>
	<description>&lt;div&gt;&lt;p&gt;OpenCode is an open-source AI coding agent that runs in your terminal and lets you analyze and refactor a Python project through conversational commands. In this guide, you’ll install it on your system, set it up with a free Google Gemini API key, and learn the basics of how to use it in your daily programming work.&lt;/p&gt;
&lt;p&gt;Here’s what OpenCode’s main interface looks like:&lt;/p&gt;
&lt;a href=&quot;https://files.realpython.com/media/opencode-initial-screen.7b04d0286d62.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/opencode-initial-screen.7b04d0286d62.png&quot; width=&quot;2048&quot; height=&quot;1152&quot; alt=&quot;OpenCode's Initial Screen&quot; /&gt;&lt;/a&gt;OpenCode's Initial Screen

&lt;p&gt;&lt;a href=&quot;https://realpython.com/ref/ai-coding-tools/opencode/&quot; class=&quot;ref-link&quot;&gt;OpenCode&lt;/a&gt; works as a conversational assistant you explicitly direct. Ask it to analyze functions, refactor code, or explain issues. Press &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-enter&quot;&gt;Enter&lt;/kbd&gt;&lt;/span&gt; to send your query, and you’ll get a response with full awareness of your project context. It supports more than seventy-five AI providers, including Anthropic, OpenAI, and &lt;a href=&quot;https://realpython.com/ref/ai-coding-tools/gemini/&quot; class=&quot;ref-link&quot;&gt;Google Gemini&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’re a Python developer who prefers working in the terminal, OpenCode offers deliberate, context-aware assistance and a customizable &lt;code&gt;AGENTS.md&lt;/code&gt; configuration file.&lt;/p&gt;
&lt;div class=&quot;container border rounded text-wrap-pretty my-3&quot;&gt;

  &lt;p class=&quot;my-3&quot;&gt;&lt;strong&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “How to Use OpenCode for AI-Assisted Python Coding” quiz. You’ll receive a score upon completion to help you track your learning progress:&lt;/p&gt;

  &lt;hr /&gt;

  &lt;div class=&quot;row my-3&quot;&gt;
    &lt;div class=&quot;col-xs-12 col-sm-4 col-md-3 align-self-center&quot;&gt;

      &lt;a href=&quot;https://realpython.com/quizzes/opencode-guide/&quot; tabindex=&quot;-1&quot;&gt;
        &lt;div class=&quot;embed-responsive embed-responsive-16by9&quot;&gt;

            &lt;img class=&quot;card-img-top m-0 p-0 embed-responsive-item rounded&quot; alt=&quot;A person in a hard hat and safety goggles with a clipboard stands beside a wall of panels labeled OpenCode, Analyze, Refactor, AI Models, Python Projects, LSP, Git UI, and Modes (Plan/Build).&quot; src=&quot;https://files.realpython.com/media/How-to-Use-OpenCode-for-AI-Assisted-Engineering_Watermarked.737726d966fd.jpg&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;


          &lt;div class=&quot;card-img-overlay d-flex align-items-center&quot;&gt;
            &lt;div class=&quot;mx-auto&quot;&gt;
              &lt;span class=&quot;text-light&quot;&gt;&lt;span class=&quot;icon baseline scale2x&quot;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/a&gt;

    &lt;/div&gt;
    &lt;div class=&quot;col&quot;&gt;
      &lt;div class=&quot;mt-3 d-md-none&quot;&gt;&lt;/div&gt; 
      &lt;p class=&quot;small text-muted mb-0&quot;&gt;&lt;strong&gt;Interactive Quiz&lt;/strong&gt;&lt;/p&gt;
      &lt;a href=&quot;https://realpython.com/quizzes/opencode-guide/&quot; class=&quot;stretched-link&quot;&gt;&lt;span class=&quot;my-0 h4&quot;&gt;How to Use OpenCode for AI-Assisted Python Coding&lt;/span&gt;&lt;/a&gt; 
      &lt;p class=&quot;text-muted mb-0 small&quot;&gt;Quiz yourself on OpenCode: install it, connect an AI provider, and use it to analyze and refactor Python from your terminal.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#prerequisites&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you start working with OpenCode, you’ll need to fulfill the following prerequisites regarding your current system and working environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.11 or higher for the sample project&lt;/li&gt;
&lt;li&gt;A modern terminal emulator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You also need an &lt;strong&gt;AI provider account&lt;/strong&gt;. In this guide, you’ll use &lt;a href=&quot;https://aistudio.google.com&quot;&gt;Google AI Studio&lt;/a&gt; to get a free Gemini &lt;a href=&quot;https://realpython.com/ref/glossary/api/&quot; class=&quot;ref-link&quot;&gt;API&lt;/a&gt; key. The free Gemini tier lets you follow along without any additional costs. However, you can also use Anthropic, OpenAI, or &lt;a href=&quot;https://realpython.com/github-copilot-python/&quot;&gt;GitHub Copilot&lt;/a&gt; if you already have subscriptions to those services.&lt;/p&gt;
&lt;p&gt;This guide uses a sample project consisting of a dice-rolling script. You’ll find the full &lt;a href=&quot;https://realpython.com/ref/glossary/source-code/&quot; class=&quot;ref-link&quot;&gt;source code&lt;/a&gt; in a collapsible block at the start of Step 2. The download below includes the starting script and the final refactored version so you can compare your work when you’re done:&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Get Your Code:&lt;/strong&gt; &lt;a href=&quot;https://realpython.com/bonus/opencode-guide-code/&quot; class=&quot;alert-link&quot;&gt;Click here to download the free sample code&lt;/a&gt; you’ll use to learn about AI-assisted Python coding with OpenCode.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You’ll also need some background knowledge of &lt;a href=&quot;https://realpython.com/python-first-steps/&quot;&gt;Python programming&lt;/a&gt; and basic experience with your operating system’s &lt;a href=&quot;https://realpython.com/terminal-commands/&quot;&gt;terminal or command line&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;step-1-install-and-set-up-opencode&quot;&gt;Step 1: Install and Set Up OpenCode&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#step-1-install-and-set-up-opencode&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s time to install OpenCode and get it talking to a model. You’ll install the tool on your system, authenticate with Gemini using a free API key, configure a default model, and verify that OpenCode responds correctly to your Python questions before you start coding with it.&lt;/p&gt;
&lt;h3 id=&quot;install-and-launch-opencode&quot;&gt;Install and Launch OpenCode&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#install-and-launch-opencode&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The quickest way to install OpenCode is to use the official installation script, which you can do with the following command:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;&lt;span class=&quot;sr-only&quot;&gt;Language: &lt;/span&gt;Shell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;curl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-fsSL&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;https://opencode.ai/install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;This script detects your platform, downloads the appropriate binary, installs the tool, and adds it to your &lt;a href=&quot;https://realpython.com/add-python-to-path/&quot;&gt;&lt;code&gt;PATH&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you prefer a package manager, you can also install OpenCode with Homebrew on macOS or Linux:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;&lt;span class=&quot;sr-only&quot;&gt;Language: &lt;/span&gt;Shell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;brew&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;anomalyco/tap/opencode
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;Note that the Homebrew team maintains the official formula and updates it less frequently than the installation script above.&lt;/p&gt;
&lt;p&gt;Alternatively, you can install it as a &lt;a href=&quot;https://nodejs.org/en&quot;&gt;Node.js&lt;/a&gt; package using &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;&lt;code&gt;npm&lt;/code&gt;&lt;/a&gt; if you already have this tool on your system:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;&lt;span class=&quot;sr-only&quot;&gt;Language: &lt;/span&gt;Shell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;npm&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-g&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;opencode-ai
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;If you’re on &lt;a href=&quot;https://opencode.ai/docs/windows-wsl&quot;&gt;Windows&lt;/a&gt;, the best experience comes from using WSL (Windows Subsystem for Linux). Set up WSL first by following &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/wsl/install&quot;&gt;Microsoft’s WSL installation guide&lt;/a&gt;, then open a WSL terminal and run the &lt;code&gt;curl&lt;/code&gt; command above. For optimal performance, you should store your project within the WSL filesystem rather than on a Windows drive.&lt;/p&gt;
&lt;/div&gt;&lt;h2&gt;&lt;a href=&quot;https://realpython.com/opencode-guide/?utm_source=realpython&amp;utm_medium=rss&quot;&gt;Read the full article at https://realpython.com/opencode-guide/ »&lt;/a&gt;&lt;/h2&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Wed, 13 May 2026 14:00:00 +0000</pubDate>
</item>
<item>
	<title>PyCharm: Support for uv, Poetry, and Hatch Workspaces (Beta)</title>
	<guid>https://blog.jetbrains.com/pycharm/2026/05/support-for-uv-poetry-and-hatch-workspaces-beta/</guid>
	<link>https://blog.jetbrains.com/pycharm/2026/05/support-for-uv-poetry-and-hatch-workspaces-beta/</link>
	<description>&lt;p&gt;Workspaces are increasingly the go-to choice for companies and open-source teams aiming to manage shared code, enforce consistency, and simplify dependency management across multiple services. Working within massive codebases often means juggling many interdependent Python projects simultaneously.&lt;/p&gt;



&lt;p&gt;To streamline this experience, PyCharm 2026.1.1 introduced&amp;nbsp;built-in support for uv workspaces, as well as those managed by Poetry and Hatch. This new functionality – currently in Beta – allows the IDE to automatically manage dependencies and environments across your entire workspace.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Intelligent workspace detection&lt;/h2&gt;



&lt;p&gt;When you open a workspace, PyCharm can now derive its entire structure and all its dependencies directly from your pyproject.toml files. This allows the IDE to understand relationships between projects deeply, significantly reducing the amount of configuration you have to do manually.&lt;/p&gt;



&lt;p&gt;Because this is a fundamental change to how PyCharm handles your workspace, we’ve implemented it as an opt-in feature. Here is what you need to know about the transition:&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Opt-in dialog:&lt;/strong&gt; When you open a project, PyCharm may suggest enabling automatic detection for uv workspaces and Poetry/Hatch setups.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;




&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/opt-in-dialog.png&quot; alt=&quot;&quot; class=&quot;wp-image-706672&quot; /&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/opt-in-dialog-example.png&quot; alt=&quot;&quot; class=&quot;wp-image-706676&quot; /&gt;




&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Manual configuration:&lt;/strong&gt; You can toggle workspace detection in &lt;em&gt;Settings | Project Structure&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/manual-configuration.png&quot; alt=&quot;&quot; class=&quot;wp-image-706697&quot; /&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Configuration note:&lt;/strong&gt; If you previously manually edited settings in .idea files, those settings may be reset when you agree to the new model.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Managing workspaces and their projects&lt;/h2&gt;



&lt;p&gt;PyCharm now provides an integrated experience that handles the complexities of multi-package setups in uv workspaces automatically. When you open a uv workspace, the IDE identifies the individual projects and their interdependencies, ensuring the project structure is ready for you to work with.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Visualizing workspace dependencies&lt;/h3&gt;



&lt;p&gt;Once the workspace is loaded, you can verify how your projects relate to one another. PyCharm presents these dependencies in &lt;em&gt;Settings | Project Dependencies&lt;/em&gt;.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/visualizing-workspace-dependencies.png&quot; alt=&quot;&quot; class=&quot;wp-image-706708&quot; /&gt;



&lt;p&gt;These relationships are derived directly from your configuration and are shown as read-only in the UI. To make changes to the dependency graph, you can edit the pyproject.toml file manually – PyCharm will then update its internal model.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Automatic environment configuration&lt;/h3&gt;



&lt;p&gt;PyCharm prioritizes a zero-config approach to your Python SDK. When you open a .py or pyproject.toml file within a project, the IDE performs an immediate check.&lt;/p&gt;



&lt;p&gt;If a compatible environment already exists on your system, PyCharm automatically configures it as the SDK for that project. If no environment is detected, a file-level notification will appear suggesting that you create a new uv environment and install the necessary dependencies for that project.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/automatic-environment-configuration.png&quot; alt=&quot;&quot; class=&quot;wp-image-706719&quot; /&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Maintaining environment consistency&lt;/h3&gt;



&lt;p&gt;Beyond the initial setup, PyCharm continuously monitors the health of your environment to ensure it stays in sync with your defined requirements.&amp;nbsp;&lt;/p&gt;



&lt;p&gt;If a dependency is not defined in your pyproject.toml file but is imported in your code, PyCharm will trigger a warning with a &lt;em&gt;Sync project&lt;/em&gt; quick-fix to resolve these discrepancies.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/maintaining-environment-consistency.png&quot; alt=&quot;&quot; class=&quot;wp-image-706730&quot; /&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Import management&lt;/h3&gt;



&lt;p&gt;PyCharm also assists when you are actively writing code by identifying gaps in your project configuration.&lt;/p&gt;



&lt;p&gt;If you import a package that isn’t present in the environment and is not yet listed in the project’s pyproject.toml, the IDE will detect the omission. A quick-fix will suggest adding the package to the environment and updating the corresponding .toml file simultaneously.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/import-management.png&quot; alt=&quot;&quot; class=&quot;wp-image-706741&quot; /&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Transparency via the &lt;em&gt;Python Process Output&lt;/em&gt; tool window&lt;/h3&gt;



&lt;p&gt;While PyCharm automates the backend execution of commands – such as uv sync &amp;#8211;all-packages – it still remains fully transparent.&lt;/p&gt;



&lt;p&gt;You can track all executed commands and their live output in the &lt;em&gt;Python Process Output&lt;/em&gt; tool window. If synchronization fails for an environment, you can analyze the specific error logs to quickly identify the root cause.&lt;/p&gt;



&lt;img src=&quot;https://blog.jetbrains.com/wp-content/uploads/2026/05/image-14.png&quot; alt=&quot;&quot; class=&quot;wp-image-706630&quot; /&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Poetry and Hatch workspaces&lt;/h3&gt;



&lt;p&gt;The logic for Poetry and Hatch workspaces follows this exact same workflow. PyCharm detects projects via their pyproject.toml files and manages the environments with the same automated precision.&lt;/p&gt;



&lt;p&gt;The only minor difference is in tool selection – the suggested environment tool is determined by what you have specified in your pyproject.toml. If no tool is specified, PyCharm will prioritize uv (if installed) or a standard virtual environment to get you up and running quickly.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Looking ahead&lt;/h2&gt;



&lt;p&gt;This Beta version of the functionality is just the beginning of our focus on supporting complex workspace structures. We are already working on expanding the UI to allow creating new projects, linking dependencies, and activating the terminal for specific projects.&lt;/p&gt;



&lt;p&gt;As we refine these features, your feedback is our best guide – please share your thoughts or report any issues on our &lt;a href=&quot;https://youtrack.jetbrains.com/issues/PY?u=1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;YouTrack issue tracker&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Wed, 13 May 2026 12:28:25 +0000</pubDate>
</item>
<item>
	<title>Python GUIs: How to Add Custom Widgets to Qt Designer — Use widget promotion to integrate your own Python widgets into Qt Designer layouts</title>
	<guid>https://www.pythonguis.com/faq/add-widgets-in-a-kind-of-library-show-in-the-qt-designer/</guid>
	<link>https://www.pythonguis.com/faq/add-widgets-in-a-kind-of-library-show-in-the-qt-designer/</link>
	<description>&lt;blockquote&gt;
&lt;p&gt;Can I use custom widgets in Qt Designer?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When you're building Python GUI applications with PyQt6 and Qt Designer, you'll reach a point where the built-in widgets aren't enough. Maybe you've created a custom plotting widget or a specialized input control in Python, and you want to place it into your Qt Designer layouts alongside all the standard widgets.&lt;/p&gt;
&lt;p&gt;The good news is that Qt Designer supports exactly this through a feature called &lt;strong&gt;widget promotion&lt;/strong&gt;. In this tutorial, you'll learn how to take any custom Python widget and integrate it into your Qt Designer &lt;code&gt;.ui&lt;/code&gt; files, so you can position and size it visually just like any built-in widget.&lt;/p&gt;
&lt;p&gt;The bad news is that since Qt Designer is a C++ application, it can't run your Python code. That means you won't see your custom widget rendered in the Designer preview. Instead, you'll see a placeholder (the base widget type you promoted from). Once you load the &lt;code&gt;.ui&lt;/code&gt; file in your running Python application, your custom widget appears in all its glory.&lt;/p&gt;
&lt;p&gt;With that caveat aside, let's look at how we can use custom widgets in Qt Designer.&lt;/p&gt;
&lt;h2 id=&quot;what-is-widget-promotion&quot;&gt;What is Widget Promotion?&lt;/h2&gt;
&lt;p&gt;Widget promotion is Qt Designer's way of letting you swap a standard widget for a custom one. You start by placing a regular widget on your form, a plain &lt;code&gt;QWidget&lt;/code&gt; for example, and then tell Qt Designer: &quot;When this UI is actually used, replace this placeholder with my custom widget class instead.&quot;&lt;/p&gt;
&lt;p&gt;Behind the scenes, this adds some extra information to the &lt;code&gt;.ui&lt;/code&gt; file. When you load that file in Python using &lt;code&gt;uic.loadUi()&lt;/code&gt; or compile it with &lt;code&gt;pyuic6&lt;/code&gt;, the loader knows to import your custom class and use it in place of the base widget.&lt;/p&gt;
&lt;h2 id=&quot;creating-a-custom-widget&quot;&gt;Creating a Custom Widget&lt;/h2&gt;
&lt;p&gt;Before we get into Qt Designer, let's create a simple custom widget in Python. We'll make a basic colored widget that draws a gradient background&amp;mdash;something you'd never get from a standard widget.&lt;/p&gt;
&lt;p&gt;Create a new file called &lt;code&gt;custom_widgets.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;from PyQt6.QtWidgets import QWidget
from PyQt6.QtGui import QPainter, QLinearGradient, QColor
from PyQt6.QtCore import Qt


class GradientWidget(QWidget):
    &quot;&quot;&quot;A custom widget that displays a gradient background.&quot;&quot;&quot;

    def __init__(self, parent=None):
        super().__init__(parent)

    def paintEvent(self, event):
        painter = QPainter(self)
        gradient = QLinearGradient(0, 0, self.width(), self.height())
        gradient.setColorAt(0.0, QColor(&quot;#2c3e50&quot;))
        gradient.setColorAt(1.0, QColor(&quot;#3498db&quot;))
        painter.fillRect(self.rect(), gradient)
        painter.end()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This widget overrides &lt;code&gt;paintEvent&lt;/code&gt; to draw a diagonal gradient from dark blue to lighter blue. It's a straightforward example, but the same promotion process works for any custom widget&amp;mdash;complex plotting canvases, custom controls, or anything else you build by subclassing a Qt widget.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-your-project-structure&quot;&gt;Setting Up Your Project Structure&lt;/h2&gt;
&lt;p&gt;For widget promotion to work, the Python file containing your custom widget needs to be importable when your application runs. The simplest way to achieve this is to keep everything in the same directory:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;my_project/
&amp;amp;boxvr&amp;amp;boxh&amp;amp;boxh custom_widgets.py      # Your custom widget classes
&amp;amp;boxvr&amp;amp;boxh&amp;amp;boxh mainwindow.ui          # Your Qt Designer file
&amp;amp;boxur&amp;amp;boxh&amp;amp;boxh main.py                # Your application entry point
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The file name and class name matter here&amp;mdash;you'll need to tell Qt Designer both of these during the promotion step.&lt;/p&gt;
&lt;h2 id=&quot;promoting-a-widget-in-qt-designer&quot;&gt;Promoting a Widget in Qt Designer&lt;/h2&gt;
&lt;p&gt;Now we can open Qt Designer and set up the promotion.&lt;/p&gt;
&lt;h3&gt;Place a base widget on your form&lt;/h3&gt;
&lt;p&gt;Open Qt Designer and create a new &lt;strong&gt;Main Window&lt;/strong&gt; (or open your existing &lt;code&gt;.ui&lt;/code&gt; file). From the widget box on the left, drag a plain &lt;strong&gt;Widget&lt;/strong&gt; (&lt;code&gt;QWidget&lt;/code&gt;) onto your form. Position and resize it however you like&amp;mdash;this is where your custom widget will appear when the application runs.&lt;/p&gt;
&lt;p&gt;You can use any base widget class as your starting point. If your custom widget subclasses &lt;code&gt;QPushButton&lt;/code&gt;, promote a &lt;code&gt;QPushButton&lt;/code&gt;. If it subclasses &lt;code&gt;QLabel&lt;/code&gt;, promote a &lt;code&gt;QLabel&lt;/code&gt;. For our &lt;code&gt;GradientWidget&lt;/code&gt;, which subclasses &lt;code&gt;QWidget&lt;/code&gt;, a plain &lt;code&gt;QWidget&lt;/code&gt; is the right choice.&lt;/p&gt;
&lt;h3&gt;Open the Promote Widgets dialog&lt;/h3&gt;
&lt;p&gt;Right-click on the widget you just placed. In the context menu, select &lt;strong&gt;Promote to...&lt;/strong&gt;. This opens the &lt;strong&gt;Promoted Widgets&lt;/strong&gt; dialog.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Promote to option in Qt Designer context menu&quot; src=&quot;https://www.pythonguis.com/static/images/qt-designer/promote-to-context-menu.png&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Fill in the promotion details&lt;/h3&gt;
&lt;p&gt;In the dialog, you'll see fields for three pieces of information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Base class name&lt;/strong&gt; &amp;mdash; This should already be filled in with the type of widget you right-clicked on (e.g., &lt;code&gt;QWidget&lt;/code&gt;). Leave this as is.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Promoted class name&lt;/strong&gt; &amp;mdash; Enter the name of your custom Python class. For our example, type &lt;code&gt;GradientWidget&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Header file&lt;/strong&gt; &amp;mdash; This is where Qt Designer's C++ heritage shows through. In C++, this would be a header file path. For Python, you enter the &lt;strong&gt;module import path&lt;/strong&gt; for your widget, &lt;em&gt;without&lt;/em&gt; the &lt;code&gt;.py&lt;/code&gt; extension. Since our class lives in &lt;code&gt;custom_widgets.py&lt;/code&gt;, type &lt;code&gt;custom_widgets&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;Promoted Widgets dialog filled in&quot; src=&quot;https://www.pythonguis.com/static/images/qt-designer/promoted-widgets-dialog.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Leave the &lt;strong&gt;Global include&lt;/strong&gt; checkbox unchecked.&lt;/p&gt;
&lt;h3&gt;Add and promote&lt;/h3&gt;
&lt;p&gt;Click &lt;strong&gt;Add&lt;/strong&gt; to add your class to the list of known promoted widgets. Then, with your class selected in the list, click &lt;strong&gt;Promote&lt;/strong&gt;. The dialog closes, and you'll notice the widget's class name in the &lt;strong&gt;Object Inspector&lt;/strong&gt; (top-right panel) now shows &lt;code&gt;GradientWidget&lt;/code&gt; instead of &lt;code&gt;QWidget&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That's it for the Designer side. Save your &lt;code&gt;.ui&lt;/code&gt; file.&lt;/p&gt;
&lt;h3&gt;Promoting additional widgets&lt;/h3&gt;
&lt;p&gt;Once you've added a promoted class through this dialog, it becomes available for reuse. The next time you want to promote a widget to &lt;code&gt;GradientWidget&lt;/code&gt;, just right-click the widget and you'll see it listed directly in the &lt;strong&gt;Promote to&lt;/strong&gt; submenu&amp;mdash;no need to open the full dialog again.&lt;/p&gt;
&lt;h2 id=&quot;loading-the-ui-in-python&quot;&gt;Loading the UI in Python&lt;/h2&gt;
&lt;p&gt;Now let's write the Python code to load the &lt;code&gt;.ui&lt;/code&gt; file and see our custom widget in action. Create &lt;code&gt;main.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6 import uic


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi(&quot;mainwindow.ui&quot;, self)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When you run this, &lt;code&gt;uic.loadUi()&lt;/code&gt; reads the &lt;code&gt;.ui&lt;/code&gt; file and sees that one of the widgets has been promoted to &lt;code&gt;GradientWidget&lt;/code&gt; from the &lt;code&gt;custom_widgets&lt;/code&gt; module. It automatically does the equivalent of:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;from custom_widgets import GradientWidget
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;...and creates an instance of &lt;code&gt;GradientWidget&lt;/code&gt; wherever you placed that promoted widget in your layout. Instead of a blank &lt;code&gt;QWidget&lt;/code&gt;, you'll see your gradient background.&lt;/p&gt;
&lt;h2 id=&quot;using-compiled-ui-files&quot;&gt;Using Compiled UI Files&lt;/h2&gt;
&lt;p&gt;If you prefer to compile your &lt;code&gt;.ui&lt;/code&gt; files to Python using &lt;code&gt;pyuic6&lt;/code&gt; rather than loading them at runtime, promotion works the same way. Run:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-sh&quot;&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;pyuic6 mainwindow.ui -o ui_mainwindow.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you open the generated &lt;code&gt;ui_mainwindow.py&lt;/code&gt;, you'll find an import line near the bottom:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;from custom_widgets import GradientWidget
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The compiled code creates your &lt;code&gt;GradientWidget&lt;/code&gt; instance in the right place automatically. You can then use the generated file in your application:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindow


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Both approaches&amp;mdash;runtime loading and compiled files&amp;mdash;handle promoted widgets in the same way.&lt;/p&gt;
&lt;h2 id=&quot;a-more-practical-example-embedding-pyqtgraph&quot;&gt;A More Practical Example: Embedding PyQtGraph&lt;/h2&gt;
&lt;p&gt;One of the most common reasons to promote widgets is to embed third-party plotting libraries like &lt;a href=&quot;https://www.pyqtgraph.org/&quot;&gt;PyQtGraph&lt;/a&gt; into your Designer layouts. PyQtGraph's &lt;code&gt;PlotWidget&lt;/code&gt; is a subclass of &lt;code&gt;QGraphicsView&lt;/code&gt;, so you'd promote a &lt;code&gt;QGraphicsView&lt;/code&gt; in Designer.&lt;/p&gt;
&lt;p&gt;Here's how you'd fill in the promotion dialog for PyQtGraph:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Base class name&lt;/strong&gt;: &lt;code&gt;QGraphicsView&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Promoted class name&lt;/strong&gt;: &lt;code&gt;PlotWidget&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Header file&lt;/strong&gt;: &lt;code&gt;pyqtgraph&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's all it takes. When your application runs, the placeholder &lt;code&gt;QGraphicsView&lt;/code&gt; becomes a fully functional &lt;code&gt;PlotWidget&lt;/code&gt; that you can plot data on.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6 import uic


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi(&quot;mainwindow.ui&quot;, self)

        # self.graphWidget is the promoted PlotWidget
        # (use the objectName you set in Designer)
        self.graphWidget.plot([1, 2, 3, 4, 5], [10, 20, 15, 30, 25])


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&quot;promoting-widgets-from-submodules&quot;&gt;Promoting Widgets from Submodules&lt;/h2&gt;
&lt;p&gt;If your custom widget lives in a submodule or package, you can use dotted import paths in the &lt;strong&gt;Header file&lt;/strong&gt; field. For example, if your project structure looks like this:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;my_project/
&amp;amp;boxvr&amp;amp;boxh&amp;amp;boxh widgets/
&amp;amp;boxv   &amp;amp;boxvr&amp;amp;boxh&amp;amp;boxh __init__.py
&amp;amp;boxv   &amp;amp;boxur&amp;amp;boxh&amp;amp;boxh gradient.py    # contains GradientWidget
&amp;amp;boxvr&amp;amp;boxh&amp;amp;boxh mainwindow.ui
&amp;amp;boxur&amp;amp;boxh&amp;amp;boxh main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You would enter &lt;code&gt;widgets.gradient&lt;/code&gt; as the header file in the promotion dialog. The loader will then do:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;
&lt;span class=&quot;code-block-language code-block-python&quot;&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;from widgets.gradient import GradientWidget
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This keeps things organized as your project grows.&lt;/p&gt;
&lt;h2 id=&quot;troubleshooting-common-issues&quot;&gt;Troubleshooting Common Issues&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&quot;No module named 'custom_widgets'&quot;&lt;/strong&gt; &amp;mdash; This means Python can't find the file containing your custom widget class. Make sure the module file is in the same directory as your script (or somewhere on your Python path), and that the name in the promotion dialog matches the file name exactly (without &lt;code&gt;.py&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The widget appears blank or as a plain QWidget&lt;/strong&gt; &amp;mdash; Double-check that the promoted class name matches your Python class name exactly, including capitalization. &lt;code&gt;GradientWidget&lt;/code&gt; and &lt;code&gt;gradientwidget&lt;/code&gt; are different classes as far as Python is concerned.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The widget doesn't resize properly&lt;/strong&gt; &amp;mdash; Make sure you've added the promoted widget to a layout in Qt Designer. Widgets outside of layouts won't resize with the window, regardless of whether they're promoted or not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changes to your custom widget don't appear in Designer&lt;/strong&gt; &amp;mdash; Remember, Qt Designer can't render Python widgets. You'll always see the base widget type in the Designer preview. Run your application to see your custom widget.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Widget promotion is a straightforward way to bridge the gap between Qt Designer's visual layout tools and your custom Python widgets. The process is always the same:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Place a base widget of the appropriate type in Qt Designer.&lt;/li&gt;
&lt;li&gt;Right-click and promote it, specifying your custom class name and module path.&lt;/li&gt;
&lt;li&gt;Save the &lt;code&gt;.ui&lt;/code&gt; file and load it in your Python application.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your custom widget won't be visible in the Designer preview&amp;mdash;that's expected. But when your application runs, the promoted widget is swapped in seamlessly, giving you the best of both worlds: visual layout design with the full power of custom Python widgets.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href=&quot;https://www.pythonguis.com/pyqt6-book/&quot;&gt;Create GUI Applications with Python &amp;amp; Qt6.&lt;/a&gt;&lt;/p&gt;</description>
	<pubDate>Wed, 13 May 2026 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Bob Belderbos: Coding exercises that run in the browser with Pyodide</title>
	<guid>https://belderbos.dev/blog/python-exercises-browser-pyodide/</guid>
	<link>https://belderbos.dev/blog/python-exercises-browser-pyodide/</link>
	<description>&lt;p&gt;I've built coding-exercise platforms before (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pybitesplatform.com&quot;&gt;Python&lt;/a&gt;, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://rustplatform.com&quot;&gt;Rust&lt;/a&gt;). AWS API Gateway + Lambda, Docker, etc. It works great, but that's a lot of infrastructure to teach someone a four-line function.&lt;/p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;/span&gt;
&lt;p&gt;For our new &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pythonagenticai.com&quot;&gt;Agentic AI cohort&lt;/a&gt; I wanted a free warm-up: ten short Python exercises that introduce the AI vendor SDK patterns (in this case Anthropic). The hard constraint was that visitors should be able to click &quot;Run&quot; without signing up, without bringing an API key, and without complex third party infrastructure. As this site is built on Cloudflare Pages, that meant an in-browser Python runtime. Enter Pyodide ...&lt;/p&gt;
&lt;p&gt;Unlike toy Python interpreters, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pyodide.org&quot;&gt;Pyodide&lt;/a&gt; runs real CPython compiled to WebAssembly (listen to my interview &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.youtube.com/watch?v=MAK6nApfIxY&quot;&gt;Elmer Bulthuis&lt;/a&gt; why Wasm is cool), which enables broad compatibility with the Python ecosystem, including native extension packages.&lt;/p&gt;
&lt;p&gt;Getting it working was easy with some Claude Code prototyping; the interesting part was the last 20%. Some of the challenges I faced and how I worked around them.&lt;/p&gt;
&lt;h2 id=&quot;mocked-tests-a-stubbed-sdk&quot;&gt;Mocked tests + a stubbed SDK&lt;/h2&gt;
&lt;p&gt;Every exercise has a &lt;code&gt;solution.py&lt;/code&gt; and a &lt;code&gt;test_exercise.py&lt;/code&gt;. The tests look like this:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;/span&gt;&lt;span&gt; unittest.mock&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;/span&gt;&lt;span&gt; MagicMock, patch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;/span&gt;&lt;span&gt; solution&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;/span&gt;&lt;span&gt; get_completion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; test_returns_text&lt;/span&gt;&lt;span&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mock_client&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; MagicMock()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mock_client.messages.create.return_value.content&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; [MagicMock(&lt;/span&gt;&lt;span class=&quot;z-variable&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;Hello, Pythonista!&amp;quot;&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    with&lt;/span&gt;&lt;span&gt; patch(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;solution.anthropic.Anthropic&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-variable&quot;&gt; return_value&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;/span&gt;&lt;span&gt;mock_client):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        assert&lt;/span&gt;&lt;span&gt; get_completion(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;Say hello&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;Hello, Pythonista!&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;patch(&quot;solution.anthropic.Anthropic&quot;)&lt;/code&gt; replaces the class with a mock for the duration of the &lt;code&gt;with&lt;/code&gt; block. The original &lt;code&gt;Anthropic&lt;/code&gt; class is never instantiated. Which means the only thing the &lt;em&gt;real&lt;/em&gt; SDK contributes is the &lt;em&gt;name&lt;/em&gt; &lt;code&gt;anthropic.Anthropic&lt;/code&gt; existing somewhere on the Python path.&lt;/p&gt;
&lt;p&gt;So I don't install it. I write a tiny stub package straight to Pyodide's in-browser filesystem:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ANTHROPIC_INIT&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;class Anthropic:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    def __init__(self, *args, **kwargs):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ANTHROPIC_TYPES&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;class TextBlock: ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;class MessageParam: ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;class ToolParam: ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;class ToolUseBlock: ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; pyodide.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;loadPackage&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;pytest&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;pydantic&amp;quot;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pyodide.&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;FS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;mkdirTree&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;/home/pyodide/anthropic&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pyodide.&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;FS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;writeFile&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;/home/pyodide/anthropic/__init__.py&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ANTHROPIC_INIT&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pyodide.&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;FS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;writeFile&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;/home/pyodide/anthropic/types.py&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ANTHROPIC_TYPES&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's a package, not a single file, because some exercises also do &lt;code&gt;from anthropic.types import TextBlock&lt;/code&gt;, which I needed to fix &lt;code&gt;ty&lt;/code&gt; type errors. Both modules exist only so the imports resolve. The bodies never execute under test thanks to the mocking.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Inside Pyodide, before running pytest:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sys.path.insert(&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;/home/pyodide&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# `import anthropic` finds the stub. `patch` replaces it. Tests run.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That one decision cuts ~3 seconds and several megabytes off the boot. The real &lt;code&gt;anthropic&lt;/code&gt; package pulls in &lt;code&gt;pydantic-core&lt;/code&gt;, &lt;code&gt;httpx&lt;/code&gt;, &lt;code&gt;httpcore&lt;/code&gt;, &lt;code&gt;anyio&lt;/code&gt;, &lt;code&gt;sniffio&lt;/code&gt;, &lt;code&gt;idna&lt;/code&gt;, &lt;code&gt;distro&lt;/code&gt;, &lt;code&gt;certifi&lt;/code&gt;, &lt;code&gt;typing-extensions&lt;/code&gt;. Every byte irrelevant to learning the pattern, because the test never lets the SDK run anyway.&lt;/p&gt;
&lt;p&gt;If you've read &lt;a href=&quot;https://belderbos.dev/blog/build-data-layer-before-llm/&quot;&gt;build the data layer before you touch the LLM&lt;/a&gt;, this is the same strategy: cut the AI piece down to its smallest shape so the rest of the engineering is more flexible.&lt;/p&gt;
&lt;h2 id=&quot;lazy-loading-the-runtime&quot;&gt;Lazy-loading the runtime&lt;/h2&gt;
&lt;p&gt;Pyodide is 5MB+ over the network. I don't want this to load on the homepage, not even on the exercise index page. Even on an exercise page, visitors might skim and leave. So the &lt;code&gt;pyodide.js&lt;/code&gt; script tag isn't in the HTML. The page ships a ~250-line &lt;code&gt;runner.js&lt;/code&gt; and that script injects Pyodide on demand:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;// Module-level constants, defined once at the top of runner.js:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; PYODIDE_VERSION&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;0.27.7&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; PYODIDE_URL&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; `https://cdn.jsdelivr.net/pyodide/v${&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;PYODIDE_VERSION&lt;/span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}/full/`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; PYODIDE_JS_SRI&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;sha384-90so5tCKvl0xs9agU29IMKlAVzhfzFX7QO//YxQkRhJG58bBZrFN+2ZTRB026X5X&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-storage z-type&quot;&gt;async function&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ensurePyodide&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  if&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; (pyodide)&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; return&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; pyodide;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  if&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; (bootPromise)&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; return&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; bootPromise;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;  bootPromise&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-storage&quot;&gt;async&lt;/span&gt;&lt;span&gt; ()&lt;/span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; loadPyodide&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !==&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;function&amp;quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;      await new&lt;/span&gt;&lt;span class=&quot;z-support&quot;&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span class=&quot;z-variable&quot;&gt;resolve&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-variable&quot;&gt; reject&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;        const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; s&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; document.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        s.src&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; PYODIDE_URL&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;pyodide.js&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        s.integrity&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; PYODIDE_JS_SRI&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        s.crossOrigin&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;anonymous&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        s.onload&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; resolve;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        s.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;onerror&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span&gt; ()&lt;/span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; reject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;Failed to load pyodide.js&amp;quot;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        document.head.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(s);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    pyodide&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; = await&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; loadPyodide&lt;/span&gt;&lt;span&gt;({ indexURL:&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; PYODIDE_URL&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    await&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; pyodide.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;loadPackage&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;pytest&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;pydantic&amp;quot;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    // write the anthropic stub here…&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; pyodide;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  return&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; bootPromise;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two triggers prewarm the runtime &lt;em&gt;before&lt;/em&gt; the user clicks Run:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cm.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;focus&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;, prewarm);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;runBtn.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;mouseenter&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;, prewarm, { once:&lt;/span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The moment they tab into the editor or hover the button, the 3-second cold start starts ticking. By the time they're done typing, the runtime is usually ready. The cached &lt;code&gt;bootPromise&lt;/code&gt; deduplicates: focus and hover both await the same in-flight promise, never two parallel boots.&lt;/p&gt;
&lt;h2 id=&quot;tracking-progress-without-a-backend&quot;&gt;Tracking progress without a backend&lt;/h2&gt;
&lt;p&gt;No users, no database, no sessions, but I still want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✓ badges on completed exercises in the list view&lt;/li&gt;
&lt;li&gt;A progress bar across all ten&lt;/li&gt;
&lt;li&gt;Draft code that survives a tab close&lt;/li&gt;
&lt;li&gt;A next step that only appears once all ten are green&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One &lt;code&gt;localStorage&lt;/code&gt; key holds the whole state:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; STORAGE_KEY&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;pyai_progress_v1&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;// { &amp;quot;first-api-call&amp;quot;: { passed: true, code: &amp;quot;...&amp;quot;, lastRun: 1736... } }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three operations carry the state: &lt;code&gt;saveCode(slug, code)&lt;/code&gt; runs on every CodeMirror change, &lt;code&gt;markPassed(slug)&lt;/code&gt; runs when pytest returns 0, and &lt;code&gt;get(slug)&lt;/code&gt; reads on page load to restore drafts and badges.&lt;/p&gt;
&lt;p&gt;In a similar vein, the &lt;strong&gt;Solution&lt;/strong&gt; tab stays locked until the tests pass. The point of an exercise is the struggle, not the answer.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;markPassed(slug)&lt;/code&gt; writes to localStorage, it also fires a &lt;code&gt;pyai:passed&lt;/code&gt; event, and a separate &lt;code&gt;tabs.js&lt;/code&gt; listener flips the solution from &lt;code&gt;&amp;lt;div data-solution-locked&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;div data-solution-revealed&amp;gt;&lt;/code&gt; and lazy-fetches &lt;code&gt;solution.py&lt;/code&gt; for a side-by-side compare. No reload. One key, three consumers (runner, list page, solution tab).&lt;/p&gt;
&lt;p&gt;And the key is versioned: &lt;code&gt;pyai_progress_v1&lt;/code&gt;. The day I want to change the shape, I can bump it to &lt;code&gt;_v2&lt;/code&gt; and old state cleanly stops loading. No migration code, no schema check.&lt;/p&gt;
&lt;p&gt;The list page reads the same store on render and walks the DOM:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;document.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;.exercises-list-item&amp;quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span class=&quot;z-variable&quot;&gt;item&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; slug&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; item.dataset.exerciseSlug;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  const&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; passed&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; window.PyAIProgress.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(slug);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  if&lt;/span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; (passed) item.classList.&lt;/span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;is-passed&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;passedCount() &amp;gt;= total&lt;/code&gt;, a hidden next-step block flips visible. That's the whole mechanism: ten green checks reveal one element, all computed in the browser from that one &lt;code&gt;localStorage&lt;/code&gt; key.&lt;/p&gt;
&lt;h2 id=&quot;all-static-all-local&quot;&gt;All static, all local&lt;/h2&gt;
&lt;p&gt;The whole thing is a static site. Cloudflare serves the HTML, JS, and the synced exercise files. The browser does the rest. Zero extra cost. It scales for free because the load is on the client, not a server.&lt;/p&gt;
&lt;p&gt;For development, &lt;a href=&quot;https://belderbos.dev/blog/modern-python-tooling-uv-ruff-ty/&quot;&gt;uv runs the end-to-end check&lt;/a&gt; with a single command:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-string&quot;&gt; run scripts/e2e_test.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It walks every exercise in headless Chromium, pastes the reference solution, clicks Run, asserts the test suite passes. Ten exercises in ~22 seconds. Anytime the upstream content changes I know in under half a minute whether all ten warm-ups still pass end-to-end. I will save the details of this Playwright end-to-end testing for another article.&lt;/p&gt;
&lt;h2 id=&quot;starter-code&quot;&gt;Starter code&lt;/h2&gt;
&lt;p&gt;The site this runs on is standalone so I put together a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://gist.github.com/bbelderbos/4921cc7aba4d78f99b9bacbd49c8f934&quot;&gt;single-file Pyodide starter gist&lt;/a&gt; of a mini coding platform experience: code in the browser, click &quot;Run tests&quot;, pytest runs against your code, all in the browser. Lazy boot and the Solution/Tests tabs are wired up. The SDK stub and localStorage progress I left out for simplicity, but the core Pyodide integration is there. You can download and build on it if you want to try your hand at a browser-based Python coding experience.&lt;/p&gt;
&lt;h2 id=&quot;try-it-out&quot;&gt;Try it out&lt;/h2&gt;
&lt;p&gt;Back to the 10 exercises, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://pythonagenticai.com/exercises/&quot;&gt;you can try them out here&lt;/a&gt;. They cover the basics that show up in the typical production Agentic AI app: a first API call, structured outputs with Pydantic, system prompts, multi-turn state, tool use, then the architectural patterns (Protocol, Repository, Service layer, HITL, the agent loop).&lt;/p&gt;
&lt;h2 id=&quot;keep-reading&quot;&gt;Keep reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://belderbos.dev/blog/ai-agent-architecture-python/&quot;&gt;How an AI expense agent is actually structured&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://belderbos.dev/blog/build-data-layer-before-llm/&quot;&gt;Build the data layer before you touch the LLM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://belderbos.dev/blog/modern-python-tooling-uv-ruff-ty/&quot;&gt;Modern Python tooling: uv, ruff, ty&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One bigger lesson I'm taking away from this: every time I've built a thing server-side over the years, I was usually paying a complexity tax for flexibility I didn't need. Sometimes the right architecture is to push the work to the client, especially where modern browsers and Wasm can handle this performantly and securely.&lt;/p&gt;</description>
	<pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate>
</item>
<item>
	<title>PyCoder’s Weekly: Issue #734: Dunder-Gets, Django Tasks in Prod, Codex CLI, and More (2026-05-12)</title>
	<guid>https://pycoders.com/issues/734</guid>
	<link>https://pycoders.com/issues/734</link>
	<description>&lt;p&gt; &lt;span&gt;#734 – MAY 12, 2026&lt;/span&gt;&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/issues/734/feed&quot;&gt;View in Browser »&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://pycoders.com&quot;&gt;&lt;img alt=&quot;The PyCoder&amp;rsquo;s Weekly Logo&quot; src=&quot;https://cdn.pycoders.com/37bdf31dc645f968ffb90196e5d38ff5&quot; /&gt;&lt;/a&gt;&lt;/p&gt; &lt;hr /&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16439/feed&quot; target=&quot;_blank&quot;&gt;Do You Get It Now?&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Learn about Python&amp;rsquo;s &lt;code&gt;.__getitem__()&lt;/code&gt;, &lt;code&gt;.__getattr__()&lt;/code&gt;, &lt;code&gt;.__getattribute__()&lt;/code&gt;, and &lt;code&gt;.__get__()&lt;/code&gt;: how they&amp;rsquo;re different and where to use them.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16439/feed&quot; target=&quot;_blank&quot;&gt;STEPHEN GRUPPETTA&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16445/feed&quot; target=&quot;_blank&quot;&gt;Using Django Tasks in Production&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Django added a generic API for dealing with concurrent tasks in version 6. This post talks about how it has been used in production.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16445/feed&quot; target=&quot;_blank&quot;&gt;TIM SCHILLING&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16430/feed&quot; target=&quot;_blank&quot;&gt;Use Codex CLI to Enhance Your Python Projects&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Learn how to use Codex CLI to add features to Python projects directly from your terminal, without needing a browser or IDE plugins.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16430/feed&quot; target=&quot;_blank&quot;&gt;REAL PYTHON&lt;/a&gt;&lt;/span&gt; &lt;span&gt;course&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16459/feed&quot; target=&quot;_blank&quot;&gt;Depot CI: Built for the Agent era&lt;/a&gt;&lt;/h3&gt; &lt;a href=&quot;https://pycoders.com/link/16459/feed&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://cdn.pycoders.com/633447d4ba4a284f4456b6f98ec4d4fa&quot; alt=&quot;alt&quot; /&gt;&lt;/a&gt; &lt;p&gt; Depot CI: A new CI engine. Fast by design. Your GitHub Actions workflows, running on a fundamentally faster engine — instant job startup, parallel steps, full debuggability, per-second billing. &lt;a href=&quot;https://pycoders.com/link/16459/feed&quot; target=&quot;_blank&quot;&gt;One command to migrate →&lt;/a&gt;&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16459/feed&quot; target=&quot;_blank&quot;&gt;DEPOT&lt;/a&gt;&lt;/span&gt; &lt;span&gt;sponsor&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16453/feed&quot; target=&quot;_blank&quot;&gt;PEP 828: Supporting &amp;lsquo;Yield From&amp;rsquo; in Asynchronous Generators (Deferred to 3.16)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16453/feed&quot; target=&quot;_blank&quot;&gt;PYTHON.ORG&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16456/feed&quot; target=&quot;_blank&quot;&gt;PEP 797: Shared Object Proxies (Deferred to 3.16)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16456/feed&quot; target=&quot;_blank&quot;&gt;PYTHON.ORG&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16438/feed&quot; target=&quot;_blank&quot;&gt;Django Security Releases: 6.0.5 and 5.2.14&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16438/feed&quot; target=&quot;_blank&quot;&gt;DJANGO SOFTWARE FOUNDATION&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;h2&gt;Articles &amp;amp; Tutorials&lt;/h2&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16431/feed&quot; target=&quot;_blank&quot;&gt;Handling Schema Issues in Polars&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; You&amp;rsquo;ve got this great data pipeline going until one day it stops working. A schema error causes by a column upstream has stopped you in your tracks. This post talks about the four different causes of schema errors and what to do about them.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16431/feed&quot; target=&quot;_blank&quot;&gt;THIJS NIEUWDORP&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16436/feed&quot; target=&quot;_blank&quot;&gt;Textual: An Intro to DOM Queries (Part II)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Textual is a TUI framework library for building terminal applications. It uses a DOM to represent the widgets in the application, and that DOM is queryable. This is part 2 in a series on how to find things in your Textual DOM.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16436/feed&quot; target=&quot;_blank&quot;&gt;MIKE DRISCOLL&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16443/feed&quot; target=&quot;_blank&quot;&gt;Everything You Always Wanted to Know About PyCon Sprints!&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; PyCon US includes coding sprints to work on CPython itself, or projects in the ecosystem like Django, Flask, and BeeWare. This post tells you all about sprints and how you can join in on the fun.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16443/feed&quot; target=&quot;_blank&quot;&gt;DEB NICHOLSON&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16440/feed&quot; target=&quot;_blank&quot;&gt;Why TUIs Are Back&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Terminal User Interfaces are seeing a resurgence in the tools space. This opinion piece briefly talks about the history of interfaces and why we are where we are now.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16440/feed&quot; target=&quot;_blank&quot;&gt;ALCIDES FONSECA&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16455/feed&quot; target=&quot;_blank&quot;&gt;Parallel Python at Anyscale With Ray&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Talk Python interviews Richard Liaw and Edward Oakes. They talk about Ray, an open source Python framework a distributed execution engine for AI workloads.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16455/feed&quot; target=&quot;_blank&quot;&gt;TALK PYTHON&lt;/a&gt;&lt;/span&gt; &lt;span&gt;podcast&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16432/feed&quot; target=&quot;_blank&quot;&gt;Python 3.14.5 Release Candidate&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Normally nobody fusses over a release candidate of a point release, but 3.14.5 includes a major change: rolling back of the incremental garbage collector.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16432/feed&quot; target=&quot;_blank&quot;&gt;HUGO VAN KEMENADE&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16449/feed&quot; target=&quot;_blank&quot;&gt;Wagtail 7.4: Custom Page Explorer, Preview Checks &amp;amp; More&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Between autosave improvements, new ways to sort your pages, and a content checker upgrade, you&amp;rsquo;ll have a lot of reasons to move to Wagtail 7.4&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16449/feed&quot; target=&quot;_blank&quot;&gt;MEAGEN VOSS&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16450/feed&quot; target=&quot;_blank&quot;&gt;The Simplest MCP Example Possible in Python&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; This guide introduces you to connecting your code to a local LLM model. It covers Ollama and FastMCP and what you can do with these tools.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16450/feed&quot; target=&quot;_blank&quot;&gt;AL SWEIGART&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16458/feed&quot; target=&quot;_blank&quot;&gt;ChatterBot: Build a Chatbot With Python&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Build a Python chatbot with the ChatterBot library. Clean real conversation data, train on custom datasets, and add local AI with Ollama.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16458/feed&quot; target=&quot;_blank&quot;&gt;REAL PYTHON&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16444/feed&quot; target=&quot;_blank&quot;&gt;Hardening Firefox With Claude Mythos Preview&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; New details about what Mozilla found and how agentic harnesses helped them reproduce real bugs and dismiss false positives.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16444/feed&quot; target=&quot;_blank&quot;&gt;MOZILLA&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;h2&gt;Projects &amp;amp; Code&lt;/h2&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16457/feed&quot; target=&quot;_blank&quot;&gt;pytest-fly: pytest Observer&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16457/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/JAMESABEL&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16434/feed&quot; target=&quot;_blank&quot;&gt;Pymetrica: A Codebase Analysis Tool&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16434/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/JUANJFARINA&lt;/a&gt; • Shared by Juan José Farina&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16446/feed&quot; target=&quot;_blank&quot;&gt;PyWry: Cross-Platform Rendering Engine and UI Toolkit&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16446/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/DEELEERAMONE&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16454/feed&quot; target=&quot;_blank&quot;&gt;secure: HTTP Security Headers for FastAPI, Flask, Django&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16454/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/TYPEERROR&lt;/a&gt; • Shared by &lt;a href=&quot;https://pycoders.com/link/16433/feed&quot; target=&quot;_blank&quot;&gt;Caleb Kinney&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16451/feed&quot; target=&quot;_blank&quot;&gt;Kirokyu: Modular Task Management System&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16451/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/AMRYOUNIS&lt;/a&gt; • Shared by Amr Younis&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;h2&gt;Events&lt;/h2&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16448/feed&quot; target=&quot;_blank&quot;&gt;Weekly Real Python Office Hours Q&amp;amp;A (Virtual)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; May 13, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16448/feed&quot; target=&quot;_blank&quot;&gt;REALPYTHON.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16435/feed&quot; target=&quot;_blank&quot;&gt;PyCon US 2026&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; May 13 to May 20, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16435/feed&quot; target=&quot;_blank&quot;&gt;PYCON.ORG&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16441/feed&quot; target=&quot;_blank&quot;&gt;Python Atlanta&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; May 14 to May 15, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16441/feed&quot; target=&quot;_blank&quot;&gt;MEETUP.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16437/feed&quot; target=&quot;_blank&quot;&gt;Chattanooga Python User Group&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; May 15 to May 16, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16437/feed&quot; target=&quot;_blank&quot;&gt;MEETUP.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16442/feed&quot; target=&quot;_blank&quot;&gt;PyDelhi User Group Meetup&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; May 16, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16442/feed&quot; target=&quot;_blank&quot;&gt;MEETUP.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16447/feed&quot; target=&quot;_blank&quot;&gt;PyData London&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; June 5 to June 7, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16447/feed&quot; target=&quot;_blank&quot;&gt;PYDATA.ORG&lt;/a&gt; • Shared by &lt;a href=&quot;https://pycoders.com/link/16452/feed&quot; target=&quot;_blank&quot;&gt;Tomara Youngblood&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;hr /&gt; &lt;p&gt;Happy Pythoning!&lt;br /&gt;This was PyCoder&amp;rsquo;s Weekly Issue #734.&lt;br /&gt;&lt;a href=&quot;https://pycoders.com/issues/734/feed&quot;&gt;View in Browser »&lt;/a&gt;&lt;/p&gt; &lt;img src=&quot;https://pycoders.com/issues/734/open/feed&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;alt&quot; /&gt; 
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Subscribe to 🐍 PyCoder&amp;rsquo;s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week &lt;a href=&quot;https://pycoders.com/?utm_source=pycoders&amp;utm_medium=feed&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Tue, 12 May 2026 19:30:00 +0000</pubDate>
</item>
<item>
	<title>Marcos Dione: Monitoring Apache with SQL and Grafana</title>
	<guid>https://www.grulic.org.ar/~mdione/glob/posts/monitoring-apache-with-sql-and-grafana/</guid>
	<link>https://www.grulic.org.ar/~mdione/glob/posts/monitoring-apache-with-sql-and-grafana/</link>
	<description>&lt;p&gt;Ever since my last job I have been wanting to make this. I think it's not the first time I do it, but
for one reason or another, I did it (again?) in two evenings only.&lt;/p&gt;
&lt;p&gt;In that job we had an internet facing API with Apache as the router in front of several services. All
our metrics and even our billing was based on the Apache logs. We had a system that ingested the logs
into a PostgreSQL database, and we tried to create Grafana panels and alerts based on that info. At
the same time, I wanted to reproduce &lt;a href=&quot;https://www.awstats.org/&quot;&gt;&lt;code&gt;awstats&lt;/code&gt;&lt;/a&gt; in Grafana, and found it was 
almost impossible.&lt;/p&gt;
&lt;p&gt;Another problem is that the usual tools to solve this, Loki or Prometheus, have big problems to handle this
type of too arbitrary data (think of the &lt;code&gt;referer&lt;/code&gt; or &lt;code&gt;user_agent&lt;/code&gt; columns) or whose space is too big
(&lt;code&gt;client&lt;/code&gt; is an IPv4, with 4Bi different values). They effectively suffer (in principle) of what they 
call &quot;cardinality bomb&quot;: since they build one time series database (TSDB) per combination of fields 
(they call them &quot;labels&quot;), storage use is big and aggregation operations inter TSDBs are expensive.&lt;/p&gt;
&lt;p&gt;Last night I sat down to reimplement the ingestion side. Instead of PostgreSQL I used SQLite mostly
because almost all of my services (with low traffic and mostly only me as user) already use it. To be fair,
and one really can't expect anything else, the script is quite straight forward. It uses regexps to 
parse the logs, which for the moment is good enough. I'm &quot;releasing&quot; it as is, because I'm tired, but you'll
find some surprises around parsing the request line (see &lt;code&gt;request_re&lt;/code&gt; and its handling); some janky ways
to convert from &lt;code&gt;str&lt;/code&gt; to &lt;code&gt;int&lt;/code&gt; or &lt;code&gt;datetime&lt;/code&gt;; and an iteration trick to use &lt;code&gt;dataclass&lt;/code&gt;es as &lt;code&gt;execute()&lt;/code&gt;
argument. I omited some comments and all the testing:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre class=&quot;code literal-block&quot;&gt;&lt;span class=&quot;ch&quot;&gt;#! /usr/bin/env python3&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;dataclasses&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataclass&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tzinfo&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;pathlib&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;sqlite3&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# I miss dinant&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# no 0-255 range check since this is written by apache&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# if the number is not in that range, we have bigger problems&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;octet_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'\d{1,3}'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ip_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'\.'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;octet_re&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;word_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'[^ ]+'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;identd_user_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word_re&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# it can be '-'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_id_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word_re&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# it can be '-'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;month_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Jan'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Feb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Mar'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Apr'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'May'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Jun'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Jul'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Aug'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Sep'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Oct'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Nov'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Dec'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;day_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'\d{1,2}'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;month_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;(?:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'|'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;month_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;year_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'\d&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{4}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;date_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)/(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;month_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)/(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;time_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'(\d&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;):(\d&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;):(\d&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;)'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;utc_offset_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'(?:\+|\-)\d&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{4}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# no capture&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# fscking double escaping :(&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;date_time_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utc_offset_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;]&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;method_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word_re&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;url_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word_re&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# technically not a word, but word_re is too generic&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;proto_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'HTTP'&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# who are we kidding&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;version_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'\d\.\d'&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# who are we kidding&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;proto_and_version_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proto_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)/(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# idiot skrip kidz send no method or proto/version!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# and re is silly? enough to produce empty matches for the ()s here&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# oh, but re.compile().match().groups() returns things like&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# (None, None, None, None, '', '\\x16\\x03\\x02\\x01o\\x01', '', '')&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# so we gained nothing&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;request_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&quot;(?:(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;) (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proto_and_version_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;|()(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;)()())&quot;'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;number_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'\d+'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;http_status_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number_re&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bytes_rx_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number_re&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bytes_tx_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number_re&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ttfb_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;(?:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;|-)&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response_time_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number_re&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;double_quoted_text_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&quot;([^&quot;]+)&quot;'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;referer_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double_quoted_text_re&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user_agent_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double_quoted_text_re&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;log_line_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ip_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;identd_user_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_time_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http_status_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes_rx_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes_tx_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttfb_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response_time_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;referer_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_agent_re&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;LogRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client_ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# 0&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;indent_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;date_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;               &lt;span class=&quot;c1&quot;&gt;# 5&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;protocol_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# could be float, but we don't really care; besides, x.y.z?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bytes_rx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bytes_tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# 10&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ttfb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# maybe -!&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;referer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user_agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_log_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log_line_re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Malformed line: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;new_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__dataclass_fields__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;# [11/May/2026:20:15:28 +0200]&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;# convert month str to number&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month_names&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;# convert to ints&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;new_data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                                          &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                          &lt;span class=&quot;n&quot;&gt;utc_offset2tzinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;# handle ttfb as -&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'-'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;# &lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;# handle (None, None, None, None, '', '\\x16\\x03\\x02\\x01o\\x01', '', '')&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;# handle ('GET', '/', 'HTTP', '1.0', None, None, None, None)&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;# no need to add anything, it's handled by the fallback&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;# but we still need to skip this cruft&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Got confused: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;# convert ints&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;# fallback&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;new_data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;group_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


    &lt;span class=&quot;c1&quot;&gt;# implement the iterator protocol so we can mostly be passed as argument to execute()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__iter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;


    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__iter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;vm&quot;&gt;__dict__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# the whole protocol could be replaced with .__dataclass_fields__.values() :shrug:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# but this way I can do further conversions&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;utc_offset2tzinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tzinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# +0200&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# +02&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;minutes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:])&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 00&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# if we test after sqlite3.connect(), the file is already created&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pathlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'./apache_logs.db'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlite3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'./apache_logs.db'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'''&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;CREATE TABLE &quot;logs&quot; (&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;client&quot;    TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;indent_user&quot;   TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;user_id&quot;   TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;timestamp&quot; INTEGER,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;method&quot;    TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;url&quot;   TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;protocol&quot;  TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;protocol_version&quot;  TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;status&quot;    INTEGER,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;bytes_rx&quot;  INTEGER,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;bytes_tx&quot;  INTEGER,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;ttfb&quot;  INTEGER,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;response_time&quot; INTEGER,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;referer&quot;   TEXT,&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;    &quot;user_agent&quot;    TEXT&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;);'''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;



&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;log_record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_log_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ValueError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'''INSERT INTO logs VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log_record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'__main__'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One of the things I didn't do was to further play with the URLs. One could make list of different apps
based on whether there is any routing to different services, like in the cases of my previous job and
my own server; or even different subdivisions on a single app, like for NextCloud:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre class=&quot;code literal-block&quot;&gt;&lt;span class=&quot;n&quot;&gt;ocs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serverinfo&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;remote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dav&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;USER&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;remote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dav&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CALENDAR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;etc. I haven't really thought about it; it could be implemented either as more columns or extra tables.&lt;/p&gt;
&lt;p&gt;Today I managed to finish the rest.&lt;/p&gt;
&lt;p&gt;The next step is to install this so it runs constantly with the output of &lt;code&gt;tail --follow=name --retry&lt;/code&gt;
piped into its &lt;code&gt;stdin&lt;/code&gt;&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.grulic.org.ar/~mdione/glob/posts/monitoring-apache-with-sql-and-grafana/#fn:1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Left as an exercise for the reader; use SystemD units :)&lt;/p&gt;
&lt;p&gt;Next is installing &lt;a href=&quot;https://github.com/fr-ser/grafana-sqlite-datasource&quot;&gt;Grafana's plugin to read SQLite&lt;/a&gt;
and declare the new Grafana datasource.&lt;/p&gt;
&lt;p&gt;The hard part was to query the data in a way that was useful for Grafana. I managed to get a query like:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre class=&quot;code literal-block&quot;&gt;&lt;span class=&quot;c1&quot;&gt;-- round to the minute&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&quot;count&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logs&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- $__from and $__to are defined by Grafana based on the dashboards's time range&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;to get the count of different status codes per minute&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.grulic.org.ar/~mdione/glob/posts/monitoring-apache-with-sql-and-grafana/#fn:2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. But this returns a table that looks like:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre class=&quot;code literal-block&quot;&gt;      time | status | count
1778533620 |    200 |    30
1778533620 |    207 |     3
1778533620 |    403 |     1
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;while Grafana is expecting one line per sample (but remember we're aggregating data) and one column 
per data series:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre class=&quot;code literal-block&quot;&gt;      time | 200 | 207 | 403
1778533620 |  30 |   3 |   1
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I read how to pivot this in SQL, but it mostly works only if you know the different values for the
&lt;code&gt;status&lt;/code&gt; column beforehand. This might be feasible with 
&lt;a href=&quot;https://en.wikipedia.org/w/index.php?title=List_of_HTTP_status_codes&quot;&gt;HTTP status codes&lt;/a&gt; (I count
63 standard ones, including the joke &lt;code&gt;418 I'm a teapot&lt;/code&gt;), but that would be impossible for the &lt;code&gt;referer&lt;/code&gt;
or &lt;code&gt;user_agent&lt;/code&gt; columns.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;code&gt;iRobbery#postgresql@libera.chat&lt;/code&gt; I found about 
&lt;a href=&quot;https://community.grafana.com/t/merging-rows-with-same-timestamp-for-time-series/103254/3&quot;&gt;Grafana's Partition by values data transformation&lt;/a&gt;.
Aplying it to the column that defines the time series (&lt;code&gt;status&lt;/code&gt;, etc), it gives us exactly what we want!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.grulic.org.ar/~mdione/glob/images/Screenshot_20260512_210706.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And one can even include a pure table with all the logs to inspect when one finds weird spikes or values.
I made almost impossible queries like transferred bytes per URL, methods per client and more! One
missing piece, i possible, would be to implement histograms, 
&lt;a href=&quot;https://www.grulic.org.ar/~mdione/glob/posts/monitoring-apache-with-prometheus-and-grafana/&quot;&gt;like last time we looked into this&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;footnote&quot;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&quot;fn:1&quot;&gt;
&lt;p&gt;One could cite the UNIX philosophy, but seriously, who wants to reimplement all the corner cases
  of that &lt;code&gt;tail&lt;/code&gt; invocation? See for instance 
  &lt;a href=&quot;https://www.phoronix.com/news/Ubuntu-Rust-Coreutils-Audit&quot;&gt;the 113 bugs found in the coreutils Rust reimplementaiton&lt;/a&gt; &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.grulic.org.ar/~mdione/glob/posts/monitoring-apache-with-sql-and-grafana/#fnref:1&quot; title=&quot;Jump back to footnote 1 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn:2&quot;&gt;
&lt;p&gt;One could use a dashboard variable to control this arbitrarily. One could get granularity 
  per second! &lt;a class=&quot;footnote-backref&quot; href=&quot;https://www.grulic.org.ar/~mdione/glob/posts/monitoring-apache-with-sql-and-grafana/#fnref:2&quot; title=&quot;Jump back to footnote 2 in the text&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
	<pubDate>Tue, 12 May 2026 18:07:46 +0000</pubDate>
</item>
<item>
	<title>Real Python: Building Type-Safe LLM Agents With Pydantic AI</title>
	<guid>https://realpython.com/courses/building-type-safe-llm-agents-with-pydantic-ai/</guid>
	<link>https://realpython.com/courses/building-type-safe-llm-agents-with-pydantic-ai/</link>
	<description>&lt;p&gt;Pydantic AI is a Python framework for building LLM agents that return validated, structured outputs using Pydantic models. Instead of parsing raw strings from LLMs, you get type-safe objects with automatic validation.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve used FastAPI or Pydantic before, then you&amp;rsquo;ll recognize the familiar pattern of defining schemas with type hints and letting the framework handle the type validation for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this video course, you&amp;rsquo;ll understand that:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pydantic AI&lt;/strong&gt; uses &lt;code&gt;BaseModel&lt;/code&gt; classes to define structured outputs that guarantee &lt;strong&gt;type safety&lt;/strong&gt; and automatic &lt;strong&gt;validation&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;@agent.tool&lt;/code&gt; &lt;strong&gt;decorator&lt;/strong&gt; registers Python functions that &lt;strong&gt;LLMs can invoke&lt;/strong&gt; based on user queries and docstrings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency injection&lt;/strong&gt; with &lt;code&gt;deps_type&lt;/code&gt; provides &lt;strong&gt;type-safe&lt;/strong&gt; runtime context like database connections without using &lt;strong&gt;global state&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validation retries&lt;/strong&gt; automatically rerun queries when the LLM returns invalid data, which increases &lt;strong&gt;reliability&lt;/strong&gt; but also &lt;strong&gt;API costs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Gemini&lt;/strong&gt;, &lt;strong&gt;OpenAI&lt;/strong&gt;, and &lt;strong&gt;Anthropic&lt;/strong&gt; models support structured outputs best, while other providers have &lt;strong&gt;varying capabilities&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Tue, 12 May 2026 14:00:00 +0000</pubDate>
</item>
<item>
	<title>Django Weblog: 2026 Django Developers Survey</title>
	<guid>https://www.djangoproject.com/weblog/2026/may/12/2026-django-developers-survey/</guid>
	<link>https://www.djangoproject.com/weblog/2026/may/12/2026-django-developers-survey/</link>
	<description>&lt;p&gt;The Django Software Foundation is once again partnering with JetBrains to run the 2026 &lt;a href=&quot;https://surveys.jetbrains.com/s3/wb-django-developers-survey-2026&quot;&gt;Django Developers Survey&lt;/a&gt; 🌈&lt;/p&gt;
&lt;p&gt;It’s an important metric of Django usage and is immensely helpful to guide future technical and community decisions.&lt;/p&gt;
&lt;p&gt;After the survey closes, we will publish the aggregated results. JetBrains will also randomly select 10 winners (from those who complete the survey in full with meaningful answers) who will each receive a $100 Amazon voucher or the equivalent in local currency.&lt;/p&gt;
&lt;h3 id=&quot;s-how-you-can-help&quot;&gt;How you can help&lt;/h3&gt;
&lt;p&gt;Once you’ve done the survey, take a moment to re-share on socials and with your communities. The more diverse the answers, the better the results for all of us.&lt;/p&gt;
&lt;p&gt;Please use the following links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Bluesky&lt;br /&gt;
&lt;a href=&quot;https://surveys.jetbrains.com/s3/bs-django-developers-survey-2026&quot;&gt;https://surveys.jetbrains.com/s3/bs-django-developers-survey-2026&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Django Forum&lt;br /&gt;
&lt;a href=&quot;https://surveys.jetbrains.com/s3/df-django-developers-survey-2026&quot;&gt;https://surveys.jetbrains.com/s3/df-django-developers-survey-2026&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LinkedIn&lt;br /&gt;
&lt;a href=&quot;https://surveys.jetbrains.com/s3/li-django-developers-survey-2026&quot;&gt;https://surveys.jetbrains.com/s3/li-django-developers-survey-2026&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mastodon&lt;br /&gt;
&lt;a href=&quot;https://surveys.jetbrains.com/s3/md-django-developers-survey-2026&quot;&gt;https://surveys.jetbrains.com/s3/md-django-developers-survey-2026&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reddit&lt;br /&gt;
&lt;a href=&quot;https://surveys.jetbrains.com/s3/r-django-developers-survey-2026&quot;&gt;https://surveys.jetbrains.com/s3/r-django-developers-survey-2026&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;X / Twitter&lt;br /&gt;
&lt;a href=&quot;https://surveys.jetbrains.com/s3/x-django-developers-survey-2026&quot;&gt;https://surveys.jetbrains.com/s3/x-django-developers-survey-2026&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
	<pubDate>Tue, 12 May 2026 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Real Python: Quiz: Building Type-Safe LLM Agents With Pydantic AI</title>
	<guid>https://realpython.com/quizzes/pydantic-ai-video/</guid>
	<link>https://realpython.com/quizzes/pydantic-ai-video/</link>
	<description>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of &lt;a href=&quot;https://realpython.com/courses/building-type-safe-llm-agents-with-pydantic-ai/&quot;&gt;Building Type-Safe LLM Agents With Pydantic AI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By working through this quiz, you&amp;rsquo;ll revisit how Pydantic AI returns structured outputs from LLMs, how validation retries improve reliability, how tools and function calling work, how dependency injection flows through &lt;code&gt;RunContext&lt;/code&gt;, and what trade-offs to expect when running agents in production.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</description>
	<pubDate>Tue, 12 May 2026 12:00:00 +0000</pubDate>
</item>

</channel>
</rss>
