Planet Python
Last update: March 17, 2026 10:45 AM 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 […]
The post Textual – Creating a Custom Checkbox appeared first on Mouse Vs Python.
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 […]
PyCon
Attend PyCon US for a day of Trailblazing Python Security!
Real Python
Spyder: Your IDE for Data Science Development in Python
Learn how to use the Spyder IDE, a Python code editor built for scientists, engineers, and data analysts working with data-heavy workflows.
Quiz: Speed Up Python With Concurrency
Test your Python concurrency knowledge: CPU vs I/O-bound tasks, GIL, asyncio, race conditions, and multiprocessing.
Python Bytes
#473 A clean room rewrite?
Topics include , refined-github, , and Agentic Engineering Patterns.
March 15, 2026
The Python Coding Stack
The Weird and Wonderful World of Descriptors in Python • The Price is Right
The Weird and Wonderful World of Descriptors in Python • Let’s demystify one of the trickiest topics around
March 14, 2026
Real Python
Quiz: Splitting, Concatenating, and Joining Python Strings
Brush up on splitting, concatenating, and joining strings in Python. Test your understanding of methods, immutability, and common pitfalls.
Marcos Dione
Block sizes from OSM data
My city has big blocks. One close to mine is around 6km of circumference. I wanted to make a map that show the sizes of the blocks. What I have is a rendering DB for my area. Let's see what I can do.
An obvious solution would be to use any routing database, which would convert squiggly segments into edges of a graph. I feebly tried that, but no routing system I know is packaged for Debian, and one of them asked me to compile it. I could compile it, but I was lazy that way.
So the plan is this: take the whole street network1 and recursively remove the dead end streets. The recursion part is because there are areas where there are whole branches of dead end streets, so if in one pass a street A might look not dead because it connects to another B, but B is removed, the next pass should remove A too. So, for each street, take each end, and see how many other segments it's connected to; if 0, it's a dead end and we should remove it. Keep going until no new streets were removed.
One problem is how the data is represented. In a rendering database, streets do not exist, just segments with a consistent tagging, so streets can be split if, for instance, max speed changes, or there's a bridge. But one thing they're not split on is where another street joins them (which would be reflected in a routing db, lazy me! :)
This does not matter when searching for connected streets to a point, but a segment might have parts that are a dead end, and parts that are not, because they're connected to other streets(segments) that are connected too. So now, for each end, take the point, and if it's not connected, removed the point and try again. The full algo:
- Find all the segments
- While no segment has been removed
- For each segment
- For each end
- While the end is not connected
- Remove the end from the segment
- If not enough nodes left, remove the segment
- While the end is not connected
- For each end
- For each segment
Looks like an infinite problem! The city I'm interested in has 16k segments, and this looks worse than O(N²)! But we can do some shortcuts. One of the expensive operations is "the end is connected" or "find all the other segments that include this point". Luckily, a rendering DB has a geographic index, and we can do trics like looking for segments only around the area of the segment you're looking at, so reducing the amount of comparisons by a lot: less that 10 instead of 16k! And of course, you should not consider segments you already removed.
To be honest, I thought this would take way more effort and code. Yes, I changed the algo thrice, but all in all it took me like 6h to get it as it is. Now, it is not perfect. It does not detect some artifacts (mostly cycles connected to a single segment), but is good enough for me for now. Not bad for <200 lines of Python and SQL :)
#! /usr/bin/env python3 import psycopg2 from shapely import from_wkb, set_srid, LineString, Point def main(): print('Fetching all segments, takes a while.') conn = psycopg2.connect(dbname='europe') cur = conn.cursor() cur.execute(f""" WITH marseille AS ( SELECT way FROM planet_osm_polygon WHERE osm_id = -76469 ) SELECT line.osm_id, line.way FROM planet_osm_line AS line, marseille WHERE line.way && marseille.way AND line.highway IN ( 'primary', 'primary_link', 'secondary', 'secondary_link', 'tertiary', 'tertiary_link', 'residential', 'unclassified', 'living_street', 'road' ) AND (line.access IS NULL OR line.access = 'yes') { f" LIMIT {sys.argv[1]}" if len(sys.argv) > 1 else '' }; """) segments = {} segments_removed = set([666]) # fake osm_id so line.osm_id NOT IN %s AND works (empty sets are syntax errors) # first pass: collect all segments for data in cur.fetchall(): osm_id = data[0] segment = from_wkb(data[1]) segments[osm_id] = segment print(f"Found {len(segments)} segments.") # second pass: eliminate segments for which we can find an unconnected end pass_number = 1 while len(segments) > 0: print(f"PASS {pass_number:2d}") total_count = 0 total_changed = 0 to_remove = set() for osm_id, segment in segments.items(): changed = False removed = False connections = 0 for direction in -1, 0: while len(segment.coords) > 0: point = Point(segment.coords[direction]) cur.execute(""" WITH segment AS ( SELECT way FROM planet_osm_line WHERE osm_id = %s ) SELECT line.osm_id, line.name FROM planet_osm_line AS line, segment WHERE line.way && ST_Buffer(ST_Envelope(segment.way), 10) AND line.osm_id != %s AND line.osm_id NOT IN %s AND line.highway IN ( 'trunk', 'trunk_link', 'primary', 'primary_link', 'secondary', 'secondary_link', 'tertiary', 'tertiary_link', 'residential', 'unclassified', 'living_street', 'road' ) AND (access IS NULL OR access = 'yes') AND ST_Intersects(line.way, ST_GeomFromWKB(%s, 3857)); """, (osm_id, osm_id, tuple(segments_removed), point.wkb)) data = cur.fetchall() if len(data) == 0: if len(segment.coords) == 2: # removing one point removes the segment removed = True to_remove.add(osm_id) break # remove the point if direction == 0: segment = LineString(segment.coords[1:]) else: segment = LineString(segment.coords[:-1]) changed = True else: connections += len(data) break if changed: segments[osm_id] = segment total_changed += 1 total_count += 1 match total_count % 10, changed, removed: case _, True, _: print('*', end='', flush=True) case _, False, True: print('_', end='', flush=True) case 0, _, _: print('|', end='', flush=True) case 5, _, _: print(',', end='', flush=True) case _: print('.', end='', flush=True) if total_count % 100 == 0: print() segments_removed.update(to_remove) print() print(', '.join([ str(id) for id in to_remove ])) for osm_id in to_remove: del segments[osm_id] print(f"{total_changed} changed, {len(to_remove)} removed, {len(segments)} left.") print() # if only some segments were changed, the topology does not change, so we save one pass if len(to_remove) == 0: break pass_number += 1 print("Saving segments...") for segment in segments.values(): cur.execute(""" INSERT INTO streets (way) VALUES (ST_GeomFromWKB(%s, 3857)) """, (segment.wkb, )) conn.commit() conn.commit() print() print("Cutting...") for index, segment in enumerate(segments.values()): buffered = segment.buffer(1) marseille = difference(marseille, buffered) match (index + 1) % 10: case 0: print('|', end='', flush=True) case 5: print(',', end='', flush=True) case _: print('.', end='', flush=True) if (index + 1) % 100 == 0: print() print() out = open('blocks.geojson', 'w+') # GeoJSONstream, one GeoJSON object per line # GDAL/QGIS won't accept for polygon in marseille.geoms: out.write(f"{json.dumps(mapping(polygon))}\n") main()
Runtime takes a while because someone correctly micromapped a 350m dead end road due to diffrences in road side parking, 14 segments/passes in total; otherwise 8 would have suffised. I could have added a heuristic where, when a pass removed just a few segments, the next pass would only search among the segments close to those. Technically I could do this from the first pass.
One thing that surprised me whas this: QGIS can read GeoJSON files, but if they're going to be a colleciton of things,
better be a GeoJSONseq, that is a file with a GeoJSON object per line. But since these files do not include info about
the EPSG, you have to set it by hand on QGIS. now, this would be fine if QGIS would ask about it when loading the
layer, but instead two things happen: it confuses the EPSG, but would still correctly zoom to the layer assuming that
the layer has the same projection as the project. Thanks to uglyhack#qgis@libera.chat.
Several caveats:
I didn't include motorways or tunks in the block computation, because they create a complication in the definition of block, specially with bridges over or tunnels under other roads, of which there are a lot here. I know at least 3 bridges that still break the graph; there are few enough that I can ignore them.
The city has big blocks of industrial areas. Service roads are not included, and in any case most are dead ends.
The biggest areas are actually not urban, including a big chunk of a National Park in the two big red areas to the South2 and the ports and the sea nearby3. Extracting a real urban area is possible, but harder.
On top I also drew three things, roads (grey, white, blue), rivers (thick blue) and train tracks (black), to give a better idea of the complexity of the city. This time it includes motorways, trunks, service and private roads. The latter also give an idea of how much is in private hands (white). The thin blue lines are the streets thad define the blocks, and the grey are public but dead ends.

-
I'm focused on a traffic issue: the city is not navigable by car, leading to lots of traffic in the few streets available, leading to lots of noise from impatient drivers. ↩
-
The map is rotated 108° so I could zoom in as much as possible, so South is rougly to the left. ↩
-
See where all the ferry lines go to; that's the Vieux Port (Old Port), and the new, industrial one is in the same area to the North/right. ↩
Seth Michael Larson
I’ve added human.json to my website
March 13, 2026
EuroPython
Humans of EuroPython: Kshitijaa Jaglan
Discover the motivations behind volunteering, the challenges and rewards of organizing such a significant conference, and the impact it has on both attendees and contributors. Hear personal stories and learn how individuals like our interviewee help shape the future of Python through their commitment and collaboration.
In our latest interview
Talk Python to Me
#540: Modern Python monorepo with uv and prek
Monorepos -- you've heard the talks, you've read the blog posts, maybe you've seen a few tantalizing glimpses into how Google or Meta organize their massive codebases. But it's often in the abstract and behind closed doors. What if you could crack open a real, production monorepo, one with over a million lines of Python and over 100 of sub-packages, and actually see how it's built, step by step, using modern tools and standards? That's exactly what Apache Airflow gives us. On this episode, I sit down with Jarek Potiuk and Amogh Desai, two of Airflow's top contributors, to go inside one of the largest open-source Python monorepos in the world and learn how they manage it with uv, pyproject.toml, and the latest packaging standards, so you can apply those same patterns to your own projects.
PyCon
Launching the PyCon US 2026 Schedule!
PyCharm
Last week marked the fruition of almost a year of hard work by the entire PyCharm team. On March 4th, 2026, we hosted Python Unplugged on PyTV, our first-ever community conference featuring a 90s music-inspired online conference for the Python community. The PyCharm team is a fixture at Python conferences globally, such as PyCon US […]
Rodrigo Girão Serrão
TIL #141 – Inspect a lazy import
Today I learned how to inspect a lazy import object in Python 3.15.
Python 3.15 comes with lazy imports and today I played with them for a minute.
I defined the following module mod.py:
print("Hey!")
def f():
return "Bye!"
Then, in the REPL, I could check that lazy imports indeed work:
>>> # Python 3.15
>>> lazy import mod
>>>
The fact that I didn't see a "Hey!" means that the import is, indeed, lazy. Then, I wanted to take a look at the module so I printed it, but that triggered reification (going from a lazy import to a regular module):
>>> print(mod)
Hey!
<module 'mod' from '/Users/rodrigogs/Documents/tmp/mod.py'>
So, I checked the PEP that introduced explicit lazy modules and turns out as soon as you reference the lazy object directly, it gets reified.
But you can work around it by using globals:
>>> # Fresh 3.15 REPL
>>> lazy import mod
>>> globals()["mod"]
<lazy_import 'mod'>
This shows the new class lazy_import that was added to support lazy imports!
Pretty cool, right?
PyCharm
Python Unplugged on PyTV Recap
Real Python
The Real Python Podcast – Episode #287: 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.
Quiz: Your Python Coding Environment on Windows: Setup Guide
Test your knowledge of setting up a Python dev environment on Windows, from updates and terminals to paths, tools, and WSL.
PyPy
PyPy v7.3.21 release
PyPy v7.3.21: release of python 2.7, 3.11
The PyPy team is proud to release version 7.3.21 of PyPy after the previous release on July 4, 2025. This is a bug-fix release that also updates to Python 3.11.15.
The release includes two different interpreters:
PyPy2.7, which is an interpreter supporting the syntax and the features of Python 2.7 including the stdlib for CPython 2.7.18+ (the
+is for backported security updates)PyPy3.11, which is an interpreter supporting the syntax and the features of Python 3.11, including the stdlib for CPython 3.11.15.
The interpreters are based on much the same codebase, thus the double release. This is a micro release, all APIs are compatible with the other 7.3 releases.
We recommend updating. You can find links to download the releases here:
We would like to thank our donors for the continued support of the PyPy project. If PyPy is not quite good enough for your needs, we are available for direct consulting work. If PyPy is helping you out, we would love to hear about it and encourage submissions to our blog via a pull request to https://github.com/pypy/pypy.org
We would also like to thank our contributors and encourage new people to join the project. PyPy has many layers and we need help with all of them: bug fixes, PyPy and RPython documentation improvements, or general help with making RPython's JIT even better.
If you are a python library maintainer and use C-extensions, please consider making a HPy / CFFI / cppyy version of your library that would be performant on PyPy. In any case, cibuildwheel supports building wheels for PyPy.
What is PyPy?
PyPy is a Python interpreter, a drop-in replacement for CPython It's fast (PyPy and CPython performance comparison) due to its integrated tracing JIT compiler.
We also welcome developers of other dynamic languages to see what RPython can do for them.
We provide binary builds for:
x86 machines on most common operating systems (Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)
64-bit ARM machines running Linux (
aarch64) and macos (macos_arm64).
PyPy supports Windows 32-bit, Linux PPC64 big- and little-endian, Linux ARM 32 bit, RISC-V RV64IMAFD Linux, and s390x Linux but does not release binaries. Please reach out to us if you wish to sponsor binary releases for those platforms. Downstream packagers provide binary builds for debian, Fedora, conda, OpenBSD, FreeBSD, Gentoo, and more.
What else is new?
For more information about the 7.3.21 release, see the full changelog.
Please update, and continue to help us make pypy better.
Cheers, The PyPy Team
eGenix.com
PyDDF Python Spring Sprint 2026
The following text is in German, since we're announcing a Python sprint in Düsseldorf, Germany.
Ankündigung
Python Meeting Spring Sprint 2026 in
Düsseldorf
Samstag, 21.03.2026, 10:00-18:00 Uhr
Sonntag, 22.03.2026. 10:00-18:00 Uhr
Atos Information Technology GmbH, Am Seestern 1, 40547 Düsseldorf
Informationen
Das Python Meeting Düsseldorf (PyDDF) veranstaltet mit freundlicher Unterstützung von Atos Deutschland ein Python Sprint Wochenende.Der Sprint findet am Wochenende 21/22.03.2026 in der Atos Niederlassung, Am Seestern 1, in Düsseldorf statt.Folgende Themengebiete sind als Anregung bereits angedacht:
- MicroPythonOS: Portierung auf einen ESP32-S3 mit Display, Vernetzung per ESP-NOW
- Python Desktop Anwendungen mit Django und Electron umsetzen
- Weitere Themen folgen in den nächsten Tagen
Anmeldung, Kosten und weitere Infos
Alles weitere und die Anmeldung findet Ihr auf unserer Blog Seite:
WICHTIG: Ohne Anmeldung können wir den Gebäudezugang nicht vorbereiten. Eine spontane Anmeldung am Sprint Tag wird daher vermutlich nicht funktionieren.
Teilnehmer sollten sich zudem in der PyDDF Telegram Gruppe registrieren, da wir uns dort koordinieren:
Über das Python Meeting Düsseldorf
Das Python Meeting Düsseldorf ist eine regelmäßige Veranstaltung in Düsseldorf, die sich an Python-Begeisterte aus der Region wendet.
Einen guten Überblick über die Vorträge bietet unser PyDDF YouTube-Kanal, auf dem wir Videos der Vorträge nach den Meetings veröffentlichen.Veranstaltet wird das Meeting von der eGenix.com GmbH, Langenfeld, in Zusammenarbeit mit Clark Consulting & Research, Düsseldorf.
Marc-André Lemburg, eGenix.com
Daniel Roy Greenfeld
To return a value or not return a value
I believe operations that change things should always return values.
The Python Show
56 - Python Illustrated
In this episode, we hear from two sisters who put together a beginner's book about Python.
Audrey M. Roy Greenfeld
Staticware 0.2.0: The first cut
This is an early release. Staticware does something very satisfyingly today: it serves static files with content-hashed URLs for cache busting. That means when you edit your CSS then redeploy and restart your server, visitors get the latest CSS without forcing a refresh. More will come.
March 12, 2026
Python Morsels
Standard error
Standard error is one of the two writable file streams that is used for printing errors, warning messages, or any outputs that shouldn't be mixed with the main program.
Printing writes to "standard output" by default
When we call Python's print function, Python will write to standard output:
>>> print("Hi!")
Hi!
Standard output is a file-like object, also known as a file stream.
The standard output file-like object is represented by the stdout object in Python's sys module.
If we look at the documentation for Python's print function, we'll see that by default, print writes to sys.stdout:
>>> help(print)
Help on built-in function print in module builtins:
print(*args, sep=' ', end='\n', file=None, flush=False)
Prints the values to a stream, or to sys.stdout by default.
sep
string inserted between values, default a space.
end
string appended after the last value, default a newline.
file
a file-like object (stream); defaults to the current sys.stdout.
flush
whether to forcibly flush the stream.
If we call the write method on sys.stdout, text will write to the terminal screen:
>>> import sys
>>> bytes_written = sys.stdout.write("Hello!\n")
Hello!
Python also has a "standard error" stream
Standard output is actually one …
Read the full article: https://www.pythonmorsels.com/standard-error/
Real Python
Quiz: How to Use Ollama to Run Large Language Models Locally
Test your knowledge of running LLMs locally with Ollama. Install it, pull models, chat, and connect coding tools from your terminal.

