skip to navigation
skip to content

Planet Python

Last update: March 22, 2026 04:44 PM UTC

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:

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:

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.

March 22, 2026 03:10 PM UTC


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.

altNiklas Mertsch, member of the Operations team at EuroPython 2025

EP: 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!

March 22, 2026 01:53 PM UTC


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:

python-sql is available on PyPI: python-sql 1.7.0.

1 post - 1 participant

Read full topic

March 22, 2026 09:18 AM UTC


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 <[email protected]>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/14/pypy-migrates-to-mercurial/), 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.

March 22, 2026 12:22 AM UTC

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 ]

March 20, 2026 12:00 PM UTC

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 ]

March 20, 2026 12:00 PM UTC


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 20, 2026 12:00 AM UTC

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:

There’s this quite interesting performance graph from Robyn’s benchmarks. Of course, take it with all the caveats that benchmarks come with.

Benchmark comparing Robyn, FastAPI, Flask, and Django on request throughput

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.

March 19, 2026 11:18 PM UTC


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="proxy.php?url=https%3A%2F%2Fgithub.com%2Fsamuelcolvin%3Ffeatured_on%3Dtalkpython" target="_blank" >github.com</a><br/> <br/> <strong>CPython</strong>: <a href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fpython%2Fcpython%3Ffeatured_on%3Dtalkpython" target="_blank" >github.com</a><br/> <strong>IronPython</strong>: <a href="proxy.php?url=https%3A%2F%2Fironpython.net%3Ffeatured_on%3Dtalkpython" target="_blank" >ironpython.net</a><br/> <strong>Jython</strong>: <a href="proxy.php?url=https%3A%2F%2Fwww.jython.org%3Ffeatured_on%3Dtalkpython" target="_blank" >www.jython.org</a><br/> <strong>Pyodide</strong>: <a href="proxy.php?url=https%3A%2F%2Fpyodide.com%3Ffeatured_on%3Dtalkpython" target="_blank" >pyodide.com</a><br/> <strong>monty</strong>: <a href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fpydantic%2Fmonty%3Ffeatured_on%3Dtalkpython" target="_blank" >github.com</a><br/> <strong>Pydantic AI</strong>: <a href="proxy.php?url=https%3A%2F%2Fpydantic.dev%2Fpydantic-ai%3Ffeatured_on%3Dtalkpython" target="_blank" >pydantic.dev</a><br/> <strong>Python AI conference</strong>: <a href="proxy.php?url=https%3A%2F%2Fpyai.events%3Ffeatured_on%3Dtalkpython" target="_blank" >pyai.events</a><br/> <strong>bashkit</strong>: <a href="proxy.php?url=https%3A%2F%2Fgithub.com%2Feverruns%2Fbashkit%3Ffeatured_on%3Dtalkpython" target="_blank" >github.com</a><br/> <strong>just-bash</strong>: <a href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fjust-bash%3Ffeatured_on%3Dtalkpython" target="_blank" >github.com</a><br/> <strong>Narwhals</strong>: <a href="proxy.php?url=https%3A%2F%2Fnarwhals-dev.github.io%2Fnarwhals%2F%3Ffeatured_on%3Dtalkpython" target="_blank" >narwhals-dev.github.io</a><br/> <strong>Polars</strong>: <a href="proxy.php?url=https%3A%2F%2Fpola.rs%3Ffeatured_on%3Dtalkpython" target="_blank" >pola.rs</a><br/> <strong>Strands Agents</strong>: <a href="proxy.php?url=https%3A%2F%2Faws.amazon.com%2Fblogs%2Fopensource%2Fintroducing-strands-agents-an-open-source-ai-agents-sdk%2F%3Ffeatured_on%3Dtalkpython" target="_blank" >aws.amazon.com</a><br/> <strong>Subscribe Running Pydantic’s Monty Rust sandboxed Python subset in WebAssembly</strong>: <a href="proxy.php?url=https%3A%2F%2Fsimonwillison.net%2F2026%2FFeb%2F6%2Fpydantic-monty%2F%3Ffeatured_on%3Dtalkpython" target="_blank" >simonwillison.net</a><br/> <strong>Rust Python</strong>: <a href="proxy.php?url=https%3A%2F%2Fgithub.com%2FRustPython%2FRustPython%3Ffeatured_on%3Dtalkpython" target="_blank" >github.com</a><br/> <strong>Valgrind</strong>: <a href="proxy.php?url=https%3A%2F%2Fvalgrind.org%3Ffeatured_on%3Dtalkpython" target="_blank" >valgrind.org</a><br/> <strong>Cod Speed</strong>: <a href="proxy.php?url=https%3A%2F%2Fcodspeed.io%3Ffeatured_on%3Dtalkpython" target="_blank" >codspeed.io</a><br/> <br/> <strong>Watch this episode on YouTube</strong>: <a href="proxy.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DTjTV4jlMcRw" target="_blank" >youtube.com</a><br/> <strong>Episode #541 deep-dive</strong>: <a href="proxy.php?url=https%3A%2F%2Ftalkpython.fm%2Fepisodes%2Fshow%2F541%2Fmonty-python-in-rust-for-ai%23takeaways-anchor" target="_blank" >talkpython.fm/541</a><br/> <strong>Episode transcripts</strong>: <a href="proxy.php?url=https%3A%2F%2Ftalkpython.fm%2Fepisodes%2Ftranscript%2F541%2Fmonty-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="proxy.php?url=https%3A%2F%2Ftalkpython.fm%2Fflasksong" target="_blank" >talkpython.fm/flasksong</a><br/> <br/> <strong>---== Don't be a stranger ==---</strong><br/> <strong>YouTube</strong>: <a href="proxy.php?url=https%3A%2F%2Ftalkpython.fm%2Fyoutube" target="_blank" ><i class="fa-brands fa-youtube"></i> youtube.com/@talkpython</a><br/> <br/> <strong>Bluesky</strong>: <a href="proxy.php?url=https%3A%2F%2Fbsky.app%2Fprofile%2Ftalkpython.fm" target="_blank" >@talkpython.fm</a><br/> <strong>Mastodon</strong>: <a href="proxy.php?url=https%3A%2F%2Ffosstodon.org%2Fweb%2F%40talkpython" target="_blank" ><i class="fa-brands fa-mastodon"></i> @[email protected]</a><br/> <strong>X.com</strong>: <a href="proxy.php?url=https%3A%2F%2Fx.com%2Ftalkpython" target="_blank" ><i class="fa-brands fa-twitter"></i> @talkpython</a><br/> <br/> <strong>Michael on Bluesky</strong>: <a href="proxy.php?url=https%3A%2F%2Fbsky.app%2Fprofile%2Fmkennedy.codes%3Ffeatured_on%3Dtalkpython" target="_blank" >@mkennedy.codes</a><br/> <strong>Michael on Mastodon</strong>: <a href="proxy.php?url=https%3A%2F%2Ffosstodon.org%2Fweb%2F%40mkennedy" target="_blank" ><i class="fa-brands fa-mastodon"></i> @[email protected]</a><br/> <strong>Michael on X.com</strong>: <a href="proxy.php?url=https%3A%2F%2Fx.com%2Fmkennedy%3Ffeatured_on%3Dtalkpython" target="_blank" ><i class="fa-brands fa-twitter"></i> @mkennedy</a><br/></div>

March 19, 2026 07:38 PM UTC


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 ]

March 19, 2026 12:00 PM UTC


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

Read full topic

March 19, 2026 10:18 AM UTC


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.

March 19, 2026 09:38 AM UTC


"Michael Kennedy's Thoughts on Technology"

Fire and forget (or never) with Python’s asyncio

Python asyncio fire and forget task pattern

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 Task and 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.

March 19, 2026 03:22 AM UTC


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 19, 2026 12:00 AM UTC

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:

  1. Clarify Your Goal: Define exactly what progress looks like for the next seven days.
  2. Design Your Plan: Build a specific 7-day schedule that accounts for real life.
  3. 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 Progress

Build 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 ]

March 18, 2026 02:00 PM UTC

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 ]

March 18, 2026 12:00 PM UTC


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:

After installation:

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:

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! ♥

March 18, 2026 12:00 AM UTC

March 17, 2026


PyCoder’s Weekly

Issue #726: Lazy Imports, Pydantic AI, Classes, and More (March 17, 2026)

#726 – MARCH 17, 2026
View in Browser »

The PyCoder’s Weekly Logo


The Story of Python’s Lazy Imports

This article is about why lazy imports took 3 years and 2 attempts to be added to the language. From PEP 690’s rejection to PEP 810’s unanimous acceptance.
TECHLIFE

Pydantic AI: Build Type-Safe LLM Agents in Python

Learn how to use Pydantic AI to build type-safe LLM agents in Python with structured outputs, function calling, and dependency injection patterns.
REAL PYTHON

Agents as API Services, Not Prompt Chains.

alt

Clone a working 100-agent Python system: Autonomous Engineering Team, Deep Security Auditor, or Adversarial Code Reviewer. Agents run as API services, discover each other at runtime. No DAGs, no glue code. Any model. Apache 2.0. Clone a Recipe →
AGENTFIELD.AI sponsor

When Are Classes Used in Python?

While you don’t often need to make your own classes in Python, they can sometimes make your code reusable and easier to read.
TREY HUNNER

Learn the Agentic Coding Workflow That Actually Works on Real Projects

65% of Python developers are stuck using AI for small tasks that fall apart on anything real. This 2-day live course (March 21-22 via Zoom) walks you through building a complete Python CLI app with Claude Code, from an empty directory to a shipped project on GitHub.
REAL PYTHON

DuckDB 1.5.0 Released

DUCKDB.ORG

PyPy v7.3.21 Released

PYPY.ORG

Python 3.15.0 Alpha 7

PYTHON.ORG

Python Jobs

Python + AI Content Specialist (Anywhere)

Real Python

More Python Jobs >>>

Articles & Tutorials

Crafting and Editing in-Depth Tutorials at Real Python

What goes into creating the tutorials you read at Real Python? What are the steps in the editorial process, and who are the people behind the scenes? This week on the show, Real Python team members Martin Breuss, Brenda Weleschuk, and Philipp Acsany join us to discuss topic curation, review stages, and quality assurance.
REAL PYTHON podcast

The Optimization Ladder

Python loses every public benchmark by 21-875x. Cemrehan took the exact problems people use to dunk on Python and climbed every rung of the optimization ladder: from CPython version upgrades to Rust. Real numbers, real code, real effort costs.
CEMREHAN ÇAVDAR

Ship Voice Agents That Sound Like Human with Async

alt

Async Voice API is a human-like low-latency text-to-speech API for real-time apps and agents. 15 languages, streaming-ready, integrations with n8n, LiveKit, Twilio. Top-ranked on the Hugging Face TTS Arena. From$0.50/hour with a 24/7 SLA. Try Now →
ASYNC sponsor

Lock the Ghost in uv.lock

In the software world, “remove” is not equal to “gone.” Take a short trip through how the Python Package Index handles removals and how you can lock a ghost package in an uv.lock file forever!
CERT.AT • Shared by Kamil Mańkowski

Comparing PDF Table Extraction Tools

This article explores three Python tools for PDF table extraction: Docling, Marker, and LlamaParse. Learn which handles merged cells and multi-level headers best.
CODECUT.AI • Shared by Khuyen Tran

What Is Code Review For?

This post explores just what you should and should not use code reviews for. Learn when to use linters to catch problems vs when human review is important.
GLYPH

Caching an Asyncio Function the Easy Way

Caching an async function is trickier than expected, this article walks through why that is and how to use Asyncio primitives to solve the problem.
CHANGS.CO.UK • Shared by Jamie Chang

Working With APIs in Python: Reading Public Data

Learn how to consume REST APIs with Python using the requests library, including authentication, query parameters, and handling responses.
REAL PYTHON course

Nobody Gets Promoted for Simplicity

Our industry often rewards complexity and ignores simplicity. In interviews, design reviews, and promotions. Here’s how to fix it.
TERRIBLE SOFTWARE

You Store Data and You Do Stuff With Data

This post explores the Object Oriented Programming mindset and why you want to associate your data with the operations upon it.
STEPHEN GRUPPETTA

pandas’ Public API Is Now Type-Complete

Marco tells the story of how his team helped make pandas’ public API type-complete, and how to prevent it from regressing.
MARCO GORELLI

Remove Extra Spaces

Learn how to remove extra spaces from a string using regex, string splitting, a fixed point, and itertools.groupby.
RODRIGO GIRÃO SERRÃO

CPython: 36 Years of Source Code

A graphical analysis of the growth of CPython’s codebase from its first commits to the present day
PYTHON.ORG

Projects & Code

100 Days, 100 MicroPython IoT Projects

GITHUB.COM/KRITISHMOHAPATRA • Shared by Kritish Mohapatra

OpenDocs: Turn Your README Into Documentation

GITHUB.COM/IOTEVERYTHIN

Cycast: Internet Radio Streaming Server

GITHUB.COM/LUKEB42

Crime-Related Datasets for Python

PYPI.ORG • Shared by Renzo Caceres Rossi

django-tasks-db: An ORM-based Backend for Django Tasks

GITHUB.COM/REALORANGEONE

Events

Weekly Real Python Office Hours Q&A (Virtual)

March 18, 2026
REALPYTHON.COM

PyData Bristol Meetup

March 19, 2026
MEETUP.COM

PyLadies Dublin

March 19, 2026
PYLADIES.COM

Chattanooga Python User Group

March 20 to March 21, 2026
MEETUP.COM

Claude Code for Python Developers: Hands-On Agentic Coding Course

March 21 to March 23, 2026
REAL PYTHON

PyCascades 2026

March 21 to March 23, 2026
PYCASCADES.COM

PythonAsia 2026

March 21 to March 24, 2026
PYTHONASIA.ORG


Happy Pythoning!
This was PyCoder’s Weekly Issue #726.
View in Browser »

alt

[ 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 ]

March 17, 2026 07:30 PM UTC


Real Python

Downloading Files From URLs With Python

Python makes it straightforward to download files from a URL with its robust set of libraries. For quick tasks, you can use the built-in urllib module or the requests library to fetch and save files. When working with large files, streaming data in chunks can help save memory and improve performance.

You can also perform parallel file downloads using ThreadPoolExecutor for multithreading or the aiohttp library for asynchronous tasks. These approaches allow you to handle multiple downloads concurrently, significantly reducing the total download time if you’re handling many files.

By the end of this video course, you’ll understand that:

In this video course, you’ll be downloading a range of economic data from the World Bank Open Data platform.


[ 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 ]

March 17, 2026 02:00 PM UTC

Quiz: Linked Lists in Python: An Introduction

In this quiz, you’ll test your understanding of Linked Lists in Python.

By working through this quiz, you’ll revisit what linked lists are, when to use collections.deque, how to implement your own linked lists, and the other linked list types and use cases.


[ 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 ]

March 17, 2026 12:00 PM UTC

March 16, 2026


Mike Driscoll

Textual – Creating a Custom Checkbox

Textual is a great Python user interface package. Textual lets you create a GUI-like interface in your terminal.

You can use many different widgets in Textual. However, the widget you will be focusing on in this tutorial is the humble checkbox.

Checkboxes are used for Boolean choices. They return a True if checked and a False if unchecked. You can use a checkbox as a clear visual cue to indicate whether one or more options are enabled or disabled.

In this tutorial, you will learn the following:

Let’s get started!

How to Create a Standard Checkbox

You should always start with the default widget to see if it works for you. With that in mind, open up your favorite Python IDE and create a new file with the following code in it:

from textual.app import App, ComposeResult
from textual.containers import VerticalScroll
from textual.widgets import Checkbox


class CheckboxApp(App[None]):

    def compose(self) -> ComposeResult:
        with VerticalScroll():
            yield Checkbox("Mike")

    def on_checkbox_changed(self, event: Checkbox.Changed) -> None:
        self.notify(f"Checkbox value: {event.value}")


if __name__ == "__main__":
    CheckboxApp().run()

The example above is based on a code example from the Textual documentation. You don’t have to use a VerticalScroll container here, but it is convenient when adding a series of widgets to the container.

Your main focus should be on the Checkbox itself. Here, you simply yield it from compose(), and then you catch the Checkbox.Changed event. When the event is fired, you display a notification to the user indicating that they have changed the checkbox value.

When you run this code, you will see something like this:

Textual checkbox

However, if you want to make the widget’s borders always visible, then you’ll need to add a border to the widget.

But what if you want the checkbox to be completely empty, too, rather than a greyed-out “X”? That is what you will learn how to do in the next section!

How to Customize the Checkbox Widget

Having a greyed out “X” isn’t necessarily bad thing if you have set up your Textual application to have a lighter background. But if you are using a default or dark background, then you will have some contrast issues.

Many GUI toolkits will show a checkbox as empty for its False state, though, while Textual defaults to a greyed-out “X.” If you want to change the character that is shown or make it an empty box like other toolkits, then you will need to learn how to customize the widget itself.

Fortunately, making a custom widget in Textual is very straightforward. Create a new Python file in your IDE and enter the following code:

from textual.app import App, ComposeResult
from textual.containers import VerticalScroll
from textual.widgets import Checkbox

class CustomCheck(Checkbox):
    BUTTON_INNER = " "

    def toggle(self) -> None:
        if self.value:
            CustomCheck.BUTTON_INNER = " "
        else:
            CustomCheck.BUTTON_INNER = "X"
        self.value = not self.value
        return self


class CheckboxApp(App[None]):
    #CSS_PATH = "checkbox.tcss"

    def compose(self) -> ComposeResult:
        check = CustomCheck("Mike")

        with VerticalScroll():
            yield check

    def on_checkbox_changed(self, event: Checkbox.Changed) -> None:
        self.notify(f"Checkbox value: {event.value}")


if __name__ == "__main__":
    CheckboxApp().run()

When you want to customize a pre-existing widget in Textual, you will almost always subclass it. So here, you subclass the Checkbox class to create a new CustomCheck class. Next, you override the BUTTON_INNER class attribute as well as the toggle() method.

If you go looking at the source code, you will find that BUTTON_INNER defaults to the “X” so here you are defaulting it to an empty string. Then you update toggle() to swap the “X” in when it is checked, and swap it back out when it is unchecked.

The other change is to use the new widget class in your application code.

To increase the widget’s visibility, you can add a border using Textual CSS. Create a new file called checkbox.tcss and add the following code to it:

CustomCheck {
    border: round green;
}

Make sure you save this file in the same folder as your Python file.

Now, when you run this code, you will see the following:

Custom Textual checkbox

Good job! You now have a custom checkbox.

Wrapping Up

Textual is a great way for you to create beautiful user interfaces in your terminal. In this tutorial, you learned how to create a regular checkbox widget and then learned how to create a custom checkbox.

You can apply this knowledge to customize other widgets in Textual. You will need to study the widget’s code closely to understand how it works so you can modify it successfully. You may have to go through several iterations to get exactly what you want.

Don’t give up! You’ll get there eventually, and then you will be proud of yourself for sticking to it!

Learn More

If you thought this article was interesting you and you want to learn more about Textual, check out the following links:

The post Textual – Creating a Custom Checkbox appeared first on Mouse Vs Python.

March 16, 2026 07:42 PM UTC


Ari Lamstein

acs-nativity: A Python Package for Analyzing Changes in the Foreign-Born Population

President Trump has made reducing illegal immigration and increasing deportations central goals of his second administration (1, 2). This is causing many people to ask: how are these policies changing the country’s population?

To help answer that, I built a new open-source Python package called acs-nativity. It provides a simple interface for accessing and visualizing data on the size of the native-born and foreign-born populations. The data comes from American Community Survey (ACS) 1-year estimates and is available from 2005 onward. The package supports multiple geographies: nationwide, all states, all metropolitan statistical areas (MSAs), and all counties and places (i.e., towns or cities) with populations of 65,000 or more.

Because it pulls data directly from the Census API, the package will automatically work with the 2025 ACS estimates as soon as they’re released in July. That release will cover the first year of Trump’s second administration, making it possible to compare recent changes against nearly two decades of historical data.

Installation

You can install acs-nativity with pip:

pip install acs-nativity

Example Workflow

The example below walks you through getting historic nativity data for the entire country, graphing it as a time series, and graphing the year-over-year changes.

Getting Data

The code below retrieves nativity data for the entire country for all available ACS 1‑year estimates (2005–2024):

from acs_nativity import (
    get_nativity_timeseries,
    plot_nativity_timeseries,
    plot_nativity_change,
)

df = get_nativity_timeseries(us="*")
df.head(1)

The parameter us="*" tells get_nativity_timeseries() to return data for the entire country. The output of that code is:

Name          Year Total     Native    Foreign-born Percent Foreign-born
United States 2005 288378137 252688295 35689842     12.376057

The output returns the same columns for every geography: Name, Year, Total, Native, Foreign-born, and Percent Foreign-born.

Graphing Time Series

To plot the dataframe as a time series, call plot_nativity_timeseries() and specify the column you want to chart. Most chart details (e.g., title and axis labels) are handled automatically, and annotations show when presidential administrations changed.

fig = plot_nativity_timeseries(df, column="Foreign-born")
fig.show()

Note: In Jupyter notebooks, you can simply call plot_nativity_timeseries() as the last line of a cell and the figure will render automatically. In a Python REPL or script, assign the figure to a variable and call .show().

The code above generates the following graph:

This graph shows that the foreign-born population has increased steadily since 2005, with a particularly large increase during the Biden administration.

Graphing Year-over-Year Change

Sometimes it’s helpful to show year-over-year changes instead of raw values. To do that, call plot_nativity_change() with a dataframe and a column:

fig = plot_nativity_change(df, column="Foreign-born")
fig.show()

Note: In Jupyter notebooks, you can simply call plot_nativity_change() as the last line of a cell and the figure will render automatically. In a Python REPL or script, assign the figure to a variable and call .show().

The code above generates this graph:

This chart makes it clear that the only year when the foreign-born population decreased was 2008.

Choosing a Geography

get_nativity_timeseries() can provide data for several geographies covered by the ACS 1-year estimates. This includes the nation, all states, the District of Columbia, all metropolitan statistical areas, and all counties and places (i.e., towns or cities) with populations of 65,000 or more.

To specify a geography, supply a keyword argument where:

Installing acs-nativity automatically installs censusdis, so these constants are available once the package is installed.

Below are examples for several common geographies:

GeographyKeywordModule for ValueExample
United Statesus-df = get_nativity_timeseries(us="*")
Statesstatecensusdis.statesfrom censusdis.states import MN
df = get_nativity_timeseries(state=MN)
Countycountycensusdis.counties.from censusdis.states import NY
from censusdis.counties.new_york import NASSAU
df = get_nativity_timeseries(state=NY, county=NASSAU)
Cityplacecensusdis.places.from censusdis.states import IL
from censusdis.places.illinois import CHICAGO_CITY
df = get_nativity_timeseries(state=IL, place=CHICAGO_CITY)
Metropolitan Statistical Area (MSA)(See note below)censusdis.msa_msafrom censusdis.msa_msa import EL_PASO_TX_METRO_AREA
df = get_nativity_timeseries(
metropolitan_statistical_area_micropolitan_statistical_area=
EL_PASO_TX_METRO_AREA
)

Note: The keyword for MSAs is metropolitan_statistical_area_micropolitan_statistical_area, which is too long to display cleanly inside the table.

Getting the Latest Data

By default, get_nativity_timeseries() returns data for 2005–2024. These years were chosen because:

The last year of data is controlled by the end_year parameter, which defaults to 2024.

The Census Bureau is expected to release the 2025 ACS 1‑year estimates in July 2026. When that happens, you can retrieve the new data by setting end_year=2025. For example:

df = get_nativity_timeseries(end_year=2025, us="*")

After the new data is published, the above code will get nativity data for the entire US from 2005-2025.

Related Work

If you’re interested in exploring similar projects I’ve worked on, here are three that connect closely to the ideas behind acs-nativity and show how this package grew out of earlier work.

What’s Next

If you try out the package, I’d love to hear what you build with it. Whether you’re analyzing national trends or exploring changes in your own community, I’m always interested in seeing how people use public data in creative ways. If you’d like to share feedback or results, you can reach me through the contact form on this site.

March 16, 2026 04:00 PM UTC


PyCon

Attend PyCon US for a day of Trailblazing Python Security!

PyCon US is coming to Long Beach, California in 2026. This year for the first time ever there is a day-long talk track devoted to the latest in Python security. This new talk track is titled “Trailblazing Python Security”, and we have 11 Python security experts covering all angles of security from Actions to Zero-Trust including Software Bill-of-Materials, phishing, FedRAMP, Rust, and more.

Our goal with the Trailblazing Python Security track is to create a space where developers, security practitioners, and open source contributors can learn from one another and discuss the problems and solutions for securing Python software and infrastructure.

Whether you're developing web applications, infrastructure, security, working with data, or just getting started with Python: there's something for everyone to learn about how to securely build with Python.

We hope to see you at PyCon US 2026 in Long Beach to learn from experts and shape the future of Python security landscape.

If your organization wants to support security in the Python ecosystem, we are accepting sponsors for the inaugural security talk track at PyCon US. Send an email to [email protected] for more information.

Schedule at a Distance 

The Trailblazing Python Security talk track will occur on Saturday, May 16th during the PyCon US main conference in Room 103ABC. The talk track runs the full length of the main talk schedule for the day, starting after the morning keynote at 10:30AM and running until 5:30PM. Below is the schedule at a distance including topics and speakers:

td { padding: 0.5em}

Time Talk Speaker(s)
10:30AM FastAPI Security Patterns: OAuth 2.0, JWTs, and API Keys Done Right Ian Mwangi
11:15AM Anatomy of a Phishing Campaign Mike Fiedler
12:00PM Zero Trust in 200ms: Implementing Identity-Per-Transaction with Python and Serverless Tristan McKinnon
1:45PM Rust for CPython: Making Python Safer and More Robust for Everyone Emma Smith
2:30PM Asleep at the Wheel: Getting your SBOMs to pay attention to Python Builds Sanchit Sahay, Abhishek Reddypalle
3:15PM Post-Incident Runtime SBOM Generation from Python Memory Hala Ali, Andrew Case
4:15PM GitHub Actions Security in Python Packages Andrew Nesbitt
5:00PM Breaking Bad (Packages): Why Traditional Vulnerability Tracking Fails Supply Chain Attacks Shelby Cunningham, Madison Ficorilli

The complete PyCon US 2026 schedule is available online or on the PyCon US mobile app. You can use the mobile app build your schedule by selecting which talks you are interested in attending. Or better yet, come to Room 103ABC and hang out with us all day!

March 16, 2026 03:58 PM UTC


Real Python

Spyder: Your IDE for Data Science Development in Python

There are many different integrated development environments (IDEs) to choose from for Python development. One popular option for data-focused work is Spyder, an open-source Python IDE geared toward scientists, engineers, and data analysts. Its name comes from Scientific PYthon Development EnviRonment.

Out of the box, it has powerful plotting, what-if, and profiling capabilities. It also integrates well with the data science ecosystem, is extensible with first- or third-party plugins, and has a relatively quick learning curve.

How does Spyder stack up against other Python IDEs? It depends on your use case. It’s not as powerful or customizable as VS Code, nor does it pretend to be. It does, however, excel for data science workflows:

Use Case Pick Spyder Pick an Alternative
Optimized for data science workflows
Dedicated to Python
Full-featured VS Code
Supports interactive notebooks ✅ With a plugin Jupyter, VS Code

If you’re focused on data science in Python, Spyder is a strong fit. For a more full-featured IDE or heavy notebook use, consider Jupyter or VS Code instead.

You can get a handy Spyder IDE cheat sheet at the link below:

Get Your Free Spyder Cheat Sheet: Click here to download the PDF with installation steps, shortcuts, and interface tips.

Take the Quiz: Test your knowledge with our interactive “Spyder: Your IDE for Data Science Development in Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Spyder: Your IDE for Data Science Development in Python

Test your knowledge of the Spyder IDE for Python data science, including its Variable Explorer, Plots pane, and Profiler.

Start Using the Spyder IDE

You can install Spyder in a few ways: as a standalone program, through a prepackaged distribution, or from the command line. You can also try out Spyder online.

To install Spyder as a standalone application, go to the Spyder download page. When you visit the site, it detects your operating system and offers the appropriate download. Once you download your install file, open it and follow the directions.

You can also install a Python distribution tailored to data science, such as Anaconda or WinPython. Both of these choices include Spyder in their base installations.

You’ll likely want to install dependencies and useful data libraries in addition to Spyder. In this case, first create a Python virtual environment, then use this command:

Shell
$ conda create -c conda-forge -n spyder-env spyder numpy scipy pandas matplotlib sympy cython

The install process for pip is similar. To install spyder together with common packages, run:

Shell
$ python -m pip install spyder numpy scipy pandas matplotlib sympy cython

For more information on installing Spyder, refer to their install guide.

Out of the box, the Spyder interface consists of three panes:

Spyder IDE InterfaceThe Spyder IDE Interface

On the left, you see code in the Editor pane. In the bottom right, you’ll find the IPython Console. Here, you can run code and check past commands using the History tab. The top-right area includes tabs such as Help, Debugger, Files, Find, and Code Analysis. You’ll learn about the Variable Explorer, Plots, and Profiler in the upcoming sections.

Explore Data With the Variable Explorer

Read the full article at https://realpython.com/spyder-ide/ »


[ 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 ]

March 16, 2026 02:00 PM UTC

Quiz: Speed Up Python With Concurrency

In this quiz, you’ll test your understanding of Speed Up Python With Concurrency.

You’ll explore how I/O-bound programs face latency, which concurrency patterns to use, the differences between threading, asyncio, and multiprocessing, and how the Global Interpreter Lock (GIL) affects Python programs.


[ 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 ]

March 16, 2026 12:00 PM UTC