Planet Python
Last update: October 02, 2025 07:43 PM UTC
October 01, 2025
Real Python
Python 3.14 Preview: Better Syntax Error Messages
Python 3.14 brings a fresh batch of improvements to error messages that’ll make debugging feel less like detective work and more like having a helpful colleague point out exactly what went wrong. These refinements build on the clearer tracebacks introduced in recent releases and focus on the mistakes Python programmers make most often.
By the end of this tutorial, you’ll understand that:
- Python 3.14’s improved error messages help you debug code more efficiently.
- There are ten error message enhancements in 3.14 that cover common mistakes, from keyword typos to misusing
async with
. - These improvements can help you catch common coding mistakes faster.
- Python’s error messages have evolved from version 3.10 through 3.14.
- Better error messages accelerate your learning and development process.
There are many other improvements and new features coming in Python 3.14. The highlights include the following:
To try any of the examples in this tutorial, you need to use Python 3.14. The tutorials How to Install Python on Your System: A Guide and Managing Multiple Python Versions With pyenv walk you through several options for adding a new version of Python to your system.
Get Your Code: Click here to download the free sample code that you’ll use to learn about the error message improvements in Python 3.14.
Take the Quiz: Test your knowledge with our interactive “Python 3.14 Preview: Better Syntax Error Messages” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
Python 3.14 Preview: Better Syntax Error MessagesExplore how Python 3.14 improves error messages with clearer explanations, actionable hints, and better debugging support for developers.
Better Error Messages in Python 3.14
When Python 3.9 introduced a new parsing expression grammar (PEG) parser for the language, it opened the door to better error messages in Python 3.10. Python 3.11 followed with even better error messages, and that same effort continued in Python 3.12.
Python 3.13 refined these messages further with improved formatting and clearer explanations, making multiline errors more readable and adding context to complex error situations. These improvements build upon PEP 657, which introduced fine-grained error locations in tracebacks in Python 3.11.
Now, Python 3.14 takes another step forward, alongside other significant changes like PEP 779, which makes the free-threaded build officially supported, and PEP 765, which disallows using return
, break
, or continue
to exit a finally
block. What makes the error message enhancements in Python 3.14 special is their focus on common mistakes.
Each improved error message follows a consistent pattern:
- It identifies the mistake.
- It explains what’s wrong in plain English.
- It suggests a likely fix when possible.
The error message improvements in Python 3.14 cover SyntaxError
, ValueError
, and TypeError
messages. For an overview of the ten improvements you’ll explore in this tutorial, expand the collapsible section below:
-
Keyword Typos —
SyntaxError
⭕️ Previous:invalid syntax
✅ Improved:invalid syntax. Did you mean 'for'?
-
elif
Afterelse
—SyntaxError
⭕️ Previous:invalid syntax
✅ Improved:'elif' block follows an 'else' block
-
Conditional Expressions —
SyntaxError
⭕️ Previous:invalid syntax
✅ Improved:expected expression after 'else', but statement is given
-
String Closure —
SyntaxError
⭕️ Previous:invalid syntax
✅ Improved:invalid syntax. Is this intended to be part of the string?
-
String Prefixes —
SyntaxError
⭕️ Previous:invalid syntax
✅ Improved:'b' and 'f' prefixes are incompatible
-
Unpacking Errors —
ValueError
⭕️ Previous:too many values to unpack (expected 2)
✅ Improved:too many values to unpack (expected 2, got 3)
-
as
Targets —SyntaxError
⭕️ Previous:invalid syntax
✅ Improved:cannot use list as import target
-
Unhashable Types —
TypeError
⭕️ Previous:unhashable type: 'list'
✅ Improved:cannot use 'list' as a dict key (unhashable type: 'list')
-
math
Domain Errors —ValueError
⭕️ Previous:math domain error
✅ Improved:expected a nonnegative input, got -1.0
-
async with
Errors —TypeError
⭕️ Previous:'TaskGroup' object does not support the context manager protocol
✅ Improved:object does not support the context manager protocol...Did you mean to use 'async with'?
The examples in this tutorial show both the error messages from Python 3.13 and the improved messages in Python 3.14, so you can see the differences even if you haven’t installed 3.14 yet.
For a general overview of Python’s exception system, see Python’s Built-in Exceptions: A Walkthrough With Examples, or to learn about raising exceptions, check out Python’s raise: Effectively Raising Exceptions in Your Code.
Clearer Keyword Typo Suggestions
A typo is usually a tiny mistake, sometimes just one extra letter, but it’s enough to break your code completely. Typos that involve Python keywords are among the most common syntax errors in Python code.
In Python 3.13 and earlier, a typo in a keyword produces a generic syntax error that offers no guidance about what might be wrong:
Python 3.13
>>> forr i in range(5):
File "<python-input-0>", line 1
forr i in range(5):
^
SyntaxError: invalid syntax
The error points to the problem area with a helpful caret symbol (^
), which at least tells you where Python found the error. However, it doesn’t suggest what you might have meant. The message “invalid syntax” is technically correct but not particularly helpful in practice.
You have to figure out on your own that forr
should actually be for
. It might be obvious once you spot it, but finding that single wrong letter can take a surprisingly long time when you’re focused on logic rather than spelling.
Python 3.14 recognizes when you type something close to a Python keyword and offers a helpful suggestion that immediately points you to the fix:
Read the full article at https://realpython.com/python314-error-messages/ »
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
Django Weblog
Django security releases issued: 5.2.7, 5.1.13, and 4.2.25
In accordance with our security release policy, the Django team is issuing releases for Django 5.2.7, Django 5.1.13, and Django 4.2.25. These releases address the security issues detailed below. We encourage all users of Django to upgrade as soon as possible.
CVE-2025-59681: Potential SQL injection in QuerySet.annotate(), alias(), aggregate(), and extra() on MySQL and MariaDB
QuerySet.annotate(), QuerySet.alias(), QuerySet.aggregate(), and QuerySet.extra() methods were subject to SQL injection in column aliases, using a suitably crafted dictionary, with dictionary expansion, as the **kwargs passed to these methods on MySQL and MariaDB.
Thanks to sw0rd1ight for the report.
This issue has severity "high" according to the Django security policy.
CVE-2025-59682: Potential partial directory-traversal via archive.extract()
The django.utils.archive.extract() function, used by startapp --template and startproject --template, allowed partial directory-traversal via an archive with file paths sharing a common prefix with the target directory.
Thanks to stackered for the report.
This issue has severity "low" according to the Django security policy.
Affected supported versions
- Django main
- Django 6.0 (currently at alpha status)
- Django 5.2
- Django 5.1
- Django 4.2
Resolution
Patches to resolve the issue have been applied to Django's main, 6.0 (currently at alpha status), 5.2, 5.1, and 4.2 branches. The patches may be obtained from the following changesets.
CVE-2025-59681: Potential SQL injection in QuerySet.annotate(), alias(), aggregate(), and extra() on MySQL and MariaDB
- On the main branch
- On the 6.0 branch
- On the 5.2 branch
- On the 5.1 branch
- On the 4.2 branch
CVE-2025-59682: Potential partial directory-traversal via archive.extract()
- On the main branch
- On the 6.0 branch
- On the 5.2 branch
- On the 5.1 branch
- On the 4.2 branch
The following releases have been issued
- Django 5.2.7 (download Django 5.2.7 | 5.2.7 checksums)
- Django 5.1.13 (download Django 5.1.13 | 5.1.13 checksums)
- Django 4.2.25 (download Django 4.2.25 | 4.2.25 checksums)
The PGP key ID used for this release is Jacob Walls: 131403F4D16D8DC7
General notes regarding security reporting
As always, we ask that potential security issues be reported via private email to security@djangoproject.com, and not via Django's Trac instance, nor via the Django Forum. Please see our security policies for further information.
Real Python
Quiz: Python 3.14 Preview: Better Syntax Error Messages
This quiz helps you get familiar with the upgraded error messages in Python 3.14. You’ll review new keyword typo suggestions, improved math errors, string prefix feedback, and more.
Put your understanding to the test and discover how Python’s improved error messages can help you debug code faster.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
Quiz: Python MCP Server: Connect LLMs to Your Data
This quiz helps you review the core ideas behind the Model Context Protocol (MCP). You will practice how MCP connects large language models with external systems, how to install it, and what role prompts, resources, and tools play.
You’ll also revisit best practices for defining tools, explore client-server setups like Cursor, and check your understanding of transports and testing. For a full walkthrough, see Python MCP Server: Connect LLMs to Your Data.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
Zero to Mastery
[September 2025] Python Monthly Newsletter 🐍
70th issue of Andrei's Python Monthly: Customizing Your Python REPL, AI Coding Trap, and much more. Read the full newsletter to get up-to-date with everything you need to know from last month.
Tryton News
Newsletter October 2025
During the last month we focused on fixing bugs, improving the behaviour of things, speeding-up performance issues - building on the changes from our last release. We also added some new features which we would like to introduce to you in this newsletter.
For an in depth overview of the Tryton issues please take a look at our issue tracker or see the issues and merge requests filtered by label.
Changes for the User
Sales, Purchases and Projects
We now use the guest-party for a Shopify order without a known customer which can be updated with the proper party in the admin-panel at a later time.
Now we support the orders/edited and orders/cancelled web-hooks from Shopify.
New Releases
We released bug fixes for the currently maintained long term support series
7.0 and 6.0, and for the penultimate series 7.4.
Security
Please update your systems to take care of a security related bug we found last month.
Luis Falcon has found that trytond may log sensitive data like passwords when the logging level is set to INFO. Impact CVSS v3.0 Base Score: 4.2 Attack Vector: Network Attack Complexity: Low Privileges Required: High User Interaction: None Scope: Unchanged Confidentiality: High Integrity: None Availability: None Workaround Increasing the logging level above INFO prevents logging of the sensitive data. Resolution All affected users should upgrade trytond to the latest version. Affected vers…
1 post - 1 participant
Seth Michael Larson
Winning a bet about “six”, the Python 2 compatibility shim
Exactly five years ago today Andrey Petrov and I made a bet about whether
“six
”, the compatibility shim for Python 2 and 3 APIs, would
still be in the top 20 daily downloads on PyPI. I said it would,
Andrey took the side against.
Well, today I can say that I've won the bet. When the bet
was placed, six
was #2 in terms of daily downloads
and today six
is #14.
Funnily enough, six
was still exactly #14 back in 2023:
“six is top 14 after 3 years, 2 years left, sounds like [Andrey] is winning”
-- Quentin Pradet (2023-07-09)
Completely unrelated to this bet, Hynek mentioned six
still being in the top 20 downloaded packages during his PyCon UK keynote.
six
itself isn't a library that many use on its own,
as at least 96% of six
downloads come from Python 3 versions. Instead,
this library is installed because other libraries depend on the library.
Here are the top packages that still depend on six
:
Package | Downloads / Day | Last Uploaded |
---|---|---|
python-dateutil | 22M | 2024-03-01 |
yandexcloud | 6M | 2025-09-22 |
azure-core | 4M | 2025-09-11 |
jedi | 2M | 2024-11-11 |
kubernetes | 2M | 2025-06-09 |
rfc3339-validator | 2M | 2021-05-12 |
google-pasta | 1M | 2020-03-13 |
confluent-kafka | 1M | 2025-08-18 |
oauth2client | 1M | 2018-09-07 |
ecdsa | 1M | 2025-03-13 |
These packages were found by querying my own dataset about PyPI:
SELECT packages.name, packages.downloads
FROM packages JOIN deps ON packages.name = deps.package_name
WHERE deps.dep_name = 'six'
GROUP BY packages.name
ORDER BY packages.downloads DESC
LIMIT 10;
Notice how a single popular library, python-dateutil
, keeping six
as a dependency was enough to carry me to victory.
Without python-dateutil
I likely would have lost this bet.
I also wanted to note the "last uploaded" dates, as some of the libraries
aren't uploaded frequently, potentially explaining why they still depend on six
.
“surely in 10 years, six won't be a thing. right? RIGHT?”
-- Andrey Petrov (2020-10-01)
We'll see! ;) Thanks to Benjamin Peterson for creating and maintaining six
.
Thanks for keeping RSS alive! ♥
September 30, 2025
PyCoder’s Weekly
Issue #702: django.tasks, MCP, Asyncio Without the GIL, and More (Sept. 30, 2025)
#702 – SEPTEMBER 30, 2025
View in Browser »
django.tasks
Exists
django.tasks
has been officially released. This feature, likely to be included in Django 6, abstracts background task management similar to how database backends work.
JAKE HOWARD
Python MCP: Connect Your LLM With the World
Learn how to build a Model Context Protocol (MCP) server in Python. Connect tools, prompts, and data to AI agents like Cursor for smarter assistants.
REAL PYTHON
Secure Your AI Agents so That you can Protect Your Users
Traditional security measures aren’t sufficient for AI systems. Secure your AI agents using Auth0 for AI Agents with features like User Authentication, Token Vault, Async Authorization, and Fine Grain Authorization for RAG. Leave login security to Auth0 so you can focus on building something great. →
AUTH0 sponsor
Scaling Asyncio on Free-Threaded Python
A recap on the work done in Python 3.14 to enable asyncio to scale on the free-threaded build of CPython.
KUMAR ADITYA
Python for Beginners: 8-Week Live Course
Starting October 13th, this structured cohort-based course takes you from zero Python experience to writing real programs with confidence. Led by an expert Real Python instructor, you’ll build a solid programming foundation through structured daily lessons and weekly live classes: View the full syllabus and register for Oct 13–Dec 5 →
REAL PYTHON
Python Jobs
Senior Python Developer (Houston, TX, USA)
Articles & Tutorials
django-watchfiles
: More Efficient Runserver Autoreloading
Django’s runserver automatically reloads when you change Python files. Without this autoreloading feature, you’d need to manually restart the server every time you made a code change, however it isn’t very efficient. Learn about this new library that does a better job.
ADAM JOHNSON
Phishing Attacks With New Domains Likely to Continue
Another phishing attack looking for PyPI credentials is on-going. Seth Larson, Security Developer for the PSF writes about what is happening and what they’re trying to do about it.
THE PYTHON PACKAGE INDEX BLOG
CodeRabbit: Free AI Code Reviews in CLI
CodeRabbit CLI gives instant code reviews in your terminal. It plugs into any AI coding CLI and catches bugs, security issues, and AI hallucinations before they reach your codebase.
CODERABBIT sponsor
Accelerating Python Data Science at NVIDIA
Talk Pyhton interviews Ben Zaitlen from NVIDIA to discuss RAPIDS, the open source toolkit that lets pandas, scikit-learn, Spark, Polars, and even NetworkX execute on GPUs.
KENNEDY & ZAITLEN podcast
Get Started With FastAPI
FastAPI is the first choice when creating APIs in Python. Explore FastAPI code examples and get the most frequent questions about FastAPI answered.
REAL PYTHON
Why Is Python So Popular in 2025?
From powering AI and data science to driving web development and automation, Python continues to dominate in 2025. Discover why in this blog post.
EVGENIA VERBINA
Tracing JITs in the Real World
Antonio presented at the CPython Core Dev sprint, talking about the progress of the JIT compiler. This article summarizes his talk.
ANTONIO CUNI
Strip Characters From a Python String
Use Python’s .strip()
to remove whitespace or chosen chars. Learn pitfalls, real-world cases, and compare with .lstrip()
and .removesuffix()
.
REAL PYTHON course
Equality and Hashing in Python Dataclasses
Understanding Python dataclasses and how fields determine equality and hashing.
VIVIS DEV • Shared by Vivis Dev
Switching From pip
to uv
in Python
Learn the ins and outs of uv and how to transition from Pip to uv.
DAMILOLA OLATUNJI • Shared by AppSignal
Projects & Code
fenic: Build AI and Agentic Applications With DataFrames
GITHUB.COM/TYPEDEF-AI • Shared by Kostas Pardalis
Events
Weekly Real Python Office Hours Q&A (Virtual)
October 1, 2025
REALPYTHON.COM
Yaounde
October 1 to October 2, 2025
NOKIDBEHIND.ORG
PyCon Estonia 2025
October 2 to October 4, 2025
PYCON.EE
PyCon Nigeria 2025
October 2 to October 5, 2025
PYCON.ORG
Canberra Python Meetup
October 2, 2025
MEETUP.COM
Sydney Python User Group (SyPy)
October 2, 2025
SYPY.ORG
Django Girls Aba
October 4 to October 5, 2025
DJANGOGIRLS.ORG
PyCon Africa 2025
October 8 to October 13, 2025
PYCON.ORG
Wagtail Space 2025
October 8 to October 11, 2025
WAGTAIL.ORG
PyCon Hong Kong 2025
October 11 to October 13, 2025
PYCON.HK
Python for Beginners: Code with Confidence (Live Course)
October 13, 2025 to December 5, 2026
REALPYTHON.COM
Happy Pythoning!
This was PyCoder’s Weekly Issue #702.
View in Browser »
[ Subscribe to 🐍 PyCoder’s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]
September 30, 2025 07:30 PM UTC
Real Python
Modern Python Linting With Ruff
Linting is essential to writing clean and readable code that you can share with others. A linter, like Ruff, is a tool that analyzes your code and looks for errors, stylistic issues, and suspicious constructs. Linting allows you to address issues and improve your code quality before you commit your code and share it with others.
Ruff is a modern linter that’s extremely fast and has a simple interface, making it straightforward to use. It also aims to be a drop-in replacement for many other linting and formatting tools, such as Flake8, isort, and Black. It’s quickly becoming one of the most popular Python linters.
In this video course, you’ll learn how to:
- Install Ruff
- Check your Python code for errors
- Automatically fix your linting errors
- Use Ruff to format your code
- Add optional configurations to supercharge your linting
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
September 30, 2025 02:00 PM UTC
Quiz: Modern Python Linting With Ruff
Sharpen your code quality workflow with Ruff. In this quiz, you’ll review installation checks, continuous linting, and code formatting.
You’ll also revisit selecting rule codes, reading rule docs, applying safe fixes, and configuring TOML files. For a refresher, see Modern Python Linting With Ruff.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
September 30, 2025 12:00 PM UTC
September 29, 2025
Python Morsels
Why splitlines() instead of split("\n")?
To split text into lines in Python, use the splitlines()
method, not the split()
method.
Splitting on \n
Let's say we have some text that came from a file:
>>> poem = "old pond\nfrog leaping\nsplash"
If we wanted to split this text up into lines, we could use the string splitlines
method:
>>> poem.splitlines()
['old pond', 'frog leaping', 'splash']
That works... but why does that splitlines
method exist?
Couldn't we just pass \n
to the string split
method, instead?
>>> poem.split("\n")
['old pond', 'frog leaping', 'splash']
While splitting on a line feed character will often work, I recommend using the string splitlines
method instead.
Because the splitlines
method handles common scenarios that the split
method doesn't.
Splitting on different types of newlines
Let's say we have some …
Read the full article: https://www.pythonmorsels.com/splitlines/
September 29, 2025 11:45 PM UTC
Real Python
Astral's ty: A New Blazing-Fast Type Checker for Python
After Ruff and uv, the Astral team is back with another Rust-based tool called ty, short for type check. This new tool delivers a lightning-fast static type-checking experience in Python, aiming to outpace existing tools in both performance and convenience. By incorporating ty into your daily workflow, you’ll catch type-related bugs earlier and get clearer feedback.
But is ty suitable for you? Take a look at the table below to make a quick decision:
Use Case | Pick ty |
Pick Other Tools |
---|---|---|
Development or experimentation | ✅ | ❌ |
Production use | ❌ | ✅ |
At the time of writing, ty is available as an early preview release with hundreds of open issues. Despite being actively developed and boasting over ten thousand stars on GitHub, it’s still missing essential features and might occasionally fail.
As such, it’s not ready for full adoption in production yet, nor is it going to be a drop-in replacement for any of its competitors. Bugs can take you by surprise in unexpected ways! Additionally, because ty’s implementation is moving fast, some of the information you’ll find in this tutorial may become outdated over time.
If you’d like to get familiar with a new, robust, and promising type checker in your personal projects, then by all means give ty a try! Ready to dive in? Click the link below to grab the sample code you’ll be working with in this tutorial:
Get Your Code: Click here to download the free sample code that you’ll use to learn about Astral’s ty.
Take the Quiz: Test your knowledge with our interactive “Astral's ty Type Checker for Python” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
Astral's ty Type Checker for PythonTest your knowledge of Astral's ty—a blazing-fast, Rust-powered Python type checker. You'll cover installation, usage, rule configuration, and the tool's current limitations.
Start Using ty in Python Now
Python is a dynamically typed language, so it requires third-party tools to perform type checking and other kinds of static code analysis. Recently, ty joined the club of external type-checking tools for Python. Despite being created by a private company, Astral, the tool itself remains open source and MIT-licensed.
Although ty is mostly written in Rust, you don’t need the Rust compiler or its execution environment to type check your Python projects with it. To quickly get started with ty, you can install it directly from PyPI with pip
, preferably within an activated virtual environment:
(venv) $ python -m pip install ty
As long as you’re on one of the three major operating systems—Windows, macOS, or Linux—this command will bring a hefty binary script called ty
into your virtual environment. The script is already compiled to machine code suitable for your platform with all the necessary dependencies baked in, so you can run it directly, just like any other command-line program.
To verify the installation, run the following command in your terminal emulator app:
(venv) $ ty --version
ty 0.0.1-alpha.21
You should see a version number similar to the one above appear in the output. Alternatively, if that doesn’t work, then try to execute ty
as a Python module using the interpreter’s -m
option:
(venv) $ python -m ty --version
ty 0.0.1-alpha.21
When you do, Python runs a tiny wrapper script that looks for the ty
binary executable in your path and invokes it for you.
Note that the pip
install command only installs ty into the given virtual environment, which is usually associated with a specific project. To make ty available globally from any folder on your computer, you’ll need to use a different approach. Check out the official documentation for more installation options.
Note: If Visual Studio Code is your code editor of choice, then you’ll benefit from installing the official ty extension. This extension integrates ty directly into VS Code, letting you work without ever touching the command line. Unfortunately, users of PyCharm and other popular IDEs must wait until plugins for their tools catch up.
If your editor supports the Language Server Protocol, then you could leverage the ty server
subcommand. For PyCharm, this requires installing the LSP4IJ plugin first, though this tutorial won’t cover its configuration.
Finally, if none of this works for you, then you may want to run the ty check
subcommand with the optional --watch
switch. This will have ty automatically rerun the checks for instant feedback whenever changes are detected upon saving one of the files in your project.
From now on, you won’t see the virtual environment’s name (venv
) in the command prompts for the rest of this tutorial. To keep the code blocks concise, it’ll be assumed that ty is already installed globally and available in your system’s path.
Alright. It’s time for the fun part: letting ty scrutinize some Python code to catch bugs before they sneak in.
Catch Typing Errors With ty
The command-line interface of ty is pretty minimal and straightforward, as you’ll notice when you run ty
without providing any subcommands or options:
$ ty
An extremely fast Python type checker.
Usage: ty <COMMAND>
Commands:
check Check a project for type errors
server Start the language server
version Display ty's version
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
Read the full article at https://realpython.com/python-ty/ »
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
September 29, 2025 02:00 PM UTC
Quiz: Astral's ty Type Checker for Python
In this quiz, you’ll revisit the key concepts from Astral’s ty: A New Blazing-Fast Type Checker for Python. You’ll check your understanding of installing ty from PyPI, running type checks, and interpreting its structured diagnostics. You’ll also recall how to configure and silence specific rules, limit the scope of checks, and adjust Python version or platform settings.
By completing this quiz, you’ll cement your ability to experiment confidently with ty in personal or exploratory projects.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
September 29, 2025 12:00 PM UTC
Talk Python to Me
#521: Red Teaming LLMs and GenAI with PyRIT
English is now an API. Our apps read untrusted text; they follow instructions hidden in plain sight, and sometimes they turn that text into action. If you connect a model to tools or let it read documents from the wild, you have created a brand new attack surface. In this episode, we will make that concrete. We will talk about the attacks teams are seeing in 2025, the defenses that actually work, and how to test those defenses the same way we test code. Our guides are Tori Westerhoff and Roman Lutz from Microsoft. They help lead AI red teaming and build PyRIT, a Python framework the Microsoft AI Red Team uses to pressure test real products. By the end of this hour you will know where the biggest risks live, what you can ship this quarter to reduce them, and how PyRIT can turn security from a one time audit into an everyday engineering practice.<br/> <br/> <strong>Episode sponsors</strong><br/> <br/> <a href='https://talkpython.fm/sentryagents'>Sentry AI Monitoring, Code TALKPYTHON</a><br> <a href='https://talkpython.fm/agntcy'>Agntcy</a><br> <a href='https://talkpython.fm/training'>Talk Python Courses</a><br/> <br/> <h2 class="links-heading mb-4">Links from the show</h2> <div><strong>Tori Westerhoff</strong>: <a href="https://www.linkedin.com/in/victoriawesterhoff/?featured_on=talkpython" target="_blank" >linkedin.com</a><br/> <strong>Roman Lutz</strong>: <a href="https://www.linkedin.com/in/romanlutz/?featured_on=talkpython" target="_blank" >linkedin.com</a><br/> <br/> <strong>PyRIT</strong>: <a href="https://aka.ms/pyrit?featured_on=talkpython" target="_blank" >aka.ms/pyrit</a><br/> <strong>Microsoft AI Red Team page</strong>: <a href="https://learn.microsoft.com/en-us/security/ai-red-team/?featured_on=talkpython" target="_blank" >learn.microsoft.com</a><br/> <strong>2025 Top 10 Risk & Mitigations for LLMs and Gen AI Apps</strong>: <a href="https://genai.owasp.org/llm-top-10/?featured_on=talkpython" target="_blank" >genai.owasp.org</a><br/> <strong>AI Red Teaming Agent</strong>: <a href="https://learn.microsoft.com/en-us/azure/ai-foundry/concepts/ai-red-teaming-agent?featured_on=talkpython" target="_blank" >learn.microsoft.com</a><br/> <strong>3 takeaways from red teaming 100 generative AI products</strong>: <a href="https://www.microsoft.com/en-us/security/blog/2025/01/13/3-takeaways-from-red-teaming-100-generative-ai-products/?featured_on=talkpython" target="_blank" >microsoft.com</a><br/> <strong>MIT report: 95% of generative AI pilots at companies are failing</strong>: <a href="https://fortune.com/2025/08/18/mit-report-95-percent-generative-ai-pilots-at-companies-failing-cfo/?featured_on=talkpython" target="_blank" >fortune.com</a><br/> <br/> <strong>A couple of "Little Bobby AI" cartoons</strong><br/> <strong>Give me candy</strong>: <a href="https://blobs.talkpython.fm/little-bobby-ai-1.png" target="_blank" >talkpython.fm</a><br/> <strong>Tell me a joke</strong>: <a href="https://blobs.talkpython.fm/little-bobby-ai-2.png" target="_blank" >talkpython.fm</a><br/> <br/> <strong>Watch this episode on YouTube</strong>: <a href="https://www.youtube.com/watch?v=N681L4BXTUw" target="_blank" >youtube.com</a><br/> <strong>Episode #521 deep-dive</strong>: <a href="https://talkpython.fm/episodes/show/521/red-teaming-llms-and-genai-with-pyrit#takeaways-anchor" target="_blank" >talkpython.fm/521</a><br/> <strong>Episode transcripts</strong>: <a href="https://talkpython.fm/episodes/transcript/521/red-teaming-llms-and-genai-with-pyrit" target="_blank" >talkpython.fm</a><br/> <br/> <strong>Theme Song: Developer Rap</strong><br/> <strong>Served in a Flask</strong>: <a href="https://talkpython.fm/flasksong" target="_blank" >talkpython.fm/flasksong</a><br/> <br/> <strong>---== Don't be a stranger ==---</strong><br/> <strong>YouTube</strong>: <a href="https://talkpython.fm/youtube" target="_blank" ><i class="fa-brands fa-youtube"></i>youtube.com</a><br/> <br/> <strong>Bluesky</strong>: <a href="https://bsky.app/profile/talkpython.fm" target="_blank" >@talkpython.fm</a><br/> <strong>Mastodon</strong>: <a href="https://fosstodon.org/web/@talkpython" target="_blank" ><i class="fa-brands fa-mastodon"></i>talkpython</a><br/> <strong>X.com</strong>: <a href="https://x.com/talkpython" target="_blank" ><i class="fa-brands fa-twitter"></i>talkpython</a><br/> <br/> <strong>Michael on Bluesky</strong>: <a href="https://bsky.app/profile/mkennedy.codes?featured_on=talkpython" target="_blank" >@mkennedy.codes</a><br/> <strong>Michael on Mastodon</strong>: <a href="https://fosstodon.org/web/@mkennedy" target="_blank" ><i class="fa-brands fa-mastodon"></i>mkennedy</a><br/> <strong>Michael on X.com</strong>: <a href="https://x.com/mkennedy?featured_on=talkpython" target="_blank" ><i class="fa-brands fa-twitter"></i>mkennedy</a><br/></div>
September 29, 2025 08:00 AM UTC
Python Bytes
#451 Databases are a Fad
<strong>Topics covered in this episode:</strong><br> <ul> <li><em>* <a href="https://www.postgresql.org/about/news/postgresql-18-released-3142/?featured_on=pythonbytes">PostgreSQL 18 Released</a></em>*</li> <li><em>* <a href="https://nedbatchelder.com/blog/202509/testing_is_better_than_dsa.html?featured_on=pythonbytes">Testing is better than DSA (Data Structures and Algorithms)</a></em>*</li> <li><em>* <a href="https://pyrefly.org/en/docs/IDE-features/?featured_on=pythonbytes">Pyrefly in Cursor/PyCharm/VSCode/etc</a></em>*</li> <li><em>* <a href="https://www.better-simple.com/django/2025/09/17/playwright-pytest-that-brings-me-joy/?featured_on=pythonbytes">Playwright & pytest techniques that bring me joy</a></em>*</li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=EFL48VM_8e4' style='font-weight: bold;'data-umami-event="Livestream-Past" data-umami-event-episode="451">Watch on YouTube</a><br> <p><strong>About the show</strong></p> <p>Sponsored by us! Support our work through:</p> <ul> <li>Our <a href="https://training.talkpython.fm/?featured_on=pythonbytes"><strong>courses at Talk Python Training</strong></a></li> <li><a href="https://courses.pythontest.com/p/the-complete-pytest-course?featured_on=pythonbytes"><strong>The Complete pytest Course</strong></a></li> <li><a href="https://www.patreon.com/pythonbytes"><strong>Patreon Supporters</strong></a></li> </ul> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy">@mkennedy@fosstodon.org</a> / <a href="https://bsky.app/profile/mkennedy.codes?featured_on=pythonbytes">@mkennedy.codes</a> (bsky)</li> <li>Brian: <a href="https://fosstodon.org/@brianokken">@brianokken@fosstodon.org</a> / <a href="https://bsky.app/profile/brianokken.bsky.social?featured_on=pythonbytes">@brianokken.bsky.social</a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes">@pythonbytes@fosstodon.org</a> / <a href="https://bsky.app/profile/pythonbytes.fm">@pythonbytes.fm</a> (bsky)</li> </ul> <p>Join us on YouTube at <a href="https://pythonbytes.fm/stream/live"><strong>pythonbytes.fm/live</strong></a> to be part of the audience. Usually <strong>Monday</strong> at 10am PT. Older video versions available there too.</p> <p>Finally, if you want an artisanal, hand-crafted digest of every week of the show notes in email form? Add your name and email to <a href="https://pythonbytes.fm/friends-of-the-show">our friends of the show list</a>, we'll never share it.</p> <p><strong>Michael #1: <a href="https://www.postgresql.org/about/news/postgresql-18-released-3142/?featured_on=pythonbytes">PostgreSQL 18 Released</a></strong></p> <ul> <li>PostgreSQL 18 is out (Sep 25, 2025) with a focus on faster text handling, async I/O, and easier upgrades.</li> <li>New async I/O subsystem speeds sequential scans, bitmap heap scans, and vacuum by issuing concurrent reads instead of blocking on each request.</li> <li>Major-version upgrades are smoother: <code>pg_upgrade</code> retains planner stats, adds parallel checks via <code>-jobs</code>, and supports faster cutovers with <code>-swap</code>.</li> <li>Smarter query performance lands with skip scans on multicolumn B-tree indexes, better <code>OR</code> optimization, incremental-sort merge joins, and parallel GIN index builds.</li> <li>Dev quality-of-life: virtual generated columns enabled by default, a <code>uuidv7()</code> generator for time-ordered IDs, and <code>RETURNING</code> can expose both <code>OLD</code> and <code>NEW</code>.</li> <li>Security gets an upgrade with native OAuth 2.0 authentication; MD5 password auth is deprecated and TLS controls expand.</li> <li>Text operations get a boost via the new <code>PG_UNICODE_FAST</code> collation, faster <code>upper</code>/<code>lower</code>, a <code>casefold()</code> helper, and clearer collation behavior for LIKE/FTS.</li> </ul> <p><strong>Brian #2: <a href="https://nedbatchelder.com/blog/202509/testing_is_better_than_dsa.html?featured_on=pythonbytes">Testing is better than DSA (Data Structures and Algorithms)</a></strong></p> <ul> <li>Ned Batchelder</li> <li>If you need to grind through DSA problems to get your first job, then of course, do that, but if you want to prepare yourself for a career, and also stand out in job interviews, learn how to write tests.</li> <li>Testing is a skill you’ll use constantly, will make you stand out in job interviews, and isn’t taught well in school (usually).</li> <li>Testing code well is not obvious. It’s a puzzle and a problem to solve.</li> <li>It gives you confidence and helps you write better code.</li> <li>Applies everywhere, at all levels.</li> <li>Notes from Brian <ul> <li>Most devs suck at testing, so being good at it helps you stand out very quickly.</li> <li>Thinking about a system and how to test it often very quickly shines a spotlight on problem areas, parts with not enough specification, and fuzzy requirements. This is a good thing, and bringing up these topics helps you to become a super valuable team member.</li> <li>High level tests need to be understood by key engineers on a project. Even if tons of the code is AI generated. Even if many of the tests are, the people understanding the requirements and the high level tests are quite valuable.</li> </ul></li> </ul> <p><strong>Michael #3: <a href="https://pyrefly.org/en/docs/IDE-features/?featured_on=pythonbytes">Pyrefly in Cursor/PyCharm/VSCode/etc</a></strong></p> <ul> <li>Install the VSCode/Cursor extension or PyCharm plugin, see https://pyrefly.org/en/docs/IDE/</li> <li>Brian spoke about Pyrefly in <a href="https://pythonbytes.fm/episodes/show/433/dev-in-the-arena"><strong>#433: Dev in the Arena</strong></a></li> <li>I’ve subsequently had the team on Talk Python: #523: Pyrefly: Fast, IDE-friendly typing for Python (podcast version coming in a few weeks, <a href="https://www.youtube.com/watch?v=P4RKxl_giH4">see video</a> for now.)</li> <li>My experience has been Pyrefly changes the feel of the editor, give it a try. But disable the regular language server extension.</li> </ul> <p><strong>Brian #4: <a href="https://www.better-simple.com/django/2025/09/17/playwright-pytest-that-brings-me-joy/?featured_on=pythonbytes">Playwright & pytest techniques that bring me joy</a></strong></p> <ul> <li>Tim Shilling</li> <li>“I’ve been working with playwright more often to do end to end tests. As a project grows to do more with HTMX and Alpine in the markup, there’s less unit and integration test coverage and a greater need for end to end tests.”</li> <li>Tim covers some cool E2E techniques <ul> <li>Open new pages / tabs to be tested</li> <li>Using a pytest marker to identify playwright tests</li> <li>Using a pytest marker in place of fixtures</li> <li>Using page.pause() and Playwright’s debugging tool</li> <li>Using assert_axe_violations to prevent accessibility regressions</li> <li>Using page.expect_response() to confirm a background request occurred</li> </ul></li> <li>From Brian <ul> <li>Again, with more and more lower level code being generated, and many unit tests being generated (shakes head in sadness), there’s an increased need for high level tests.</li> <li>Don’t forget API tests, obviously, but if there’s a web interface, it’s gotta be tested.</li> <li>Especially if the primary user experience is the web interface, building your Playwright testing chops helps you stand out and let’s you test a whole lot of your system with not very many tests.</li> </ul></li> </ul> <p><strong>Extras</strong></p> <p>Brian:</p> <ul> <li><a href="https://samwho.dev/big-o/?featured_on=pythonbytes">Big O - By Sam Who</a></li> <li><a href="https://pythoninsider.blogspot.com/2025/09/python-3140rc3-is-go.html?m=1&featured_on=pythonbytes">Python 3.14.0rc3 has been available since Sept 18.</a> <ul> <li><a href="https://peps.python.org/pep-0745/?featured_on=pythonbytes">Python 3.14.0 final scheduled for Oct 7</a></li> </ul></li> <li><a href="https://www.djangoproject.com/weblog/2025/sep/17/django-60-alpha-released/?featured_on=pythonbytes">Django 6.0 alpha 1 released</a> <ul> <li><a href="https://code.djangoproject.com/wiki/Version6.0Roadmap?featured_on=pythonbytes">Django 6.0 final scheduled for Dec 3</a></li> </ul></li> <li><a href="https://pythontest.com?featured_on=pythonbytes">Python Test</a> Static hosting update</li> </ul> <p><strong>Joke: <a href="https://x.com/PR0GRAMMERHUM0R/status/1967317478238757032?featured_on=pythonbytes">Always be backing up</a></strong></p>
September 29, 2025 08:00 AM UTC
Armin Ronacher
90%
“I think we will be there in three to six months, where AI is writing 90% of the code. And then, in 12 months, we may be in a world where AI is writing essentially all of the code”
Three months ago I said that AI changes everything. I came to that after plenty of skepticism. There are still good reasons to doubt that AI will write all code, but my current reality is close.
For the infrastructure component I started at my new company, I’m probably north of 90% AI-written code. I don’t want to convince you — just share what I learned. In parts, because I approached this project differently from my first experiments with AI-assisted coding.
The service is written in Go with few dependencies and an OpenAPI-compatible REST API. At its core, it sends and receives emails. I also generated SDKs for Python and TypeScript with a custom SDK generator. In total: about 40,000 lines, including Go, YAML, Pulumi, and some custom SDK glue.
I set a high bar, especially that I can operate it reliably. I’ve run similar systems before and knew what I wanted.
Setting it in Context
Some startups are already near 100% AI-generated. I know, because many build in the open and you can see their code. Whether that works long-term remains to be seen. I still treat every line as my responsibility, judged as if I wrote it myself. AI doesn’t change that.
There are no weird files that shouldn’t belong there, no duplicate implementations, and no emojis all over the place. The comments still follow the style I want and, crucially, often aren’t there. I pay close attention to the fundamentals of system architecture, code layout, and database interaction. I’m incredibly opinionated. As a result, there are certain things I don’t let the AI do. I know it won’t reach the point where I could sign off on a commit. That’s why it’s not 100%.
As contrast: another quick prototype we built is a mess of unclear database tgables, markdown file clutter in the repo, and boatloads of unwanted emojis. It served its purpose — validate an idea — but wasn’t built to last, and we had no expectation to that end.
Foundation Building
I began in the traditional way: system design, schema, architecture. At this state I don’t let the AI write, but I loop it in AI as a kind of rubber duck. The back-and-forth helps me see mistakes, even if I don’t need or trust the answers.
I did get the foundation wrong once. I initially argued myself into a more complex setup than I wanted. That’s a part where I later used the LLM to redo a larger part early and clean it up.
For AI-generated or AI-supported code, I now end up with a stack that looks something like something I often wanted, but was too hard to do by hand:
-
Raw SQL: This is probably the biggest change to how I used to write code. I really like using an ORM, but I don’t like some of its effects. In particular, once you approach the ORM’s limits, you’re forced to switch to handwritten SQL. That mapping is often tedious because you lose some of the powers the ORM gives you. Another consequence is that it’s very hard to find the underlying queries, which makes debugging harder. Seeing the actual SQL in your code and in the database log is powerful. You always lose that with an ORM.
The fact that I no longer have to write SQL because the AI does it for me is a game changer.
I also use raw SQL for migrations now.
-
OpenAPI first: I tried various approaches here. There are many frameworks you can use. I ended up first generating the OpenAPI specification and then using code generation from there to the interface layer. This approach works better with AI-generated code. The OpenAPI specification is now the canonical one that both clients and server shim is based on.
Iteration
Today I use Claude Code and Codex. Each has strengths, but the constant is Codex for code review after PRs. It’s very good at that. Claude is indispensable still when debugging and needing a lot of tool access (eg: why do I have a deadlock, why is there corrupted data in the database etc.). The working together of the two is where it’s most magical. Claude might find the data, Codex might understand it better.
I cannot stress enough how bad the code from these agents can be if you’re not careful. While they understand system architecture and how to build something, they can’t keep the whole picture in scope. They will recreate things that already exist. They create abstractions that are completely inappropriate for the scale of the problem.
You constantly need to learn how to bring the right information to the context. For me, this means pointing the AI to existing implementations and giving it very specific instructions on how to follow along.
I generally create PR-sized chunks that I can review. There are two paths to this:
-
Agent loop with finishing touches: Prompt until the result is close, then clean up.
-
Lockstep loop: Earlier I went edit by edit. Now I lean on the first method most of the time, keeping a todo list for cleanups before merge.
It requires intuition to know when each approach is more likely to lead to the right results. Familiarity with the agent also helps understanding when a task will not go anywhere, avoiding wasted cycles.
Where It Fails
The most important piece of working with an agent is the same as regular software engineering. You need to understand your state machines, how the system behaves at any point in time, your database.
It is easy to create systems that appear to behave correctly but have unclear runtime behavior when relying on agents. For instance, the AI doesn’t fully comprehend threading or goroutines. If you don’t keep the bad decisions at bay early it, you won’t be able to operate it in a stable manner later.
Here’s an example: I asked it to build a rate limiter. It “worked” but lacked jitter and used poor storage decisions. Easy to fix if you know rate limiters, dangerous if you don’t.
Agents also operate on conventional wisdom from the internet and in tern do things I would never do myself. It loves to use dependencies (particularly outdated ones). It loves to swallow errors and take away all tracebacks. I’d rather uphold strong invariants and let code crash loudly when they fail, than hide problems. If you don’t fight this, you end up with opaque, unobservable systems.
Where It Shines
For me, this has reached the point where I can’t imagine working any other way. Yes, I could probably have done it without AI. But I would have built a different system in parts because I would have made different trade-offs. This way of working unlocks paths I’d normally skip or defer.
Here are some of the things I enjoyed a lot on this project:
-
Research + code, instead of research and code later: Some things that would have taken me a day or two to figure out now take 10 to 15 minutes.
It allows me to directly play with one or two implementations of a problem. It moves me from abstract contemplation to hands on evaluation. -
Trying out things: I tried three different OpenAPI implementations and approaches in a day.
-
Constant refactoring: The code looks more organized than it would otherwise have been because the cost of refactoring is quite low. You need to know what you do, but if set up well, refactoring becomes easy.
-
Infrastructure: Claude got me through AWS and Pulumi. Work I generally dislike became a few days instead of weeks. It also debugged the setup issues as it was going through them. I barely had to read the docs.
-
Adopting new patterns: While they suck at writing tests, they turned out great at setting up test infrastructure I didn’t know I needed. I got a recommendation on Twitter to use testcontainers for testing against Postgres. The approach runs migrations once and then creates database clones per test. That turns out to be super useful. It would have been quite an involved project to migrate to. Claude did it in an hour for all tests.
-
SQL quality: It writes solid SQL I could never remember. I just need to review which I can. But to this day I suck at remembering
MERGE
andWITH
when writing it.
What does it mean?
Is 90% of code going to be written by AI? I don’t know. What I do know is, that for me, on this project, the answer is already yes. I’m part of that growing subset of developers who are building real systems this way.
At the same time, for me, AI doesn’t own the code. I still review every line, shape the architecture, and carry the responsibility for how it runs in production. But the sheer volume of what I now let an agent generate would have been unthinkable even six months ago.
That’s why I’m convinced this isn’t some far-off prediction. It’s already here — just unevenly distributed — and the number of developers working like this is only going to grow.
That said, none of this removes the need to actually be a good engineer. If you let the AI take over without judgment, you’ll end up with brittle systems and painful surprises (data loss, security holes, unscalable software). The tools are powerful, but they don’t absolve you of responsibility.
September 29, 2025 12:00 AM UTC
September 28, 2025
Daniel Roy Greenfeld
TIL: Loading .env files with uv run
We don't need python-dotenv, use uv run
with --env-file
, and your env vars from .env
get loaded.
For example, if we've got a FastAPI or Air project we can run it locally with env vars like:
uv run --env-file .env fastapi dev
You can specific different env files for different environments, like .env.dev
, .env.prod
, etc.
uv run --env-file .env.dev fastapi dev
All credit goes to Audrey Roy Greenfeld for pointing this out to me.
September 28, 2025 10:44 PM UTC
September 26, 2025
Real Python
The Real Python Podcast – Episode #267: Managing Feature Flags & Comparing Python Visualization Libraries
What's a good way to enable or disable code paths without redeploying the software? How can you use feature flags to toggle functionality for specific users of your application? Christopher Trudeau is back on the show this week, bringing another batch of PyCoder's Weekly articles and projects.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
September 26, 2025 12:00 PM UTC
Julien Tayon
Abusing yahi -a log based statistic tool à la awstats- to plot histograms/date series from CSV
pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight { background: #eeffcc; } .highlight .c { color: #408090; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #007020; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #007020 } /* Comment.Preproc */ .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #333333 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0044DD } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #007020 } /* Keyword.Pseudo */ .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #902000 } /* Keyword.Type */ .highlight .m { color: #208050 } /* Literal.Number */ .highlight .s { color: #4070a0 } /* Literal.String */ .highlight .na { color: #4070a0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .highlight .no { color: #60add5 } /* Name.Constant */ .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #007020 } /* Name.Exception */ .highlight .nf { color: #06287e } /* Name.Function */ .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #bb60d5 } /* Name.Variable */ .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #208050 } /* Literal.Number.Bin */ .highlight .mf { color: #208050 } /* Literal.Number.Float */ .highlight .mh { color: #208050 } /* Literal.Number.Hex */ .highlight .mi { color: #208050 } /* Literal.Number.Integer */ .highlight .mo { color: #208050 } /* Literal.Number.Oct */ .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ .highlight .sc { color: #4070a0 } /* Literal.String.Char */ .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { color: #c65d09 } /* Literal.String.Other */ .highlight .sr { color: #235388 } /* Literal.String.Regex */ .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ .highlight .ss { color: #517918 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #06287e } /* Name.Function.Magic */ .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ div.admonition, div.topic, blockquote { clear: left; } div.admonition { margin: 20px 0px; padding: 10px 30px; background-color: #EEE; border: 1px solid #CCC; } div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { background-color: #FBFBFB; border-bottom: 1px solid #fafafa; } div.admonition p.admonition-title { font-family: Georgia, serif; font-weight: normal; font-size: 24px; margin: 0 0 10px 0; padding: 0; line-height: 1; } div.admonition p.last { margin-bottom: 0; } /* -- topics ---------------------------------------------------------------- */ nav.contents, aside.topic, div.topic { border: 1px solid #ccc; padding: 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; }
Foreword what is yahi?
Yahi is a python module that can installed with pip to make a all in one static html page aggregating the data from a web server. Actually, as shown with the parsing of auth.log in the documentation, it is pretty much a versatile log analyser based on regexp enabling various aggregation : by geo_ip, by histograms, chronological series.
The demo is here. It pretty much is close to awstats which is discontinued or goaccess.
As the author of yahi I may not be very objective, but I claim it is having a quality other tools don't have : it is easy to abuse for format other than web logs and here is an example out of the norm : parsing CSV.
Ploting histograms or time series from CSV
CSV that can be parsed as regexp
There are simple cases when CSV don’t have strings embedded and are litteraly comma separated integers/floats.
In this case, CSV can be parsed as a regexp and it’s all the more convenient when the CSV has no title.
Here is an example using the CSV coming from the CSV generated by trollometre
A line is made off a timestamp followed by various (int) counters.
Tip
For the sake of ease of use I hacked the date_pattern format to accept “%s” as a timestamp (while it’s normally only valid strptime formater)
from archery import mdict
from yahi import notch, shoot
from json import dump
import re
context=notch(
off="user_agent,geo_ip",
log_format="custom",
output_format="json",
date_pattern="%s",
log_pattern="""^(?P<datetime>[^,]+),
(?P<nb_fr>[^,]+),
(?P<nb_total>[^,]+),?.*
$""")
date_formater= lambda dt :"%s-%s-%s" % ( dt.year, dt.month, dt.day)
res= shoot(
context,
lambda data: mdict({
"date_fr" :
mdict({ date_formater(data["_datetime"]) :
int(data["nb_fr"]) }),
"hour_fr" :
mdict({ "%02d" % data["_datetime"].hour :
int(data["nb_fr"]) }),
"date_all" :
mdict({ date_formater(data["_datetime"]) :
int(data["nb_total"]) }),
"hour_all" :
mdict({ "%02d" % data["_datetime"].hour :
int(data["nb_total"]) }),
"total" : 1
})
)
dump(res,open("data.js","w"), indent=4)
Then, all that remains to do is
python test.py < ~/trollometre.csv && yahi_all_in_one_maker && firefox aio.html
You click on time series and can see the either the chronological time serie

Or the profile by hour

Raw approach with csv.DictReader
Let’s take the use case where my job insurance sent me the data of all the 10000 jobless persons in my vicinity consisting for each line of :
opaque id,civility,firstname, lastname, email,email of the counseler following the job less person
For this CSV, I have the title as the first line, and have strings that may countain “,”, hence the regexp approach is strongly ill advised.
What we want here is 2 histograms :
- the frequency of the firstname (that does not violates RGPD) and that I can share,
- how much each adviser is counseling.
Here is the code
from csv import DictReader
from json import dump
from archery import mdict
res=mdict()
with open("/home/jul/Téléchargements/GEMESCAPEG.csv") as f:
for l in DictReader(f):
res+=mdict(by_ref = mdict({l["Referent"]: 1}), by_prenom=mdict({l["Prenom"]:1}))
dump(res, open("data.js", "w"), indent=4)
Then, all that remains to do is
yahi_all_in_one_maker && firefox aio.html
And here we can see that each counseler is following on average ~250 jobless persons.

And the frequency of the firstname

Which correlated with the demographic of the firstname as included here below tends to prove that the older you are the less likeky you are to be jobless.
I am not saying ageism, the data are doing it for me.



September 26, 2025 09:59 AM UTC
Python Engineering at Microsoft
Simplifying Resource Management in mssql-python through Context Manager
Reviewed by: Sumit Sarabhai and Gaurav Sharma
If you’ve worked with databases in Python, you know the boilerplate: open a connection, create a cursor, run queries, commit or rollback transactions, close cursors and connection. Forgetting just one cleanup step can lead to resource leaks (open connections) or even inconsistent data. That’s where context managers step in.
We’ve introduced context manager support in mssql‑python driver, enabling Python applications to interact with SQL Server and Azure SQL more safely, cleanly, and in a truly Pythonic way.
Try it here
You can install driver using pip install mssql-pythonCalling all Python + SQL developers! We invite the community to try out mssql-python and help us shape the future of high-performance SQL Server connectivity in Python.!
Why Context Managers?
In Python, the with
statement is syntactic sugar for resource management. It actively sets up resources when you enter a block and automatically cleans them up when you exit — even if an exception is raised.
Think of it as hiring a helper:
- They prepare the workspace before you begin.
- They pack everything up when you’re done.
- If something breaks midway, they handle the cleanup for you.
The Problem: Managing Connections and Cursors
Earlier, working with Python applications and SQL server/Azure SQL looked something like this:
from mssql_python import connect
conn = connect(connection_string)
cursor = conn.cursor()
try:
cursor.execute("SELECT * FROM users")
for row in cursor:
print(row)
finally:
cursor.close()
conn.close()
This works perfectly fine. But imagine if your code had multiple cursors, multiple queries, and exception handling sprinkled all over. Closing every connection and cursor manually becomes tedious and error-prone. Miss a close()
somewhere, and you have a resource leak.
That’s where Python’s with
statement — the context manager — comes to the rescue. mssql_python
not only supports it for connections but also for cursors, which makes resource management nearly effortless.
Using Context Managers with Connections
Now comes the real magic — connection-level context managers. When you wrap a connection in a with
block, several things happen under the hood:
- If everything succeeds, the transaction is committed.
- If an exception occurs, the transaction is rolled back.
- The connection is always closed when leaving the block.
Example:
from mssql_python import connect
with connect(connection_string) as conn:
cursor = conn.cursor()
cursor.execute("INSERT INTO users (name) VALUES ('Alice')")
# If no exception → commit happens automatically
# If exception → rollback happens automatically
# Connection is closed automatically here
Equivalent traditional approach:
conn = connect(connection_string)
try:
cursor = conn.cursor()
cursor.execute("INSERT INTO users (name) VALUES ('Alice')")
if not conn.autocommit:
conn.commit()
except:
if not conn.autocommit:
conn.rollback()
raise
finally:
conn.close()
How It Works Internally
- Entering the block
- Connection is opened and assigned to conn.
- All operations inside the block run using this connection.
- Exiting the block
- No exception: If
autocommit=False
, transactions are committed. - Exception raised: If
autocommit=False
, uncommitted changes are rolled back. The exception propagates unless handled.
- No exception: If
- Cleanup: Connection is always closed, preventing resource leaks.
Use case: Perfect for transactional code — inserts, updates, deletes — where you want automatic commit/rollback.
Using Context Managers with Cursors
Cursors in mssql_python
now support the with
statement. The context here is tied to the cursor resource, not the transaction.
with conn.cursor() as cursor:
cursor.execute("SELECT * FROM users")
for row in cursor:
print(row)
# Cursor is automatically closed here
What happens here?
- Entering the block: A new cursor is created.
- Inside the block: All SQL statements execute using this cursor.
- Exiting the block: The cursor is automatically closed — no need to call
cursor.close()
manually. - Transactions: The cursor itself doesn’t manage transactions. Commit/rollback is controlled by the connection.
- If
autocommit=False
, changes are committed or rolled back at the connection level. - If
autocommit=True
, each statement is committed immediately as it executes.
- If
Above code is equivalent to the traditional try-finally approach:
cursor = conn.cursor()
try:
cursor.execute("SELECT * FROM users")
for row in cursor:
print(row)
finally:
cursor.close()
Use case: Best for read-only queries where you don’t want to worry about cursor leaks.
Important
If you just want to ensure the cursor closes properly without worrying about transactions, this is the simplest and safest approach.Image 1: Workflow of Context Manager in Connections and Cursor
Practical Examples
Example 1: Safe SELECT Queries
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("SELECT * FROM users WHERE age > 25")
for row in cursor:
print(row)
# Cursor closed, connection still open until block ends
# Connection is closed
Example 2: Multiple Operations in One Transaction
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("INSERT INTO users (name) VALUES ('Bob')")
cursor.execute("UPDATE users SET age = age + 1 WHERE name = 'Alice'")
# Everything committed automatically if no exception
Example 3: Handling Exceptions Automatically
try:
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("INSERT INTO users (name) VALUES ('Charlie')")
# Simulate error
raise ValueError("Oops, something went wrong")
except ValueError as e:
print("Transaction rolled back due to:", e)
# Connection closed automatically, rollback executed
Real-Life Scenarios
Example 1: Web Applications
In a web app where each request inserts or fetches data:
def add_user(name):
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("INSERT INTO users (name) VALUES (?)", (name,))
- Guarantees commit/rollback automatically.
- No open connections piling up.
- Clean, readable, and safe code for high-traffic scenarios.
Example 2: Data Migration / ETL
Migrating data between tables:
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("INSERT INTO archive_users SELECT * FROM users WHERE inactive=1")
cursor.execute("DELETE FROM users WHERE inactive=1")
- If any statement fails, rollback happens automatically.
- Prevents partial migration, keeping data consistent.
Example 3: Automated Reporting
Running multiple queries for analytics:
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("SELECT COUNT(*) FROM users")
user_count = cursor.fetchone()[0]
cursor.execute("SELECT department, COUNT(*) FROM employees GROUP BY department")
for row in cursor:
print(row)
- Cursors closed automatically after each block.
- Makes scripts modular and maintainable.
Example 4: Financial Transactions
Simple bank transfer example:
def transfer_funds(from_account, to_account, amount):
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("UPDATE accounts SET balance = balance - ? WHERE id=?", (amount, from_account))
cursor.execute("UPDATE accounts SET balance = balance + ? WHERE id=?", (amount, to_account))
- Automatic rollback on failure ensures money isn’t lost or double-counted.
- Eliminates verbose error-handling boilerplate.
Example 5: Ad-Hoc Data Exploration
When exploring data in scripts or notebooks:
with connect(connection_string) as conn:
with conn.cursor() as cursor:
cursor.execute("SELECT AVG(salary) FROM employees")
print("Average salary:", cursor.fetchone()[0])
- Perfect for quick queries.
- No forgotten
close()
calls. - Encourages clean, reusable query blocks.
Takeaway
Python’s philosophy is “simple is better than complex.” With context managers in mssql_python
, we’ve brought that simplicity to SQL Server interactions with python apps making lives of the developers easier.
Next time you’re working with mssql_python
, try wrapping your connections and cursors with with
. You’ll write less code, make fewer mistakes, and your future self will thank you. Whether it’s a high-traffic web application, an ETL script, or exploratory analysis, context managers simplify life, make code safer, and reduce errors.
Remember, context manager will help you with:
- Less boilerplate code: No longer try-finally for cursors or connections.
- Automatic transaction management: Commit or rollback is handled based on success or failure.
- Safe resource cleanup: Prevents resource leaks with automatic closing.
- Readable and Pythonic: Nested with blocks clearly show the scope of cursor and connection usage.
Try It and Share Your Feedback!
We invite you to:
- Check-out the mssql-python driver and integrate it into your projects.
- Share your thoughts: Open issues, suggest features, and contribute to the project.
- Join the conversation: GitHub Discussions | SQL Server Tech Community.
Use Python Driver with Free Azure SQL Database
You can use the Python Driver with the free version of Azure SQL Database! Deploy Azure SQL Database for free
Deploy Azure SQL Managed Instance for free Perfect for testing, development, or learning scenarios without incurring costs.
The post Simplifying Resource Management in mssql-python through Context Manager appeared first on Microsoft for Python Developers Blog.
September 26, 2025 09:49 AM UTC
Wingware
Wing Python IDE Version 11.0.5 - September 26, 2025
Wing Python IDE version 11.0.5 has been released. It fixes remote development with older Python versions, improves Python code analysis when using Python 3.14 and for type hints on instance attributes, and fixes Find Uses and Refactoring to find all overrides of a method.

Downloads
Wing 10 and earlier versions are not affected by installation of Wing 11 and may be installed and used independently. However, project files for Wing 10 and earlier are converted when opened by Wing 11 and should be saved under a new name, since Wing 11 projects cannot be opened by older versions of Wing.
New in Wing 11
Improved AI Assisted Development
Wing 11 improves the user interface for AI assisted development by introducing two separate tools AI Coder and AI Chat. AI Coder can be used to write, redesign, or extend code in the current editor. AI Chat can be used to ask about code or iterate in creating a design or new code without directly modifying the code in an editor.
Wing 11's AI assisted development features now support not just OpenAI but also Claude, Grok, Gemini, Perplexity, Mistral, Deepseek, and any other OpenAI completions API compatible AI provider.
This release also improves setting up AI request context, so that both automatically and manually selected and described context items may be paired with an AI request. AI request contexts can now be stored, optionally so they are shared by all projects, and may be used independently with different AI features.
AI requests can now also be stored in the current project or shared with all projects, and Wing comes preconfigured with a set of commonly used requests. In addition to changing code in the current editor, stored requests may create a new untitled file or run instead in AI Chat. Wing 11 also introduces options for changing code within an editor, including replacing code, commenting out code, or starting a diff/merge session to either accept or reject changes.
Wing 11 also supports using AI to generate commit messages based on the changes being committed to a revision control system.
You can now also configure multiple AI providers for easier access to different models.
For details see AI Assisted Development under Wing Manual in Wing 11's Help menu.
Package Management with uv
Wing Pro 11 adds support for the uv package manager in the New Project dialog and the Packages tool.
For details see Project Manager > Creating Projects > Creating Python Environments and Package Manager > Package Management with uv under Wing Manual in Wing 11's Help menu.
Improved Python Code Analysis
Wing 11 makes substantial improvements to Python code analysis, with better support for literals such as dicts and sets, parametrized type aliases, typing.Self, type of variables on the def or class line that declares them, generic classes with [...], __all__ in *.pyi files, subscripts in typing.Type and similar, type aliases, type hints in strings, type[...] and tuple[...], @functools.cached_property, base classes found also in .pyi files, and typing.Literal[...].
Updated Localizations
Wing 11 updates the German, French, and Russian localizations, and introduces a new experimental AI-generated Spanish localization. The Spanish localization and the new AI-generated strings in the French and Russian localizations may be accessed with the new User Interface > Include AI Translated Strings preference.
Improved diff/merge
Wing Pro 11 adds floating buttons directly between the editors to make navigating differences and merging easier, allows undoing previously merged changes, and does a better job managing scratch buffers, scroll locking, and sizing of merged ranges.
For details see Difference and Merge under Wing Manual in Wing 11's Help menu.
Other Minor Features and Improvements
Wing 11 also adds support for Python 3.14, improves the custom key binding assignment user interface, adds a Files > Auto-Save Files When Wing Loses Focus preference, warns immediately when opening a project with an invalid Python Executable configuration, allows clearing recent menus, expands the set of available special environment variables for project configuration, and makes a number of other bug fixes and usability improvements.
Changes and Incompatibilities
Since Wing 11 replaced the AI tool with AI Coder and AI Chat, and AI configuration is completely different than in Wing 10, you will need to reconfigure your AI integration manually in Wing 11. This is done with Manage AI Providers in the AI menu. After adding the first provider configuration, Wing will set that provider as the default. You can switch between providers with Switch to Provider in the AI menu.
If you have questions, please don't hesitate to contact us at support@wingware.com.
September 26, 2025 01:00 AM UTC
Seth Michael Larson
GZipped files and streams may contain names
It's just another day, you're sending a bunch of files to a friend. For no particular reason you decide to name the archive with your controversial movie opinions:
$ tar -cf i-did-not-care-for-the-godfather.tar *.txt
$ gzip i-did-not-care-for-the-godfather.tar
Realizing you'd be sharing this file with others, you decide to rename the file.
$ mv i-did-not-care-for-the-godfather.tar.gz \
i-love-the-godfather.tar.gz
That's better! Now your secret is safe. You share the tarball with your colleague who notes your "good taste" in movies and proceeds to extract the archive.
$ gunzip --name i-love-the-godfather.tar.gz
i-love-the-godfather.tar.gz: 100.0% --
replaced with i-did-not-care-for-the-godfather.tar
Uh oh, your secret is out! The decompressed .tar
file was named i-did-not-care-for-the-godfather.tar
instead of i-love-the-godfather.tar
like we intended. How could this happen?
It turns out that GZip streams have fields for information about the original file
including the filename, modified timestamp, and comments.
This means GZip streams can leak secret information if it's contained within the file metadata.
Luckily tar
when using $ tar -czf
(which is the typical workflow) instead of the gzip
and gunzip
commands
doesn't preserve the original filename in the GZip stream.
If you do have to use gzip
, use the --no-name
option to strip this information
from the GZip stream. Use a hex editor to check a GZip compressed file if you are unsure.
Thanks for keeping RSS alive! ♥
September 26, 2025 12:00 AM UTC
September 25, 2025
PyPodcats
Episode 10: With Una Galyeva
Learn about Una's journey. Una is a driving force behind PyLadies Amsterdam, a Microsoft MVP, AI4ALL Advisory board member, and Head of Artificial Intelligence.Learn about Una's journey. Una is a driving force behind PyLadies Amsterdam, a Microsoft MVP, AI4ALL Advisory board member, and Head of Artificial Intelligence.
We interviewed Una Galyeva.
With over 19 years of experience in Data and AI, Una Galyeva held various positions, from hands-on Data and AI development to leading Data and AI teams and departments. She is a driving force behind PyLadies Amsterdam, a Microsoft MVP, AI4ALL Advisory board member, and Head of Artificial Intelligence.
In this episode, Una shares her journey of relocating to Amsterdam, navigating the tech industry, and building inclusive communities. From freelancing challenges to leading innovative workshops, Una offers valuable insights on career growth, and fostering diversity in tech.
Be sure to listen to the episode to learn all about Una’s inspiring story!
Topic discussed
- Introductions
- Getting to know Una
- Una’s journey and relocation in Amsterdam
- Her transition from Exectutive Director to freelancer
- Insights to freelancing
- PyLadies Amsterdam
- Excitement of freelancing and working on diverse topics
- Her community philosophy, especially for PyLadies Amsterdam
- Job search and recruitment
Links from the show
- PyLadies Amsterdam: http://amsterdam.pyladies.com/
September 25, 2025 09:00 AM UTC
September 24, 2025
Antonio Cuni
Tracing JITs in the real world @ CPython Core Dev Sprint
Tracing JITs in the real world @ CPython Core Dev Sprint
.slide { border: 2px solid #ddd; border-radius: 8px; margin: 2em 0; background: #f9f9f9; max-width: 100%; width: 100%; box-sizing: border-box; padding: 2em; background: white; border-radius: 6px 6px 0 0; border-bottom: 2px solid #eee; display: block; min-height: 400px; height: auto; overflow-y: auto; overflow-x: hidden;}.slide h1, .slide h2, .slide h3 { margin-top: 0; color: #333;}Last week I got to take part in the CPython Core Developer Sprint inCambridge, hosted by ARM and brilliantlyorganized by Diego Russo-- about ~50 core devs and guests were there, and I was excited to join as oneof the guests.
I had three main areas of focus:
C API: this was a follow up of what we discussed at the C API summit at EuroPython. The current C API is problematic, so we are exploring ideas for the development of PyNI (Python Native Interface), whose design will likely be heavily inspired by HPy. It's important to underline that this is just the beginning and the entire process will require multiple PEPs.
fancycompleter This is a small PR which I started months ago, to enable colorful tab completions within the Python REPL. I wrote the original version of fancycompleter 15 years ago, but colorful completions work only in combination with PyREPL. Now PyREPL is part of the standard library and enabled by default, so we can finally upstream it. I hope to see it merged soon.
"JIT stuff": I spent a considerable amount of time talking to the people who are working on the CPython JIT (in particular Mark, Brandt, Savannah, Ken Jin and Diego). Knowledge transfer worked in both ways: I learned a lot about the internal details of CPython's JIT, and conversely I shared with them some of the experience, pain points and gut feelings which I got by working many years on PyPy.
In particular, on the first day I presented a talk titled Tracing JIT and real world Python (slides and source code).
What follows is an annotated version of the slides.
September 24, 2025 09:37 PM UTC
Slides for my EuroPython 2025 talks
EuroPython 2025 slides
Here are the slides for the three speeches which I gave atEuroPython 2025: