Planet Python
Last update: March 23, 2026 04:44 PM UTC
March 23, 2026
"Michael Kennedy's Thoughts on Technology"
Replacing Flask with Robyn wasn't worth it

TL;DR; I converted Python Bytes from Quart/Flask to the Rust-backed Robyn framework and benchmarked it with Locust. There was no meaningful speed or memory improvement - and Robyn actually used more memory. Framework maturity, ecosystem depth, and app server flexibility still matter more than raw benchmark numbers.
Last week I played with the idea of replacing Quart (async Flask ) with Robyn for our bigger web apps. Robyn is built almost entirely in Rust, and in the benchmarks, it looks dramatically better. Not just a little bit faster, but 25 times faster. However, if you’ve been around the block for a while, you know that benchmarks and how things work for your app and your situation are not always the same thing.
So I picked the simplest complex app that I run, Python Bytes, and converted it entirely to run on the Robyn framework. This took a few hours of careful work and experimenting, and I even had to create a Python package to allow Robyn to run the Chameleon template language.
When I was done, it was time to fire up Locust and see if there was any dramatic performance improvements. I certainly wasn’t expecting 25x, but 2x? 1.5x? That would have been really impressive.
Did Robyn improve speed or memory over Flask?
The results were in and the answer was just about no difference in RPS or latency. It turns out that almost all the computational time is in the logic of our app, which of course doesn’t change and I never intended to change it.
- Requests per second: No meaningful difference between Robyn and Quart/Granian
- Latency: Essentially identical under load
- Memory: Robyn actually used more memory, not less
Another area I was hoping to optimize is memory. Our web apps use a lot of memory for what they are. They’re certainly not trivial. But running a couple of copies of the app in a web garden was using way more than I expected that they should. And I thought moving closer to Rust might have positive influences for memory too.
It turns out the Robyn fork actually used more memory, not less, than the current setup. After all, our web apps run on Granian, which is mostly Rust right up to the Flask framework itself already.
Why Flask’s maturity still beats Robyn’s speed
So our fun little spike to explore the Robyn framework is going to remain just that. I’m sticking with Flask. I’ve talked about this before, but maturity in a library or framework is a big plus. The ecosystem for Flask/Quart is much bigger and more polished than for the smaller Robyn framework.
More than that, the app server runtime for Robyn is much less polished than some of the pluggable app servers out there. Think Granian, Gunicorn, uvicorn, etc. For example, Robyn does not support web garden process recycling. In many servers you can say after five hours or 10,000 requests or something like that, just slowly take the request out of a process, spin up a new one and shut down the old one just to keep things fresh. This helps if you’re using some library that holds on to too many caches or some other weird memory thing.
Was the Robyn experiment a waste of time?
Even though I spent maybe close to six hours working on this exploration and decided not to use it, I still found it super valuable. I created the fun Chameleon Robyn package to help people using Robyn have a greater choice of template languages. I got to see my apps from multiple perspectives. I built out some tooling for Claude that I’m going to write about later that is generally really awesome. And I ended up saving significant memory for some of my biggest web apps by just spending more time thinking about how I’m running them currently in Granian and Flask.
Antonio Cuni
My first OSS commit turns 20 today
My first OSS commit turns 20 today
Some time ago I realized that it was 20 years since I started to contribute toOpen Source. It's easy to remember, because I started to work on PyPy as part of mymaster's thesis and I graduated in 2006.
So, I did a bit of archeology to find the first commit:
$ cd ~/pypy/pypy && git show 1a086d45d9 --no-patchcommit 1a086d45d9Author: Antonio Cuni <anto.cuni@gmail.com>Date: Wed Mar 22 14:01:42 2006 +0000 Initial commit of the CLI backend!!! note "svn, hg, git"
Funny thing, the original commit was not in `git`, which was just a few months oldat the time. In 2006 PyPy was using `subversion`, then a few years later [migratedto mercurial](../../2010/12/pypy-migrates-to-mercurial-3308736161543832134.md), and many years later[migrated to git](https://pypy.org/posts/2023/12/pypy-moved-to-git-github.html).I managed to find traces of the original `svn` commit in the archives of the[pypy-svn](https://marc.info/?l=pypy-svn&m=118495688023240) mailing list.
Real Python
How to Use Note-Taking to Learn Python
Learning Python can be genuinely hard, and it’s normal to struggle with fundamental concepts. Research has shown that note-taking is invaluable when learning new things. This guide will help you get the most out of your learning efforts by showing you how to take better notes as you walk through an existing tutorial and keep handwritten notes on the side:
In this guide, you’ll begin by briefly learning about the benefits of note-taking. Then, you’ll follow along with an existing Real Python tutorial as you perform note-taking steps to help make the information in the tutorial really stick. To help you stay organized as you practice, download the Python Note-Taking Worksheet below. It outlines the process you’ll learn here and provides a repeatable framework you can use with future tutorials:
Get Your PDF: Click here to download your free Python Note-Taking Worksheet that outlines that note-taking process.
Take the Quiz: Test your knowledge with our interactive “How to Use Note-Taking to Learn Python” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
How to Use Note-Taking to Learn PythonTest your understanding of note-taking techniques that help you learn Python more effectively and retain what you study.
What Is Python Note-Taking?
In the context of learning, note-taking is the process of recording information from a source while you’re consuming it. A traditional example is a student jotting down key concepts during a lecture. Another example is typing out lines of code or unfamiliar words while watching a video course, listening to a presentation, or reading a learning resource.
In this guide, Python note-taking refers to taking notes specific to learning Python.
People take notes for a variety of reasons. Usually, the intent is to return to the notes at a later time to remind the note-taker of the information covered during the learning session.
In addition to the value of having a physical set of notes to refer back to, studies have found that the act of taking notes alone improves a student’s ability to recall information on a topic.
This guide focuses on handwritten note-taking—that is, using a writing utensil and paper. Several studies suggest that this form of note-taking is especially effective for understanding a topic and remembering it later. If taking notes by hand isn’t viable for you, don’t worry! The concepts presented here should be applicable to other forms of note-taking as well.
Prerequisites
Since this guide focuses on taking notes while learning Python programming, you’ll start by referencing the Real Python tutorial Python for Loops: The Pythonic Way. This resource is a strong choice because it clearly explains a fundamental programming concept that you’ll use throughout your Python journey.
Once you have the resource open in your browser, set aside a few pieces of paper and have a pen or pencil ready. Alternatively, you can take notes on a tablet with a stylus or another writing tool.
Generally, taking notes by hand has a stronger impact on learning than other methods, such as typing into a text document. For more information on the effectiveness of taking notes by hand versus typing, see this article from the Harvard Graduate School of Education.
Step 1: Write Down Major Concepts
With your note-taking tools ready, start by skimming the learning resource. Usually, you want to look at the major headings to see what topics the material covers. For Real Python content, you can instead just look at the table of contents at the top of the page, since this lists the main sections.
The major headings for your example resource are as follows:
- Getting Started with the Python
forLoop - Traversing Built-In Collections in Python
- Using Advanced
forLoop Syntax - Exploring Pythonic Looping Techniques
- Understanding Common Pitfalls in
forLoops - Using
forLoops vs Comprehensions - Using
async forLoops for Asynchronous Iteration
The list above doesn’t include subheadings like “Sequences: Lists, Tuples, Strings, and Ranges” under “Traversing Built-In Collections in Python”. For now, stick to top-level headings.
Read the full article at https://realpython.com/python-note-taking-guide/ »
[ 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: Strings and Character Data in Python
In this quiz, you’ll test your understanding of Python Strings and Character Data.
This quiz helps you deepen your understanding of Python’s string and byte data types. You’ll explore core concepts like string immutability, interpolation with f-strings, Unicode handling, key string methods, and working with bytes objects.
[ 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 ]
Tryton News
Relase 0.8.0 of mt940
We are proud to announce the release of the version 0.8.0 of mt940.
mt940 is a library to parse MT940 files. MT940 is a specific SWIFT message type used by the SWIFT network to send and receive end-of-day bank account statements.
In addition to bug-fixes, this release contains the following improvements:
- Upgrade to pyproject
- Add fund code to transaction
- Remove support for Python older than 3.9
mt940 is available on PyPI: mt940 0.8.0.
1 post - 1 participant
Release 0.4.0 of febelfin-coda
We are proud to announce the release of the version 0.4.0 of febelfin-coda.
febelfin-coda is a library to parse CODA files. This bank standard (also called CODA) specifies the lay-out for the electronic files, by banks to customers, of the account transactions and the information concerning the enclosures in connection with the movement.
In addition to bug-fixes, this release contains the following improvements:
- Upgrade to pyproject
- Add support for Python 3.13 and 3.14
- Remove support for Python older than 3.9
febelfin-coda is available on PyPI: febelfin-coda 0.4.0.
1 post - 1 participant
Release 0.2.0 of aeb43
We are proud to announce the release of the version 0.2.0 of aeb43.
aeb43 is a library to parse AEB43 files. AEB43 is a standard, fixed-length 80-character file format used by Spanish banks for transmitting bank statements, transaction details, and account balances.
In addition to bug-fixes, this release contains the following improvements:
- Upgrade to pyproject
- Add support for Python 3.13 and 3.14
- Remove support for Python older than 3.9
aeb43 is available on PyPI: aeb43 0.2.0.
1 post - 1 participant
Release of Relatorio 0.12.0
We are proud to announce the release of Relatorio version 0.12.0.
Relatorio is a templating library mainly for OpenDocument using also OpenDocument as source format.
In addition to bug-fixes, this release contains the following improvements:
- Upgrade to pyproject
- Add support for Python 3.13 and 3.14
- Remove support for Python older than 3.9
The package is available at relatorio · PyPI
The documentation is available at https://docs.tryton.org/relatorio/0.12.0/
1 post - 1 participant
Antonio Cuni
Inside SPy, part 1: Motivations and Goals
Inside SPy🥸, part 1: Motivations and Goals
This is the first of a series of posts in which I will try to give a deep explanation ofSPy, including motivations, goals, rules of thelanguage, differences with Python and implementation details.
This post focuses primarily on the problem space: why Python is fundamentally hardto optimize, what trade-offs existing solutions require, and where current approachesfall short. Subsequent posts in this series will explore the solutions in depth. Fornow, let's start with the essential question: what is SPy?
!!! Success "" Before diving in, I want to express my gratitude to my employer, Anaconda, for giving me the opportunity to dedicate 100% of my time to this open-source project.
March 22, 2026
Reuven Lerner
Do you teach Python? Then check out course-setup
TL;DR: If you teach Python, then you should check out course-setup (https://pypi.org/project/course-setup/) at PyPI!
I’ve been teaching Python and Pandas for many years. And while I started my teaching career like many other instructors, with slides, I quickly discovered that it was better for my students — and for me! — to replace them with live coding.
Every day I start teaching, I open a new Jupyter or Marimo notebook, and I type. I type the day’s agenda. I type the code that I want to demonstrate, and then do it right there, in front of people. I type the instructions for each exercise we’re going to use. I type explanatory notes. If people have questions, I type those, along with my answers.
In other words, every day’s notebook contains a combination of documentation, demonstration, explanation, and exercise solutions. That combination is unique to the group I’m teaching. If we get sidetracked with someone’s question, that’s OK — I include whatever I can in each day’s notebook.
Teaching in this way raises some issues. Among the biggest: If I’m working on my own computer, then how can someone see the notebook that I’m writing? Obviously, I could scroll my screen up and down, but that’s frustrating for everyone, especially when we’re doing an exercise.
I was thus delighted to learn, years ago, about “gitautopush” (https://pypi.org/project/gitautopush/), a simple PyPI project that takes a local Git repository and monitors it for any changes. When something changes, it commits those changes to Git and then pushes them to a remote repository. The fact that GitHub renders Jupyter notebooks into HTML made this a perfect solution for me.
For years, then, my setup has been:
- Create a new directory
- Make that directory into a Git repo
- Connect the repo to GitHub
- Create a Jupyter notebook in that repo
- Run “gitautopush” in another terminal window
- When I’m done with the course, make the repo private and move it to an archive directory
This worked fine for many years, but it took about 10 minutes of prep before each class. I finally realized that this was silly: I’m a programmer, and shouldn’t I be automating repetitive tasks that take a long time?
That’s where course-setup started. I wrote two Python programs that would let me create a new course (doing all of the setup tasks I mentioned above) or retire an existing one. Did it do everything I wanted? No, but it was good enough.
Once I started to use uv, I turned these programs into uv tools, always available in my shell. I made some additional progress with course-setup, but most of my thoughts about improvements stayed on the back burner.
And then? I started to use Claude Code. I decided to see just how far I could improve course-setup with Claude Code — and to be honest, the improvements were beyond my wildest dreams:
- I tightened up the code a lot, adding oodles of automated tests and type hints. This is actually a must when using Claude Code; it reduces the chances of things going off the rails completely.
- I added a bunch of options, including a configuration file, giving sensible defaults but also allowing me to customize the precise type of notebook (Jupyter or Marimo), the package dependencies, and even what files I’ll want included.
- I made it possible to create a new notebook for each day of a multi-day class, whether the class is spread over days or weeks. I made it possible to start on a day other than today (for those rare occasions when I prepare things in advance), to skip the weekend, and even to take the Israeli weekend into account, teaching on Sunday and not on Friday.
- I improved the retire-course script, so that it can handle multiple retirements at once — and added an unretire-course script, for when I accidentally retire the wrong course.
- I typically create a zipfile from each day’s course. I added the archive-course utility to do that, giving me a zipfile for each day that I can easily e-mail to the course participants
- It supports MacOS, Windows, and Linux, with the configuration file in the platform-appropriate directory.
It’s hard to exaggerate how much of this work was done by Claude Code. I supervised, checked things, added new functionality, pushed back on a number of things it suggested, and am ultimately responsible. But really, the code itself was largely written by Claude, often using a number of agents working in parallel, and I couldn’t be happier with the result. I’ve included the CLAUDE.md file in the GitHub repo, if you’re interested in learning from it and/or using it.
This suite of utilities is now available on PyPI as “course-setup” (https://pypi.org/project/course-setup/). It includes a ton of functionality, and I’m always looking to improve it — so tell me how, or send a PR my way at https://github.com/reuven/course-setup!
The post Do you teach Python? Then check out course-setup appeared first on Reuven Lerner.
EuroPython
Humans of EuroPython: Niklas Mertsch
EuroPython runs on people power—real people giving their time to make it happen. No flashy titles, just real work: setting up rooms, guiding speakers, helping attendees find their way, or making sure everyone feels welcome. Some help run sessions, others support accessibility needs or troubleshoot the Wi-Fi.
It’s all about showing up, pitching in, and sharing a passion for Python. This is what a community looks like.
Today we’d like to introduce you to Niklas Mertsch, member of the Operations team at EuroPython 2025. Check out what he has to say about the volunteering experience.
Niklas Mertsch, member of the Operations team at EuroPython 2025EP: What&aposs one thing about the programming community that made you want to give back by volunteering?
For me, it is not about “giving back” but about “participating”. I started volunteering out of curiosity, and continued because of the people and interactions. It started with a conversation, and it led to many more.
EP: Did you learn any new skills while volunteering at EuroPython? If so, which ones?
I can&apost name a “new” skill, but working with an intrinsically motivated, international and intercultural team definitely improved my social and communication skills.
EP: Did you have any unexpected or funny experiences during the EuroPython?
Tons of them, you never know what happens before or during the event. One time I just tried to print a WiFi QR code, then spent the next hours talking to someone I now call a good friend. And some months later that friend nudged me to answer these questions. You never know what you get and where it will lead you, but you know it will be good.
EP: Thank you for your work, Niklas!
Tryton News
Release 1.7.0 of python-sql
We are proud to announce the release of the version 1.7.0 of python-sql.
python-sql is a library to write SQL queries in a pythonic way. It is mainly developed for Tryton but it has no external dependencies and is agnostic to any framework or SQL database.
In addition to bug-fixes, this release contains the following improvements:
- Upgrade to pyproject
- Add support for array operators
- Remove the parentheses around the unary and binary operators
- Use the ordinal number as aliases for GROUP BY
- Check the coherence of the aliases of GROUP BY and ORDER BY expressions
- Do not use parameter for EXTRACT field
- Remove support for Python older than 3.9
python-sql is available on PyPI: python-sql 1.7.0.
1 post - 1 participant
March 20, 2026
Real Python
The Real Python Podcast – Episode #288: Automate Exploratory Data Analysis & Invent Python Comprehensions
How do you quickly get an understanding of what's inside a new set of data? How can you share an exploratory data analysis with your team? Christopher Trudeau is back on the show this week with 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 ]
Quiz: Python Decorators 101
In this quiz, you’ll test your understanding of Python Decorators 101.
Work through this quiz to review first-class functions, inner functions, and decorators, and learn how to create, reuse, and apply them to extend behavior cleanly in Python.
[ 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 ]
Armin Ronacher
Some Things Just Take Time
Trees take quite a while to grow. If someone 50 years ago planted a row of oaks or a chestnut tree on your plot of land, you have something that no amount of money or effort can replicate. The only way is to wait. Tree-lined roads, old gardens, houses sheltered by decades of canopy: if you want to start fresh on an empty plot, you will not be able to get that.
Because some things just take time.
We know this intuitively. We pay premiums for Swiss watches, Hermès bags and old properties precisely because of the time embedded in them. Either because of the time it took to build them or because of their age. We require age minimums for driving, voting, and drinking because we believe maturity only comes through lived experience.
Yet right now we also live in a time of instant gratification, and it’s entering how we build software and companies. As much as we can speed up code generation, the real defining element of a successful company or an Open Source project will continue to be tenacity. The ability of leadership or the maintainers to stick to a problem for years, to build relationships, to work through challenges fundamentally defined by human lifetimes.
Friction Is Good
The current generation of startup founders and programmers is obsessed with speed. Fast iteration, rapid deployment, doing everything as quickly as possible. For many things, that’s fine. You can go fast, leave some quality on the table, and learn something along the way.
But there are things where speed is actively harmful, where the friction exists for a reason. Compliance is one of those cases. There’s a strong desire to eliminate everything that processes like SOC2 require, and an entire industry of turnkey solutions has sprung up to help — Delve just being one example, there are more.
There’s a feeling that all the things that create friction in your life should be automated away. That human involvement should be replaced by AI-based decision-making. Because it is the friction of the process that is the problem. When in fact many times the friction, or that things just take time, is precisely the point.
There’s a reason we have cooling-off periods for some important decisions in one’s life. We recognize that people need time to think about what they’re doing, and that doing something right once doesn’t mean much because you need to be able to do it over a longer period of time.
Vibe Slop At Inference Speeds
AI writes code fast which isn’t news anymore. What’s interesting is that we’re pushing this force downstream: we seemingly have this desire to ship faster than ever, to run more experiments and that creates a new desire, one to remove all the remaining friction of reviews, designing and configuring infrastructure, anything that slows the pipeline. If the machines are so great, why do we even need checklists or permission systems? Express desire, enjoy result.
Because we now believe it is important for us to just do everything faster. But increasingly, I also feel like this means that the shelf life of much of the software being created today — software that people and businesses should depend on — can be measured only in months rather than decades, and the relationships alongside.
In one of last year’s earlier YC batches, there was already a handful that just disappeared without even saying what they learned or saying goodbye to their customers. They just shut down their public presence and moved on to other things. And to me, that is not a sign of healthy iteration. That is a sign of breaking the basic trust you need to build a relationship with customers. A proper shutdown takes time and effort, and our current environment treats that as time not wisely spent. Better to just move on to the next thing.
This is extending to Open Source projects as well. All of a sudden, everything is an Open Source project, but many of them only have commits for a week or so, and then they go away because the motivation of the creator already waned. And in the name of experimentation, that is all good and well, but what makes a good Open Source project is that you think and truly believe that the person that created it is either going to stick with it for a very long period of time, or they are able to set up a strategy for succession, or they have created enough of a community that these projects will stand the test of time in one form or another.
My Time
Relatedly, I’m also increasingly skeptical of anyone who sells me something that supposedly saves my time. When all that I see is that everybody who is like me, fully onboarded into AI and agentic tools, seemingly has less and less time available because we fall into a trap where we’re immediately filling it with more things.
We all sell each other the idea that we’re going to save time, but that is not what’s happening. Any time saved gets immediately captured by competition. Someone who actually takes a breath is outmaneuvered by someone who fills every freed-up hour with new output. There is no easy way to bank the time and it just disappears.
I feel this acutely. I’m very close to the red-hot center of where economic activity around AI is taking place, and more than anything, I have less and less time, even when I try to purposefully scale back and create the space. For me this is a problem. It’s a problem because even with the best intentions, I actually find it very hard to create quality when we are quickly commoditizing software, and the machines make it so appealing.
I keep coming back to the trees. I’ve been maintaining Open Source projects for close to two decades now. The last startup I worked on, I spent 10 years at. That’s not because I’m particularly disciplined or virtuous. It’s because I or someone else, planted something, and then I kept showing up, and eventually the thing had roots that went deeper than my enthusiasm on any given day. That’s what time does! It turns some idea or plan into a commitment and a commitment into something that can shelter and grow other people.
Nobody is going to mass-produce a 50-year-old oak. And nobody is going to conjure trust, or quality, or community out of a weekend sprint. The things I value most — the projects, the relationships, the communities — are all things that took years to become what they are. No tool, no matter how fast, was going to get them there sooner.
We recently planted a new tree with Colin. I want it to grow into a large one. I know that’s going to take time, and I’m not in a rush.
March 19, 2026
"Michael Kennedy's Thoughts on Technology"
Use Chameleon templates in the Robyn web framework
TL;DR; Chameleon-robyn is a new Python package I created that brings Chameleon template support to the Robyn web framework. If you prefer Chameleon’s structured, HTML-first approach over Jinja and want to try Robyn’s Rust-powered performance, this package bridges the two.
People who have known me for a while know that I’m very much not a fan of the Jinja templating language. Neither am I a fan of the Django templating language, since it’s very similar. I dislike the fact that you’re mostly programming with interlaced HTML rather than having mostly HTML that is very restricted in what it allows in terms of coding. While nowhere near perfect, I prefer Chameleon because it requires you to write well-structured code. Sadly, I think Jinja won exactly because it allows you to write whatever Python code in your HTML you want. For most frameworks, Jinja is the only templating language they support.
Why migrate Chameleon templates to a new framework?
I’d love to try out some new frameworks, but I have so much existing Chameleon code that any sort of migration will never include converting to Jinja, if I have a say in it. Not because of my dislike for it, but because it’s incredibly error prone, and it would mean changing my entire web design, not just my code.
Here’s the code breakdown for just Talk Python Training.
That design category is 14,650 lines of HTML and 11,104 lines of CSS! If I can get Chameleon running on a framework, it will 100% reuse every line of that to perfection. If I cannot, I’m rewriting them all. No thanks.
How does Robyn use Rust to speed up Python web apps?
I’ve been thinking a lot about what if our web frameworks actually ran in Rust? Right now I’m running Quart (async Flask) on top of Granian. So Rust is the base of my web server and processing. But there is a lot of infrastructure provided by Flask and Werkzueg leading up to my code actually running that is all based on Python.
Would it be a lot faster? Maybe. My exploring this idea was inspired by TurboAPI. TurboAPI did exactly this as I was thinking about, but with Zig and for FastAPI. While I am not recommending people leave FastAPI, their headline “FastAPI-compatible. Zig HTTP core. 22x faster,” does catch one’s attention.
Eventually I found my way to Robyn. Robyn merges Python’s async capabilities with a Rust runtime for reliable, scalable web solutions. Here are a few key highlights:
- Runtime: Rust-based request handling for high throughput
- API style: Flask-like Python API, making migration straightforward
- Async: Built-in async support out of the box
There’s this quite interesting performance graph from Robyn’s benchmarks. Of course, take it with all the caveats that benchmarks come with.

How to use Chameleon templates with Robyn
I want to try this framework on real projects that I’m running in production to see how they size up. However, given all of my web UI is written in Chameleon, there’s absolutely no way I’m converting to Jinja. I can hear everyone now, “So just use it for something simple and new, Michael.” For me that defeats the point. Thus, my obsession with getting Chameleon to work.
I created the integration for Chameleon for Flask with my chameleon-flask package. Could I do the same thing for Robyn?
It turns out that I can! Without further ado, introducing chameleon-robyn:
It’s super early days and I’m just starting to use this package for my prototype. I’m sure as I put it into production in a real app, I’ll see if it’s feature complete or not.
For now, it’s out there on GitHub and on PyPI. If Chameleon + Robyn sounds like an interesting combo to you as well, give this a try. PRs are welcome.
Talk Python to Me
#541: Monty - Python in Rust for AI
When LLMs write code to accomplish a task, that code has to actually run somewhere. And right now, the options aren't great. Spin up a sandboxed container and you're paying a full second of cold start overhead plus the complexity of another service. Let the LLM loose on your actual machine and... well, you'd better be watching. <br/> <br/> On this episode, I sit down with Samuel Colvin, creator of Pydantic, now at 10 billion downloads, to explore Monty, a Python interpreter written from scratch in Rust, purpose-built to run LLM-generated code. It starts in microseconds, is completely sandboxed by design, and can even serialize its entire state to a database and resume later. We dig into why this deliberately limited interpreter might be exactly what the AI agent era needs.<br/> <br/> <strong>Episode sponsors</strong><br/> <br/> <a href='https://talkpython.fm/training'>Talk Python Courses</a><br> <a href='https://talkpython.fm/devopsbook'>Python in Production</a><br/> <br/> <h2 class="links-heading mb-4">Links from the show</h2> <div><strong>Guest</strong><br/> <strong>Samuel Colvin</strong>: <a href="https://github.com/samuelcolvin?featured_on=talkpython" target="_blank" >github.com</a><br/> <br/> <strong>CPython</strong>: <a href="https://github.com/python/cpython?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>IronPython</strong>: <a href="https://ironpython.net?featured_on=talkpython" target="_blank" >ironpython.net</a><br/> <strong>Jython</strong>: <a href="https://www.jython.org?featured_on=talkpython" target="_blank" >www.jython.org</a><br/> <strong>Pyodide</strong>: <a href="https://pyodide.com?featured_on=talkpython" target="_blank" >pyodide.com</a><br/> <strong>monty</strong>: <a href="https://github.com/pydantic/monty?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Pydantic AI</strong>: <a href="https://pydantic.dev/pydantic-ai?featured_on=talkpython" target="_blank" >pydantic.dev</a><br/> <strong>Python AI conference</strong>: <a href="https://pyai.events?featured_on=talkpython" target="_blank" >pyai.events</a><br/> <strong>bashkit</strong>: <a href="https://github.com/everruns/bashkit?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>just-bash</strong>: <a href="https://github.com/vercel-labs/just-bash?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Narwhals</strong>: <a href="https://narwhals-dev.github.io/narwhals/?featured_on=talkpython" target="_blank" >narwhals-dev.github.io</a><br/> <strong>Polars</strong>: <a href="https://pola.rs?featured_on=talkpython" target="_blank" >pola.rs</a><br/> <strong>Strands Agents</strong>: <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/?featured_on=talkpython" target="_blank" >aws.amazon.com</a><br/> <strong>Subscribe Running Pydantic’s Monty Rust sandboxed Python subset in WebAssembly</strong>: <a href="https://simonwillison.net/2026/Feb/6/pydantic-monty/?featured_on=talkpython" target="_blank" >simonwillison.net</a><br/> <strong>Rust Python</strong>: <a href="https://github.com/RustPython/RustPython?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Valgrind</strong>: <a href="https://valgrind.org?featured_on=talkpython" target="_blank" >valgrind.org</a><br/> <strong>Cod Speed</strong>: <a href="https://codspeed.io?featured_on=talkpython" target="_blank" >codspeed.io</a><br/> <br/> <strong>Watch this episode on YouTube</strong>: <a href="https://www.youtube.com/watch?v=TjTV4jlMcRw" target="_blank" >youtube.com</a><br/> <strong>Episode #541 deep-dive</strong>: <a href="https://talkpython.fm/episodes/show/541/monty-python-in-rust-for-ai#takeaways-anchor" target="_blank" >talkpython.fm/541</a><br/> <strong>Episode transcripts</strong>: <a href="https://talkpython.fm/episodes/transcript/541/monty-python-in-rust-for-ai" 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/@talkpython</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@fosstodon.org</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@fosstodon.org</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>
Real Python
Quiz: How to Add Python to PATH
In this quiz, you’ll test your understanding of Add Python to PATH.
By working through this quiz, you’ll review what the PATH environment variable is, how the shell searches it in order.
You’ll also practice adding Python to PATH on Windows, Linux, and macOS, prepending directories with export, refreshing your session with source, and managing unwanted entries so the python command works as expected.
[ 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 ]
Tryton News
Release 0.1.0 of hatch-tryton
We are proud to announce the first release of the version 0.1.0 of hatch-tryton.
hatch-tryton is a hatchling plugin that manages Tryton dependencies.
We will rely on this tool to upgrade Tryton’s packages to pyproject.toml for future releases.
hatch-tryton is available on PyPI as hatch-tryton 0.1.0
1 post - 1 participant
Nicola Iarocci
Eve 2.3.0
Eve v2.3 was just released on PyPI. It adds optimize_pagination_for_speed, a resource-level setting that allows granular control, overriding the equivalent global option that goes by the same name. Many thanks to Emanuele Di Giacomo for contributing to the project.
"Michael Kennedy's Thoughts on Technology"
Fire and forget (or never) with Python’s asyncio

TL;DR; Python’s asyncio.create_task() can silently garbage collect your fire-and-forget tasks starting in Python 3.12 - they may never run. The fix: store task references in a set and register a done_callback to clean them up.
Do you use Python’s async/await in programming? Often you have some async task that needs to run, but you don’t care to monitor it, know when it’s done, or even if it errors.
Let’s imagine you have an async function that logs to a remote service. You want its execution out of the main-line execution. Maybe it looks like this:
async def log_account_created(username: str): ...
Someone new to Python’s odd version of async would think they could write code like this (hint, they cannot):
async def register_user():
data = get_form_data()
user = user_service.create_user(data)
# Log in the background (actually no, but we try)
log_account_created(user.name) # BUG!
return redirect('/account/welcome')
You cannot just run an async function, you fool! Why? I don’t know. It’s a massively needless complication of modern Python. You either have to await it (which would block the main execution foiling our fire and forget intention) or you have to start it as a task separately. Here’s the working version (at least in Python 3.11 it works, Python 3.12+? Sometimes):
async def register_user():
data = get_form_data()
user = user_service.create_user(data)
# Log in the background?
# Runs on the asyncio loop, fixed, maybe
asyncio.create_task(log_account_created(user.name))
return redirect('/account/welcome')
Why asyncio.create_task loses tasks in Python 3.12+
Actually that fixed version has a tremendously subtle race condition that was introduced in Python 3.12 (seriously). In Python 3.11 or before, the async loop holds the new task and will just run it at some point soon.
Here is the first line in the docs for create_task:
Wrap the coro coroutine into a
Taskand schedule its execution. Return the Task object.
It schedules its execution. But in Python 3.12, it might forget about it!
I call functions like log_account_created fire and forget async functions. You don’t care to wait for it or even check its outcome. What are you going to do if logging fails anyway? Log it more? But check this out, straight from Python 3.14’s documentation:
Important: Save a reference to the result of [ asyncio.create_task ], to avoid a task disappearing mid-execution. The event loop only keeps weak references to tasks. A task that isn’t referenced elsewhere may get garbage collected at any time, even before it’s done. For reliable “fire-and-forget” background tasks, gather them in a collection:
background_tasks = set()
for i in range(10):
task = asyncio.create_task(some_coro(param=i))
# Add task to the set. This creates a strong reference.
background_tasks.add(task)
# To prevent keeping references to finished tasks forever,
# make each task remove its own reference from the set after
# completion:
task.add_done_callback(background_tasks.discard)
Wait, what? If I start a Python async task it might be GC’ed before it even starts? Wow, just wow.
The fix? Well, you just create a set to track them (keep a strong reference in GC-parlance). In our example, it looks like this:
background_tasks = set()
async def register_user():
data = get_form_data()
user = user_service.create_user(data)
# Log in the background
task = asyncio.create_task(log_account_created(user.name))
background_tasks.add(task) # prevent GC
task.add_done_callback(background_tasks.discard) # cleanup when done
return redirect('/account/welcome')
One obvious way to do it
This set hack is entirely non-obvious that this is required. After all, the Zen of Python states:
There should be one – and preferably only one – obvious way to do it.
But Zen doesn’t always apply, does it? I’m sure there is a reason for this change, though I’m not sure it was worth it.
If it were up to me, Python would come with one omnipresent event loop running on a background thread. Just calling an async function would schedule and run it there. The await keyword would be a control flow only construct, not the thing that actually does the execution.
But it doesn’t work that way and so we get oddities here and there I guess.
Seth Michael Larson
Getting started with the GameSir “Pocket Taco” with iPhone and Delta emulator
GameSir shipped the pre-orders for the “Pocket Taco” mobile controller on March 15th and I received mine today. This controller uses Bluetooth and a padded grip mechanism to add physical buttons to the bottom half of your mobile phone for use with mobile emulators like Delta emulator.
The guide, tutorials, FAQs, available from GameSir leave a bit to be desired. Therefore, I'm documenting what I had to do to get the controller working with my iPhone 13 Pro (iOS 26) and using the Delta emulator.
Pairing via Bluetooth
Download the GameSir app from the App Store and open the application. If you allow the app access to Bluetooth, the app can automatically pair your device when you turn it on.
Here was my first stumbling block: the included and online guides reference multiple tutorials for entering pairing mode, depending on your device. For iPhone, you want the "AP Connection Tutorial" (presumably "AP" means "Apple"). So to pair for an iPhone hold the "Home" button and the "A" button (right button in "ABYX") on the Pocket Taco for three seconds. This will enter a pairing mode where the bottom 3 of 4 LEDs on the right side of the device will flash.
On your iPhone, you should see the device in the Bluetooth pairing screen as "DUALSHOCK 4 Wireless Controller". If you see 'GameSir-Pocket' or something like this, it may not be in the right mode (?)
Upgrading Pocket Taco firmware
Once your device is paired, you can do "over-the-air" (OTA) firmware updates to the controller. When I received my controller there was a newer firmware available. There is a button to do this in the GameSir app, so might as well get the latest version.
Using the Pocket Taco controller with Delta
Download and open the Delta emulator app and open settings (gear in the top left). With the Pocket Taco paired open the "Controllers" menu for "Player 1".
Note that the Pocket Taco turns off after a few seconds after closing the mechanism holding the controller to your phone. To navigate all the menus you'll want to either gently hold the Pocket Taco open with your other hand or place it on the very edge of your phone to not overlap too much of the screen.
The Player 1 controller may be set to Keyboard. Set this to the "DUALSHOCK 4 Wireless Controller". Select "Customize Control..." below this menu. By default, the controller might have the "A" and "B" buttons switched, so select "A" in the menu and then press "A" on the Pocket Taco controller to map the button correctly. Do the same for "B" and other buttons.
Controller Skins
Now if you launch a game you might notice that the screen is cut-off, as the on-screen controller is no longer being displayed. Go back into "Settings" and scroll down to the emulator you want to use (Nintendo, Sega Genesis, Super Nintendo, etc). In the Controller Skins menu for the emulator you want to use select the button in the top-right showing a crossed out controller icon and select "Game Controller". This means you're configuring the Controller Skin for when there is a game controller connected.
By default, this will be set to "No Controller Skin". Select "No Controller Skin" under "Portrait" and then select the default Delta emulator skin for that emulator. You'll need to do this for every emulator you plan to use with Delta, as each has their own skin settings.
After this, you should be good to go with the Delta emulator and the Pocket Taco. Happy gaming! 🌮
Thanks for keeping RSS alive! ♥
March 18, 2026
Real Python
Build Your Weekly Python Study Schedule: 7 Days to Consistent Progress
Staying consistent with learning Python can be challenging. This guide helps you create a weekly Python study schedule that you can stick to and shows you how it works in practice.
By the end of this guide, you’ll have a practical, personal schedule that fits your life, not the other way around. You’ll walk away with a repeatable 7-day plan and a worksheet you can reuse each week. You’ll know exactly what to study, when to study, and how to maintain momentum, even when life gets busy.
In this guide, you’ll learn how to move from vague intentions to a concrete system using three steps:
- Clarify Your Goal: Define exactly what progress looks like for the next seven days.
- Design Your Plan: Build a specific 7-day schedule that accounts for real life.
- Make It Stick: Use behavioral psychology to turn that schedule into a habit.
By following these steps, you’ll transform your learning process from random bursts of energy into a sustainable routine. Before you dive in, take a moment to make sure you have everything you need to get started.
Prerequisites
This guide is for beginners and early intermediate learners who feel stuck, inconsistent, or overwhelmed by scattered tutorials. You don’t need advanced technical knowledge to benefit from this system.
To get the most out of this process, you’ll need:
- Time: A willingness to invest 30 to 45 minutes per day for one week.
- Tools: A calendar app (Google Calendar, Outlook, Notion) or a physical notebook.
- Ideas: A rough list of Python topics you want to learn or a manageable project idea. If you need inspiration, check out What Can I Do With Python?
- Materials: The Weekly Python Study Schedule Worksheet, which is available as a downloadable PDF:
Get Your PDF: Click here to download the free Weekly Python Study Schedule Worksheet you can use to build a consistent study schedule.
Take the Quiz: Test your knowledge with our interactive “Build Your Weekly Python Study Schedule: 7 Days to Consistent Progress” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
Build Your Weekly Python Study Schedule: 7 Days to Consistent ProgressBuild a consistent Python study habit with a repeatable 7-day plan. Learn to set specific goals, schedule your week, and make practice stick.
Once you have these ready, the first step is to clarify exactly what you’re aiming for this week.
Step 1: Define What Progress Means for You This Week
People often fall off their learning goals because those goals are too abstract. Statements like “I want to learn Python” are broad aspirations rather than specific tasks. They describe intentions, but they don’t give you a concrete action to take.
Similarly, “I want to get better at Python” sounds nice, but it doesn’t tell you what to do on Monday at 7 p.m. When you sit down at your computer with a goal that vague, you can spend the first twenty minutes just deciding what to do.
To fix this, you need to shrink your horizon and focus on a smaller timeframe. Instead of trying to plan your entire coding journey at once, focus exclusively on the next seven days. This approach reduces the pressure to know everything right now and lets you focus on immediate, achievable tasks.
Understand Why Specificity Matters
Research on goal setting, specifically the work by Locke and Latham, highlights the relationship between clarity and effort. Their studies show that specific, challenging goals lead to higher performance than easy goals or vague instructions to “do your best.”
Specific goals focus attention, increase effort and persistence, and motivate the development of strategies to accomplish the goal.
— Locke and Latham, A Theory of Goal Setting & Task Performance (Source)
If you define a specific target, your brain switches from “What should I do?” to “How do I do it?” If you can’t describe what progress looks like in seven days, your schedule doesn’t have anything to aim toward.
Abstract goals require motivation to start. Specific goals require only clarity. When you know the target, practice becomes more approachable and sustainable.
You can see the difference in clarity in the table below:
Read the full article at https://realpython.com/weekly-study-schedule/ »
[ 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: Exploring Basic Data Types in Python
In this quiz, you’ll test your understanding of Python Data Types.
By working through this quiz, you’ll revisit basic numeric, string, and Boolean types, how Python represents these objects, and common built-in functions.
This quiz helps beginners solidify a foundation for working with Python types and using built-in functions in real programs. Work through the questions to check your skills and identify topics to review.
[ 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 ]
Seth Michael Larson
Python library “Requests” needs you to test type hints
Requests is a popular HTTP client library available on the Python Package Index (PyPI). Sitting in the top 10 packages by downloads on PyPI, this library is used by many, many projects. This library is known for its user-friendly and ergonomic API for HTTP requests and responses. However, the API implementation can sometimes confuse static analysis tools like IDEs or type checkers, causing issues for users.
Requests maintainer Nate Prewitt is planning to add support for type hints to Requests in the next three months. Right now the feature is available as a development branch but will later be published to PyPI as a pre-release version. The goal is to find and fix issues before rolling the change out to users to avoid unnecessary breakage.
What can you do to help?
Shipping large changes to codebases this popular is difficult without breaking users. This is where you come in: Requests needs your help! If your project or application uses Requests as a dependency, try installing and running the development branch with type hints. This is especially useful if your project uses type hints and a type checker like Mypy, Pyright, or Ty.
Here's how you can install the development branch. These instructions are available in the pull request, too:
python -m pip install git+https://github.com/psf/requests.git@inline_types_rfcuv pip install git+https://github.com/psf/requests.git@inline_types_rfc
After installation:
- Run your existing test suite
- Run Pyright or Mypy against your code that uses Requests
- Try your usual Requests patterns and see if anything doesn't typecheck
If you encounter issues after running your test suite or type checker with the development branch: please report them on GitHub. Be sure to check the list of known issues and search for a duplicate of your issue before opening a new one. When reporting an issue, be as descriptive as possible:
- What Python version? (
python --version) - What dependencies are you using? (
python -m pip freeze) - What type checker are you using, what settings? (Mypy, Pyright, Ty)
This will help Requests maintainers address your issue quickly ahead of shipping the new type hints. Thanks much for your contributions towards shipping this feature smoothly. 🙏
Thanks for keeping RSS alive! ♥
