25 Jun 2025
Planet Python
TestDriven.io: Building a Multi-tenant App with Django
This tutorial looks at how to implement multi-tenancy in Django.
25 Jun 2025 10:28pm GMT
Peter Bengtsson: Native connection pooling in Django 5 with PostgreSQL
Enabling native connection pooling in Django 5 gives me a 5.4x speedup.
25 Jun 2025 9:36pm GMT
Real Python: Your Guide to the Python print() Function
If you're like most Python users, then you probably started your Python journey by learning about print()
. It helped you write your very own "Hello, World!" one-liner and brought your code to life on the screen. Beyond that, you can use it to format messages and even find some bugs. But if you think that's all there is to know about Python's print()
function, then you're missing out on a lot!
Keep reading to take full advantage of this seemingly boring and unappreciated little function. This tutorial will get you up to speed with using Python print()
effectively. However, be prepared for a deep dive as you go through the sections. You may be surprised by how much print()
has to offer!
By the end of this tutorial, you'll understand that:
- The
print()
function can handle multiple arguments and custom separators to format output effectively. - You can redirect
print()
output to files or memory buffers using thefile
argument, enhancing flexibility. - Mocking
print()
in unit tests helps verify code behavior without altering the original function. - Using the
flush
argument ensures immediate output, overcoming buffering delays in certain environments. - Thread-safe printing is achievable by implementing locks to prevent output interleaving.
If you're just getting started with Python, then you'll benefit most from reading the first part of this tutorial, which illustrates the essentials of printing in Python. Otherwise, feel free to skip ahead and explore the sections that interest you the most.
Get Your Code: Click here to download the free sample code that shows you how to use the print() function in Python.
Take the Quiz: Test your knowledge with our interactive "The Python print() Function" quiz. You'll receive a score upon completion to help you track your learning progress:
Interactive Quiz
The Python print() FunctionIn this quiz, you'll test your understanding of Python's built-in print() function, covering how to format output, specify custom separators, and more.
Printing in a Nutshell
It's time to jump in by looking at a few real-life examples of printing in Python. By the end of this section, you'll know every possible way of calling print()
.
Producing Blank Lines
The simplest example of using Python print()
requires just a few keystrokes:
print()
This produces an invisible newline character, which in turn causes a blank line to appear on your screen. To add vertical space, you can call print()
multiple times in a row like this:
print()
print()
print()
It's just as if you were hitting Enter on your keyboard in a word processor program or a text editor.
While you don't pass any arguments to print()
, you still need to put empty parentheses at the end of the line to tell Python to actually execute that function rather than just refer to it by name. Without parentheses, you'd obtain a reference to the underlying function object:
>>> print()
>>> print
<built-in function print>
The code snippet above runs within an interactive Python REPL, as indicated by the prompt (>>>
). Because the REPL executes each line of Python code immediately, you see a blank line right after calling print()
. On the other hand, when you skip the trailing parentheses, you get to see a string representation of the print()
function itself.
As you just saw, calling print()
without arguments results in a blank line, which is a line comprised solely of the newline character. Don't confuse this with an empty string, which doesn't contain any characters at all, not even the newline!
You can use Python's string literals to visualize these two:
- Blank Line:
"\n"
- Empty String:
""
The first string literal is exactly one character long, whereas the second one has no content-it's empty.
Note: To remove the newline character from a string in Python, use its .rstrip()
method, like this:
>>> "A line of text.\n".rstrip()
'A line of text.'
This strips any trailing whitespace from the right edge of the string of characters. To learn more about .rstrip()
, check out the How to Strip Characters From a Python String tutorial.
Even though Python usually takes care of the newline character for you, it helps to understand how to deal with it yourself.
Dealing With Newlines
Read the full article at https://realpython.com/python-print/ »
[ 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 ]
25 Jun 2025 2:00pm GMT
Mike Driscoll: An Intro to ty – The Extremely Fast Python type checker
Ty is a brand new, extremely fast Python type checker written in Rust from the fine folks at Astral, the makers of Ruff. Ty is in preview and is not ready for production use, but you can still try it out on your code base to see how it compares to Mypy or other popular Python type checkers.
Getting Started with ty
You can try out ty using the online playground, or run ty with uvx to get started quickly:
uvx ty
If you prefer to install ty, you can use pip:
python -m pip install ty
Astral provides other installation methods as well.
Using the ty Type Checker
Want to give ty a try? You can run it in much the same way as you would Ruff. Open up your terminal and navigate to your project's top-level directory. Then run the following command:
ty check
If ty finds anything, you will quickly see the output in your terminal.
Astral has also provided a way to exclude files from type checking. By default, ty ignores files listed in an .ignore
or .gitignore
file.
Adding ty to Your IDE
The Astral team maintains an official VS Code extension for ty. You can get it from the VS Code Marketplace. Their documentation states that other IDEs can also use ty if they support the language server protocol.
Wrapping Up
Ruff is a great tool and has been adopted by many teams since its release. Ty will likely follow a similar trajectory if it as fast and useful as Ruff has been. Only time will tell. However, these new developments in Python tooling are exciting and will be fun to try. If you have used ty, feel free to jump into the comments and let me know what you think.
The post An Intro to ty - The Extremely Fast Python type checker appeared first on Mouse Vs Python.
25 Jun 2025 12:45pm GMT
Real Python: Quiz: The Python print() Function
In this quiz, you'll test your understanding of Your Guide to the Python print() Function.
The print()
function outputs objects to the console or a specified file-like stream. You'll practice:
- Printing multiple values with custom separators
- Changing the end-of-line character
- Redirecting output using the
file
parameter - Forcing immediate output with the
flush
parameter
Work through these questions to reinforce your knowledge of print()
's parameters and best practices for clear, formatted I/O.
[ 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 ]
25 Jun 2025 12:00pm GMT
PyPodcats: Trailer: Episode 9 With Tamara Atanasoska
A preview of our chat with Tamara Atanasoska. Watch the full episode on June 27, 2025A preview of our chat with Tamara Atanasoska. Watch the full episode on June 27, 2025
Sneak Peek of our chat with Tamara Atanasoska, hosted by Georgi Ker and Mariatta Wijaya.
Tamara has been contributing to open source projects since 2012. She participated in Google Summer of Code to contribute to projects like Gnome and e-cidadania.
She is now a maintainer of Fairlearn, an open-source, community-driven project to help data scientists improve fairness of AI systems.
Hear how Django helps her feel empowered, and how the PyLadies Berlin community has helped her feel welcomed as a new immigrant in Germany.
In this episode, Tamara shares perspective about open source contributions, maintain, mentorship, and her experience in running beginner-friendly sprints.
Full episode is coming on June 27, 2025! Subscribe to our podcast now!
25 Jun 2025 9:00am GMT
Talk Python to Me: #511: From Notebooks to Production Data Science Systems
If you're doing data science and have mostly spent your time doing exploratory or just local development, this could be the episode for you. We are joined by Catherine Nelson to discuss techniques and tools to move your data science game from local notebooks to full-on production workflows.<br/> <br/> <strong>Episode sponsors</strong><br/> <br/> <a href='https://talkpython.fm/agntcy'>Agntcy</a><br> <a href='https://talkpython.fm/sentry'>Sentry Error Monitoring, Code TALKPYTHON</a><br> <a href='https://talkpython.fm/training'>Talk Python Courses</a><br/> <br/> <h2 class="links-heading">Links from the show</h2> <div><strong>New Course: LLM Building Blocks for Python</strong>: <a href="https://training.talkpython.fm/courses/llm-building-blocks-for-python" target="_blank" >training.talkpython.fm</a><br/> <br/> <strong>Catherine Nelson LinkedIn Profile</strong>: <a href="https://www.linkedin.com/in/catherinenelson1/?featured_on=talkpython" target="_blank" >linkedin.com</a><br/> <strong>Catherine Nelson Bluesky Profile</strong>: <a href="https://bsky.app/profile/catnelson.bsky.social?featured_on=talkpython" target="_blank" >bsky.app</a><br/> <strong>Enter to win the book</strong>: <a href="https://forms.gle/1okKtSdSNTtAd4SRA?featured_on=talkpython" target="_blank" >forms.google.com</a><br/> <strong>Going From Notebooks to Scalable Systems - PyCon US 2025</strong>: <a href="https://us.pycon.org/2025/schedule/presentation/51/?featured_on=talkpython" target="_blank" >us.pycon.org</a><br/> <strong>Going From Notebooks to Scalable Systems - Catherine Nelson - YouTube</strong>: <a href="https://www.youtube.com/watch?v=o4hyA4hotxw&ab_channel=PyConUS" target="_blank" >youtube.com</a><br/> <strong>From Notebooks to Scalable Systems Code Repository</strong>: <a href="https://github.com/catherinenelson1/from_notebooks_to_scalable?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Building Machine Learning Pipelines Book</strong>: <a href="https://www.oreilly.com/library/view/building-machine-learning/9781492053187/?featured_on=talkpython" target="_blank" >oreilly.com</a><br/> <strong>Software Engineering for Data Scientists Book</strong>: <a href="https://www.oreilly.com/library/view/software-engineering-for/9781098136192/?featured_on=talkpython" target="_blank" >oreilly.com</a><br/> <strong>Jupytext - Jupyter Notebooks as Markdown Documents</strong>: <a href="https://github.com/mwouts/jupytext?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Jupyter nbconvert - Notebook Conversion Tool</strong>: <a href="https://github.com/jupyter/nbconvert?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Awesome MLOps - Curated List</strong>: <a href="https://github.com/visenger/awesome-mlops?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Watch this episode on YouTube</strong>: <a href="https://www.youtube.com/watch?v=n2WFfVIqlDw" target="_blank" >youtube.com</a><br/> <strong>Episode #511 deep-dive</strong>: <a href="https://talkpython.fm/episodes/show/511/from-notebooks-to-production-data-science-systems#takeaways-anchor" target="_blank" >talkpython.fm/511</a><br/> <strong>Episode transcripts</strong>: <a href="https://talkpython.fm/episodes/transcript/511/from-notebooks-to-production-data-science-systems" target="_blank" >talkpython.fm</a><br/> <br/> <strong>--- Stay in touch with us ---</strong><br/> <strong>Subscribe to Talk Python on YouTube</strong>: <a href="https://talkpython.fm/youtube" target="_blank" >youtube.com</a><br/> <strong>Talk Python on Bluesky</strong>: <a href="https://bsky.app/profile/talkpython.fm" target="_blank" >@talkpython.fm at bsky.app</a><br/> <strong>Talk Python on Mastodon</strong>: <a href="https://fosstodon.org/web/@talkpython" target="_blank" ><i class="fa-brands fa-mastodon"></i>talkpython</a><br/> <strong>Michael on Bluesky</strong>: <a href="https://bsky.app/profile/mkennedy.codes?featured_on=talkpython" target="_blank" >@mkennedy.codes at bsky.app</a><br/> <strong>Michael on Mastodon</strong>: <a href="https://fosstodon.org/web/@mkennedy" target="_blank" ><i class="fa-brands fa-mastodon"></i>mkennedy</a><br/></div>
25 Jun 2025 8:00am GMT
24 Jun 2025
Planet Python
PyCoder’s Weekly: Issue #687: Scaling With Kubernetes, Substrings, Big-O, and More (June 24, 2025)
#687 - JUNE 24, 2025
View in Browser »
Scaling Web Applications With Kubernetes and Karpenter
What goes into scaling a Python web application today? What are resources for learning and practicing DevOps skills? This week on the show, Calvin Hendryx-Parker is back to discuss the tools and infrastructure for autoscaling web applications with Kubernetes and Karpenter.
REAL PYTHON podcast
The Fastest Way to Detect a Vowel in a String
If you need to find the vowels in a string there are several different approaches you could take. This article covers 11 different ways and how each performs.
AUSTIN Z. HENLEY
Prevent Postgres Slowdowns on Python Apps with this Check List
Avoid performance regressions in your Python app by staying on top of Postgres maintenance. This monthly check list outlines what to monitor, how to catch slow queries early, and ways to ensure indexes, autovacuum, and I/O are performing as expected →
PGANALYZE sponsor
O(no) You Didn't
A deep dive into why real-world performance often defies Big-O expectations and why context and profiling matter more than theoretical complexity
MRSHINY608
Discussions
Python Jobs
Sr. Software Developer (Python, Healthcare) (USA)
Articles & Tutorials
The PSF's 2024 Annual Impact Report Is Here!
The Python Software Foundation releases a report every year on the state of the PSF and our community. This year's report gives a run down of the successes of the language, key highlights of PyCon US, updates from the developers in residence, and more.
PYTHON SOFTWARE FOUNDATION
Are Python Dictionaries Ordered Data Structures?
Although dictionaries have maintained insertion order since Python 3.6, they aren't strictly speaking ordered data structures. Read on to find out why and how the edge cases can be important depending on your use case.
STEPHEN GRUPPETTA
10 Polars Tools and Techniques to Level Up Your Data Science
Are you using Polars for your data science work? There are many libraries out there that might help you write less code. Talk Python interviews Christopher Trudeau and they talk about the Polars ecosystem.
KENNEDY & TRUDEAU podcast
Cut Django DB Latency With Native Connection Pooling
"Deploy Django 5.1's native connection pooling in 10 minutes to cut database latency by 50-70ms, reduce connection overhead by 60-80%, and improve response times by 10-30% with zero external dependencies."
SAURABH KUMAR
Will AI Replace Junior Developers?
At PyCon US this year, Adarsh chatted with various Python folks about one big question: will AI replace junior developers? He spoke with Guido van Rossum, Anthony Shaw, Simon Willison, and others.
ADARSH DIVAKARAN • Shared by Adarsh Divakaran
PEP 779: Free-Threaded Python (Accepted)
Free-threaded Python has been upgraded from experimental to part of the supported build. This quick quote from a longer discussion covers exactly what that means.
SIMON WILSON
PSF Board Election Schedule
It is time for the Python Software Foundation Board elections. Nominations are due by July 29th. See the article for the full election schedule and deadlines.
PYTHON SOFTWARE FOUNDATION
Exploring Python's list
Data Type With Examples
In this video course, you'll dive deep into Python's lists: how to create them, update their content, populate and grow them - with practical code examples.
REAL PYTHON course
Write Pythonic and Clean Code With namedtuple
Discover how Python's namedtuple
lets you create simple, readable data structures with named fields you can access using dot notation.
REAL PYTHON
Execute Your Python Scripts With a Shebang
In this video course, you'll learn when and how to use the shebang line in your Python scripts to execute them from a Unix-like shell.
REAL PYTHON course
All About the TypedDict
Are you fan of type hinting in Python? Learn how to add typing to a dictionary with different types of keys.
MIKE DRISCOLL
Projects & Code
Events
Weekly Real Python Office Hours Q&A (Virtual)
June 25, 2025
REALPYTHON.COM
PyCamp Leipzig 2025
June 28 to June 30, 2025
BARCAMPS.EU
Launching Python Katsina Community
June 28 to June 29, 2025
PYTHONKATSINA.ORG
PyDelhi User Group Meetup
June 28, 2025
MEETUP.COM
Workshop: Creating Python Communities
June 29 to June 30, 2025
PYTHON-GM.ORG
PyCon Colombia 2025
July 4 to July 7, 2025
PYCON.CO
Happy Pythoning!
This was PyCoder's Weekly Issue #687.
View in Browser »
[ Subscribe to 🐍 PyCoder's Weekly 💌 - Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]
24 Jun 2025 7:30pm GMT
Real Python: Starting With DuckDB and Python
The DuckDB database provides a seamless way to handle large datasets in Python with Online Analytical Processing (OLAP) optimization. You can create databases, verify data imports, and perform efficient data queries using both SQL and DuckDB's Python API.
By the end of this video course, you'll understand that:
- You can create a DuckDB database by reading data from files like Parquet, CSV, or JSON and saving it to a table.
- You query a DuckDB database using standard SQL syntax within Python by executing queries through a DuckDB connection object.
- You can also use DuckDB's Python API, which uses method chaining for an object-oriented approach to database queries.
- Concurrent access in DuckDB allows multiple reads but restricts concurrent writes to ensure data integrity.
- DuckDB integrates with pandas and Polars by converting query results into DataFrames using the
.df()
or.pl()
methods.
[ 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 ]
24 Jun 2025 2:00pm GMT
23 Jun 2025
Planet Python
Real Python: Python enumerate(): Simplify Loops That Need Counters
Python's enumerate()
function helps you with loops that require a counter by adding an index to each item in an iterable. This is particularly useful when you need both the index and value while iterating, such as listing items with their positions. You can also customize the starting index with the start
argument, offering flexibility in various scenarios.
By the end of this tutorial, you'll understand that:
enumerate()
in Python adds a counter to an iterable and returns it as anenumerate
object, pairing each item with an index.enumerate()
accepts an iterable and an optionalstart
argument to set the initial index value.- The advantage of
enumerate()
is that it provides both index and value pairs without manual counter management. - You can use
zip()
or slicing as alternatives toenumerate()
for iterating multiple sequences or selecting specific elements.
Explore how to implement your own version of enumerate()
and discover alternative methods like zip()
and itertools
for more complex iteration patterns.
Get Your Code: Click here to download the free sample code you'll use to explore how enumerate() works in Python.
Using Python's enumerate()
There are situations when you need both the index and the value of items in an iterable. That's when Python's enumerate()
function can come in handy for you.
Python's enumerate()
function adds a counter to an iterable and returns it in the form of an enumerate
object, which can be used directly in loops. For example, when you want to display the winners of a race, you might write:
>>> runners = ["Lenka", "Martina", "Gugu"]
>>> for winner in enumerate(runners):
... print(winner)
...
(0, 'Lenka')
(1, 'Martina')
(2, 'Gugu')
Since enumerate()
is a built-in function, you can use it right away without importing it. When you use enumerate()
in a for
loop, you get pairs of count and value from the iterable.
Note: If you're coming from languages like C, Java, or JavaScript, you might be tempted to use range(len())
to get both the index and value in a loop. While this works in Python, enumerate()
is considered a more Pythonic and preferred approach.
In the example above, you're storing the position and the runner's name in a variable named winner
, which is a tuple
. To display the position and the name right away, you can unpack the tuple
when you define the for
loop:
>>> runners = ["Lenka", "Martina", "Gugu"]
>>> for position, name in enumerate(runners):
... print(position, name)
...
0 Lenka
1 Martina
2 Gugu
Just like the winner
variable earlier, you can name the unpacked loop variables whatever you want. You use position
and name
in this example, but they could just as easily be named i
and value
, or any other valid Python names.
When you use enumerate()
in a for
loop, you typically tell Python to use two variables: one for the count and one for the value itself. You're able to do this by applying a Python concept called tuple unpacking.
Unpacking is the idea that a tuple-or another type of a Python sequence-can be split into several variables depending on the length of that sequence. For instance, you can unpack a tuple of two elements into two variables:
>>> cart_item = ("Wireless Mouse", 2)
>>> product_name, quantity_ordered = cart_item
>>> product_name
'Wireless Mouse'
>>> quantity_ordered
2
First, you create a tuple with two elements, "Wireless Mouse"
and 2
. Then, you unpack that tuple into product_name
and quantity_ordered
, which are each assigned one of the values from the tuple.
When you call enumerate()
and pass a sequence of values, Python returns an iterator. When you ask the iterator for its next value, it yields a tuple with two elements. The first element of the tuple is the count, and the second element is the value from the sequence that you passed:
>>> values = ["first", "second"]
>>> enumerate_instance = enumerate(values)
>>> print(enumerate_instance)
<enumerate at 0x7fe75d728180>
>>> next(enumerate_instance)
(0, 'first')
>>> next(enumerate_instance)
(1, 'second')
>>> next(enumerate_instance)
Traceback (most recent call last):
File "<python-input-5>", line 1, in <module>
next(enumerate_instance)
~~~~^^^^^^^^^^^^^^^
StopIteration
In this example, you create a Python list called values
with two elements, "first"
and "second"
. Then, you pass values
to enumerate()
and assign the return value to enumerate_instance
. When you print enumerate_instance
, you'll see that it's an enumerate
object with a particular memory address.
Then, you use Python's built-in next()
to get the next value from enumerate_instance
. The first value that enumerate_instance
returns is a tuple with the count 0
and the first element from values
, which is "first"
.
Calling next()
again on enumerate_instance
yields another tuple-this time, with the count 1
and the second element from values
, "second"
. Finally, calling next()
one more time raises a StopIteration
exception since there are no more values to be returned from enumerate_instance
.
When an iterable is used in a for
loop, Python automatically calls next()
at the start of every iteration until StopIteration
is raised. Python assigns the value it retrieves from the iterable to the loop variable.
By default, the count of enumerate()
starts at 0
. This isn't ideal when you want to display the winners of a race. Luckily, Python's enumerate()
has one additional argument that you can use to control the starting value of the count.
When you call enumerate()
, you can use the start
argument to change the starting count:
>>> runners = ["Lenka", "Martina", "Gugu"]
>>> for position, name in enumerate(runners, start=1):
... print(position, name)
...
1 Lenka
2 Martina
3 Gugu
In this example, you pass start=1
, which initializes position
with the value 1
on the first loop iteration.
You should use enumerate()
anytime you need to use the count and an item in a loop. Keep in mind that enumerate()
increments the count by one on every iteration. However, this only slightly limits your flexibility. Since the count is a standard Python integer, you can use it in many ways.
Practicing With Python enumerate()
Now that you've explored the basics of enumerate()
, it's time to practice using enumerate()
with some real-world examples.
Read the full article at https://realpython.com/python-enumerate/ »
[ 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 ]
23 Jun 2025 2:00pm GMT
Daniel Roy Greenfeld: TIL: HTML 404 errors for FastAPI
I had a TIL about custom responses in FastAPI but there's enough nuance that it deserves a full blog post. In the meantime, here's a TIL about custom HTTP responses.
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
async def custom_404_exception_handler(request, exc):
return HTMLResponse(
f'<p>404 Not Found at "{request.url.path}"</p>', status_code=404
)
# Add more HTTP exceptions as needed
HTTP_EXCEPTIONS = {404: custom_404_exception_handler}
app = FastAPI(exception_handlers=HTTP_EXCEPTIONS)
@app.get("/")
async def read_root():
return {"Hello": "World"}
Try it out by running the app and going to a non-existent path, like /not-found
. You should see a simple HTML page with a 404 message.
23 Jun 2025 1:00pm GMT
Python Bytes: #437 Python Language Summit 2025 Highlights
<strong>Topics covered in this episode:</strong><br> <ul> <li><em>* <a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025.html?featured_on=pythonbytes">The Python Language Summit 2025</a></em>*</li> <li><strong><a href="https://willmcgugan.github.io/fixing-python-properties/?featured_on=pythonbytes">Fixing Python Properties</a></strong></li> <li><em>* <a href="https://github.com/rohaquinlop/complexipy?featured_on=pythonbytes">complexipy</a></em>*</li> <li><em>* <a href="https://github.com/OKUA1/juvio?featured_on=pythonbytes">juvio</a></em>*</li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=sOJf3iBM6Tg' style='font-weight: bold;'data-umami-event="Livestream-Past" data-umami-event-episode="437">Watch on YouTube</a><br> <p><strong>About the show</strong></p> <p><strong>Sponsored by Posit:</strong> <a href="https://pythonbytes.fm/connect">pythonbytes.fm/connect</a></p> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy">@mkennedy@fosstodon.org</a> / <a href="https://bsky.app/profile/mkennedy.codes?featured_on=pythonbytes">@mkennedy.codes</a> (bsky)</li> <li>Brian: <a href="https://fosstodon.org/@brianokken">@brianokken@fosstodon.org</a> / <a href="https://bsky.app/profile/brianokken.bsky.social?featured_on=pythonbytes">@brianokken.bsky.social</a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes">@pythonbytes@fosstodon.org</a> / <a href="https://bsky.app/profile/pythonbytes.fm">@pythonbytes.fm</a> (bsky)</li> </ul> <p>Join us on YouTube at <a href="https://pythonbytes.fm/stream/live"><strong>pythonbytes.fm/live</strong></a> to be part of the audience. Usually <strong>Monday</strong> at 10am PT. Older video versions available there too.</p> <p>Finally, if you want an artisanal, hand-crafted digest of every week of the show notes in email form? Add your name and email to <a href="https://pythonbytes.fm/friends-of-the-show">our friends of the show list</a>, we'll never share it.</p> <p><strong>Michael #1: <a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025.html?featured_on=pythonbytes">The Python Language Summit 2025</a></strong></p> <ul> <li>Write up by Seth Michael Larson</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-how-can-we-make-breaking-changes-less-painful.html?featured_on=pythonbytes">How can we make breaking changes less painful?</a>: talk by Itamar Oren</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-uncontentious-talk-about-contention.html?featured_on=pythonbytes">An Uncontentious Talk about Contention</a>: talk by Mark Shannon</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-state-of-free-threaded-python.html?featured_on=pythonbytes">State of Free-Threaded Python</a>: talk by Matt Page</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-fearless-concurrency.html?featured_on=pythonbytes">Fearless Concurrency</a>: talk by Matthew Parkinson, Tobias Wrigstad, and Fridtjof Stoldt</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-challenges-of-the-steering-council.html?featured_on=pythonbytes">Challenges of the Steering Council</a>: talk by Eric Snow</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-docs-editorial-board.html?featured_on=pythonbytes">Updates from the Python Docs Editorial Board</a>: talk by Mariatta</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-packaging-governance-process.html?featured_on=pythonbytes">PEP 772 - Packaging Governance Process</a>: talk by Barry Warsaw and Pradyun Gedam</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-python-on-mobile.html?featured_on=pythonbytes">Python on Mobile - Next Steps</a>: talk by Russell Keith-Magee</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-what-do-core-developers-want-from-rust.html?featured_on=pythonbytes">What do Python core developers want from Rust?</a>: talk by David Hewitt</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-upstreaming-the-pyodide-js-ffi.html?featured_on=pythonbytes">Upstreaming the Pyodide JS FFI</a>: talk by Hood Chatham</li> <li><a href="https://pyfound.blogspot.com/2025/06/python-language-summit-2025-lightning-talks.html?featured_on=pythonbytes">Lightning Talks</a>: talks by Martin DeMello, Mark Shannon, Noah Kim, Gregory Smith, Guido van Rossum, Pablo Galindo Salgado, and Lysandros Nikolaou</li> </ul> <p><strong>Brian #2:</strong> <a href="https://willmcgugan.github.io/fixing-python-properties/?featured_on=pythonbytes">Fixing Python Properties</a></p> <ul> <li>Will McGugan</li> <li>"Python properties work well with type checkers such Mypy and friends. … The type of your property is taken from the getter only. Even if your setter accepts different types, the type checker will complain on assignment."</li> <li>Will describes a way to get around this and make type checkers happy.</li> <li>He replaces <code>@property</code> with a descriptor. It's a cool technique.</li> <li>I also like the way Will is allowing different ways to use a property such that it's more convenient for the user. This is a cool deverloper usability trick.</li> </ul> <p><strong>Brian #3: <a href="https://github.com/rohaquinlop/complexipy?featured_on=pythonbytes">complexipy</a></strong></p> <ul> <li>Calculates the cognitive complexity of Python files, written in Rust.</li> <li>Based on the cognitive complexity measurement described in a white paper by Sonar</li> <li>Cognitive complexity builds on the idea of cyclomatic complexity.</li> <li>Cyclomatic complexity was intended to measure the "testability and maintainability" of the control flow of a module. Sonar argues that it's fine for testability, but doesn't do well with measuring the "maintainability" part. So they came up with a new measure.</li> <li>Cognitive complexity is intended to reflects the relative difficulty of understanding, and therefore of maintaining methods, classes, and applications.</li> <li>complexipy essentially does that, but also has a really nice color output.</li> <li>Note: at the very least, you should be using "cyclomatic complexity" <ul> <li>try with <code>ruff check --select C901</code></li> </ul></li> <li>But also try complexipy.</li> <li>Great for understanding which functions might be ripe for refactoring, adding more documentation, surrounding with more tests, etc.</li> </ul> <p><strong>Michael #4: <a href="https://github.com/OKUA1/juvio?featured_on=pythonbytes">juvio</a></strong></p> <ul> <li>uv kernel for Jupyter</li> <li>⚙️ <strong>Automatic Environment Setup:</strong> When the notebook is opened, Juvio installs the dependencies automatically in an ephemeral virtual environment (using <code>uv</code>), ensuring that the notebook runs with the correct versions of the packages and Python</li> <li>📁 <strong>Git-Friendly Format:</strong> Notebooks are converted on the fly to a script-style format using <code># %%</code> markers, making diffs and version control painless</li> <li>Why Use Juvio? <ul> <li>No additional lock or requirements files are needed</li> <li>Guaranteed reproducibility</li> <li>Cleaner Git diffs</li> </ul></li> <li>Powered By <ul> <li><code>uv</code> - ultra-fast Python package management</li> <li><code>PEP 723</code> - Python inline dependency standards</li> </ul></li> </ul> <p><strong>Extras</strong></p> <p>Brian:</p> <ul> <li><a href="https://testandcode.com?featured_on=pythonbytes">Test & Code</a> in slow mode currently. But will be back with some awesome interviews.</li> </ul> <p><strong>Joke: <a href="https://www.youtube.com/watch?v=hwG89HH0VcM">The 0.1x Engineer</a></strong></p> <ul> <li>via Balázs</li> <li>Also <ul> <li><a href="https://www.youtube.com/watch?v=NY2nnsAplSo&ab_channel=StormtrooperVlogs">StormTrooper vlog</a></li> <li><a href="https://www.youtube.com/watch?v=oHYaOdIyuQA">BIGFOOT VLOG - ATTACKED BY WENDIGO!</a></li> </ul></li> </ul>
23 Jun 2025 8:00am GMT
21 Jun 2025
Planet Python
Will McGugan: Seeking sponsorship for Rich and Textual
After a somewhat stressful three years running a startup, I am takings a year's sabbatical. I'm going to use this time to focus on my health‐something I have neglected. As good as it feels not to work full-time, I still plan on maintaining Rich, Textual and other Open Source projects, in addition to offering free tech support.
Since I don't have any income at the moment, I want to start looking for sponsorship again. I'm hoping it will rise to a level where it will cover living costs for the year, but I'll be happy if it pays for coffee.
If you have benefited from my work in the past, consider sponsoring me. Whatever you feel comfortable with.
If your organization has extracted any value from my work, then consider picking one of the higher tiers to return the favor. I may be able to assist you with your project, wether it is terminal related or not. There is a lot of knowledge in my fleshy mammalian brain. Even in the age of AI, it could still benefit you.
21 Jun 2025 4:50pm GMT
Armin Ronacher: My First Open Source AI Generated Library
I'm currently evaluating how different models perform when generating XML versus JSON. Not entirely unexpectedly, XML is doing quite well - except for one issue: the models frequently return invalid XML. That made it difficult to properly assess the quality of the content itself, independent of how well the models serialize data. So I needed a sloppy XML parser.
After a few failed attempts of getting Claude to just fix up my XML parsing in different ways (it tried html5lib, the html lxml parser etc.) which all resulted in various kinds of amusing failures, I asked Claude to ultrathink and write me a proper XML library from scratch. I gave it some basic instructions of what this should look like and it one-shotted something. Afterwards I prompted it ~20 more times to do various smaller fixes as a response to me reviewing it (briefly) and using it and to create an extensive test suite.
While that was taking place I had 4o create a logo. After that I quickly converted it into an SVG with Illustrator and had Claude make it theme-aware for dark and light modes, which it did perfectly.
On top of that, Claude fully set up CI and even remotely controlled my browser to configure the trusted PyPI publisher for the package for me.
In summary, here is what AI did:
- It wrote ~1100 lines of code for the parser
- It wrote ~1000 lines of tests
- It configured the entire Python package, CI, PyPI publishing
- Generated a README, drafted a changelog, designed a logo, made it theme-aware
- Did multiple refactorings to make me happier
The initial prompt that started it all (including typos):
I want you to implement a single-file library here that parses XML sloppily. It should implement two functions:
- stream_parse which yields a stream of events (use named tuples) for the XML stream
- tree_parse which takes the output of stream_parse and assembles an element tree. It should default to xml.etree.ElementTree and optoinally allow you to provide lxml too (or anything else)
It should be fast but use pre-compiled regular expressions to parse the stream. The idea here is that the output comes from systems that just pretend to speak XML but are not sufficiently protected against bad outoput (eg: LLMs)
So for instance & should turn into & but if &x is used (which is invalid) it should just retain as &x in the output. Additionally if something is an invalid CDATA section we just gracefully ignore it. If tags are incorrectly closed within a larger tag we recover the structure. For instance <foo><p>a<p>b</foo> will just close the internal structures when </foo> comes around.
Use ultrathink. Break down the implementation into
- planning
- api stubs
- implementation
Use sub tasks and sub agents to conserve context
Now if you look at that library, you might not find it amazingly beautiful. It probably is a bit messy and might have a handful of bugs I haven't found yet. It however works well enough for me right now for what I'm doing and it definitely unblocked me. In total it worked for about 30-45 minutes to write the initial implementation while I was doing something else. I kept prompting it for a little while to make some progress as I ran into issues using it.
If you want to look at what it looks like:
To be clear: this isn't an endorsement of using models for serious Open Source libraries. This was an experiment to see how far I could get with minimal manual effort, and to unstick myself from an annoying blocker. The result is good enough for my immediate use case and I also felt good enough to publish it to PyPI in case someone else has the same problem.
Treat it as a curious side project which says more about what's possible today than what's necessarily advisable.
Postscriptum: Yes, I did slap an Apache 2 license on it. Is that even valid when there's barely a human in the loop? A fascinating question but not one I'm not eager to figure out myself. It is however certainly something we'll all have to confront sooner or later.
21 Jun 2025 12:00am GMT
20 Jun 2025
Planet Python
The Python Coding Stack: I Want to Remove Duplicates from a Python List • How Do I Do It?
Another short article today to figure out ways to remove duplicate values from a list. The ideal solution depends on what you really need.
Let's explore.
Start With a List
Well, we need a list first-ideally, one with duplicate values. So, let's assume we have an online queue (line). But some people put their name in the queue more than once:

Note how James and Kate were eager to ensure they were in the queue, so they put their name down twice.
Removing Duplicates: The Ugly Way
I was initially tempted not to include this section, but I changed my mind, as you can see. You can come up with several algorithms to perform this task "manually". It's only a few lines of code. Here's one option:
You have a queue_unique
empty list ready to collect unique names. Next, you iterate using enumerate()
and add names to queue_unique
if they don't appear in the rest of the original list. Note that I'm using slicing in the if
statement to slice the list from index + 1
to the end of the list.
Let me show you another option. I'll discuss the outputs from these two manual versions later in this article:
This time, you reverse the list so you can loop through the names in reverse order. The queue_unique
doesn't start as an empty list this time but as a copy of the original reversed list.
In the loop, you remove names from queue_unique
if the name appears later in the reversed list. A reminder that the .remove()
list method only removes the first occurrence of an item. It doesn't remove all of them.
Both algorithms remove duplicates. Great. But compare the output from the two versions. The difference between these output lists gives a clue to what's coming next.
But I won't dwell on these versions any longer.
and PS: there are better versions of manual algorithms for this, but that's not the point of this first section, so let's move on!
Removing Duplicates: The Set Way
When you learn about data structures, you learn about the various characteristics they have. Then, you start comparing data structures based on these characteristics. For example, lists, dictionaries, tuples, and strings are all iterable. But lists and dictionaries are mutable, whereas tuples and strings are immutable. And lists, tuples, and strings are all sequences, but dictionaries are not-they're mappings. You can read more about some of these categories here: The Python Data Structure Categories Series
And some data structures enforce uniqueness while others don't. Lists, as you've seen above, can have several equal items-in the example above, you have several strings that are equal to each other.
However, sets are a Python data structure that can only have unique values:
So, the easiest way to remove duplicates from a list is to cast it into a set:
Or, if you prefer the output to still be a list, and perhaps you also want to overwrite the original variable name, then you can write the following:
Now, that was easy! Much better than the several lines of code in the previous section.
However, there's an issue. If this is a queue of customers, then the order in which they joined the queue is somewhat important, I would say!
Note how the new queue
list, the one without duplicates, no longer maintains the original order of the people within it. James was the first to join the queue, but Andy appears to have moved to the front when you removed duplicates.
Note that this also happened with the first of the manual algorithms in the previous section.
Sometimes, you don't care about the order of the elements in a list. If that's the case, you can cast the list into a set and then back into a list to remove duplicates.
But sometimes, the order matters. It certainly matters when dealing with a queue of customers. Let's look at another option.
Removing Duplicates: The Dictionary Way
A quick question before I carry on. Did you read the latest article I published just before this one? Here's the link: Are Python Dictionaries Ordered Data Structures?
If you haven't, now is a good time to read it. Like this one, it's a short article, so it won't take you too long.
You're back. Great.
So, you now know that since Python 3.7, there's a guarantee that the order of insertion of items in a dictionary is maintained. And dictionary keys must also be unique-you cannot have the same key appear twice in a dictionary.
Therefore, if you could create a dictionary from the elements in the list queue
, you would remove duplicates but also maintain the order. And there's a dictionary class method for that:
You create a dictionary from the list queue
. The items in the list become keys, and each key has a default value of None
. You can customise this default value, but you don't need to in this case, as you'll see in the next paragraph.
Great, you removed duplicates while maintaining order since dictionaries maintain order. The dictionary is created by iterating through the list, which explains why this version maintains the order of the items. But you don't want a dictionary, and you don't care about the values within it. So, you can cast this dictionary back into a list. You only keep the keys when you cast a dictionary into a list:
You've now removed duplicates from the list and maintained the original order by converting the list into a dictionary and then back into a list.
Simple-once you know this idiom.
Do you want to join a forum to discuss Python further with other Pythonistas? Upgrade to a paid subscription here on The Python Coding Stack to get exclusive access to The Python Coding Place's members' forum. More Python. More discussions. More fun.
And you'll also be supporting this publication. I put plenty of time and effort into crafting each article. Your support will help me keep this content coming regularly and, importantly, will help keep it free for everyone.
A Limitation
Both the set and dictionary routes have an important limitation. Items in a set must be hashable objects. And keys in a dictionary must also be hashable. Therefore, you can't use these techniques if you have a list that includes non-hashable objects, such as a list that contains other lists.
You can read more about hashability and hashable objects in this post: Where's William? How Quickly Can You Find Him? • What's a Python Hashable Object?
Final Words
You may need to remove duplicates from a list in Python.
Don't write your own algorithm. Life's too short for that.
If you don't care about the order of the items in the list, cast the list into a set and then back into a list: list(set(queue))
If you do care about the order, create a dictionary from the list using dict.fromkeys()
and then cast it back into a list: list(dict.fromkeys(queue))
.
And the set and dictionary routes to removing duplicates are also more efficient than the manual ones shown above. So, it's a win-win.
That's it.
Photo by Lisett Kruusimäe: https://www.pexels.com/photo/flowers-in-line-on-white-background-9510861/
Code in this article uses Python 3.13
The code images used in this article are created using Snappify. [Affiliate link]
You can also support this publication by making a one-off contribution of any amount you wish.
For more Python resources, you can also visit Real Python-you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You'd like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com
Further reading related to this article's topic:
Appendix: Code Blocks
Code Block #1
queue = ["James", "Kate", "Andy", "James", "Isabelle", "Kate"]
Code Block #2
queue_unique = []
for index, name in enumerate(queue):
if name not in queue[index + 1:]:
queue_unique.append(name)
queue_unique
# ['Andy', 'James', 'Isabelle', 'Kate']
Code Block #3
queue = ['James', 'Kate', 'Andy', 'James', 'Isabelle', 'Kate']
queue.reverse()
queue
# ['Kate', 'Isabelle', 'James', 'Andy', 'Kate', 'James']
queue_unique = queue.copy()
for index, name in enumerate(queue):
if name in queue[index + 1:]:
queue_unique.remove(name)
queue_unique.reverse()
queue_unique
# ['James', 'Kate', 'Andy', 'Isabelle']
Code Block #4
set([1, 2, 3, 4, 3, 2, 1])
# {1, 2, 3, 4}
Code Block #5
queue = ["James", "Kate", "Andy", "James", "Isabelle", "Kate"]
set(queue)
# {'Andy', 'James', 'Kate', 'Isabelle'}
Code Block #6
queue = list(set(queue))
queue
# ['Andy', 'James', 'Kate', 'Isabelle']
Code Block #7
queue = ["James", "Kate", "Andy", "James", "Isabelle", "Kate"]
dict.fromkeys(queue)
# {'James': None, 'Kate': None, 'Andy': None, 'Isabelle': None}
Code Block #8
queue = list(dict.fromkeys(queue))
queue
# ['James', 'Kate', 'Andy', 'Isabelle']
For more Python resources, you can also visit Real Python-you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You'd like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com
20 Jun 2025 6:36pm GMT
Ruslan Spivak: Book Notes: Full Frontal Calculus by Seth Braver — Chapter 1 Review
"Where there is life, there is change; where there is change, there is calculus." - Seth Braver
I recently went back to studying math to rebuild my foundations for AI and machine learning. I didn't expect to enjoy a calculus book this much. Shocking, I know. But that's exactly what happened with Full Frontal Calculus.
Can calculus feel intuitive? Even fun? From the first few pages? For me, the answer was yes. (Okay, from page 8 to be exact.)
Why This Book Clicked for Me
As part of my self-study, I'm reviewing select chapters from the books I work through. This post covers Chapter 1 of Full Frontal Calculus by Seth Braver.
Before I stumbled on Full Frontal Calculus, I tried a few limit-based calculus books and textbooks, but none of them spoke to me. Luckily, there's no shortage of calculus material these days, so it's easy to shop around and try different sources.
Braver's book grabbed me right away. The early focus on infinitesimals, the tight writing, and the emphasis on intuition won me over. I even caught myself smiling more than once. Rare for a math book.
Chapter 1 Highlights
Chapter 1 starts with infinitesimals: "an infinitely small number, smaller than any positive real number, yet greater than zero." One early example shows how a circle, imagined as a polygon with infinitely many infinitesimal sides, leads to the familiar area formula πr². If your geometry or trig is rusty, don't worry - it still makes sense. Braver then uses the same idea to show how curves appear straight on a small enough (infinitesimal) scale, which is the heart of differential calculus.
Things really clicked for me in the section titled A Gift From Leibniz: d-Notation. Braver's explanation of dy/dx shows how it captures infinitesimal change in a way that just makes sense. It helped me understand why derivatives represent slopes and rates in a way I could explain to a 10-year-old. Working through the derivative of x² from first principles was also deeply satisfying.
Practically speaking, Chapter 1 covers:
- what infinitesimals are
- how they help us define rates of change
- the geometric meaning of derivatives
- the elegant dy/dx notation from Leibniz
- why we ignore higher-order infinitesimals like (dx)² or du * dv
- and a first-principles derivation of the derivative of x²
The chapter ends with two powerful tools: the power rule and linearity properties. These let you compute derivatives of polynomials using just basic mental math.
The writing is sharp and often funny, in a math kind of way. There's even a cameo by the Sumerian beer goddess Ninkasi, who helps explain rate of change and derivatives using a vat of beer. It sounds quirky, but it works.
The book's style, clarity, and focus on intuition made me want to keep going. That's not something I've felt with many math books.
Final Thoughts and Tips
If you're following along or just curious about studying calculus again, I recommend giving Chapter 1 a shot. It's not always light reading, and the exercises are essential, but it might click for you like it did for me. Chapter 1 is available for free on the author's site, so you can explore it before deciding whether to dive in.
If you do decide to dive into the book, here are a few tips to get the most out of it:
- If you're rusty on pre-calculus (I was), make sure you've got slope, rate of change, the point-slope formula, and the slope-intercept form down cold before the Rates of Change section on page 10. For that, Seth Braver's other book Precalculus Made Difficult has excellent material on those topics. You can probably get through it in a day.
- Read slowly, with a pen or pencil in hand. Write in the margins (get a paperback copy). It might feel painfully slow at times (pun intended), but it's a recipe for deeper understanding.
- The book includes answers to many exercises and is great for self-study. But the solutions are compact, so I recommend using Grok or ChatGPT to expand on them and deepen your understanding.
- Once you've finished the chapter and exercises, check out the author's YouTube videos that go along with the book. They're criminally underrated and oddly hard to find. You might enjoy them as much as I do.
- For topics that are hard to retain, try spaced repetition with active recall. Anki works great for that, or use whatever tool you prefer.
Chapter 1 sealed the deal. This is the calculus book I'm sticking with. Looking forward to seeing how Braver develops the ideas from here.
More to come. Stay tuned.
Originally published in my newsletter Beyond Basics. If you'd like to get future posts like this by email, you can subscribe here.
P.S. I'm not affiliated with the author. I just really enjoy the book and wanted to share it.
20 Jun 2025 2:07pm GMT