skip to navigation
skip to content

Planet Python

Last update: November 20, 2025 01:42 AM UTC

November 19, 2025


Django Weblog

Twenty years of Django releases

On November 16th 2005, Django co-creator Adrian Holovaty announced the first ever Django release, Django 0.90. Twenty years later, today here we are shipping the first release candidate of Django 6.0 🚀.

Since we’re celebrating Django’s 20th birthday this year, here are a few release-related numbers that represent Django’s history:

This is what decades’ worth of a stable framework looks like. Expect more gradual improvements and bug fixes over the next twenty years’ worth of releases. And if you like this kind of data, check out the State of Django 2025 report by JetBrains, with lots of statistics on our ecosystem (and there’s a few hours left on their Get PyCharm Pro with 30 % Off & Support Django offer).


Support Django

If you or your employer counts on Django’s 20 years of stability, consider whether you can support the project via donations to our non-profit Django Software Foundation.

Once you’ve done it, post with #DjangoBirthday and tag us on Mastodon / on Bluesky / on X / on LinkedIn so we can say thank you!

59%

Of our US $300,000.00 goal for 2025, as of November 19th, 2025, we are at:

  • 58.7% funded
  • $176,098.60 donated

Donate to support Django

November 19, 2025 03:27 PM UTC


Real Python

Build a Python MCP Client to Test Servers From Your Terminal

Building an MCP client in Python can be a good option when you’re coding MCP servers and want a quick way to test them. In this step-by-step project, you’ll build a minimal MCP client for the command line. It’ll be able to connect to an MCP server through the standard input/output (stdio) transport, list the server’s capabilities, and use the server’s tools to feed an AI-powered chat.

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

  • You can build an MCP client app for the command line using the MCP Python SDK and argparse.
  • You can list a server’s capabilities by calling .list_tools(), .list_prompts(), and .list_resources() on a ClientSession instance.
  • You can use the OpenAI Python SDK to integrate MCP tool responses into an AI-powered chat session.

Next, you’ll move through setup, client implementation, capability discovery, chat handling, and packaging to test MCP servers from your terminal.

Prerequisites

To get the most out of this coding project, you should have some previous knowledge of how to manage a Python project with uv. You should also know the basics of working with the asyncio and argparse libraries from the standard library.

To satisfy these knowledge requirements, you can take a look at the following resources:

Familiarity with OpenAI’s Python API, openai, will also be helpful because you’ll use this library to power the chat functionality of your MCP client. You’ll also use the Model Context Protocol (MCP) Python SDK.

Don’t worry if you don’t have all of the prerequisite knowledge before starting this tutorial—that’s completely okay! You’ll learn through the process of getting your hands dirty as you build the project. If you get stuck, then take some time to review the resources linked above. Then, get back to the code.

You’ll also need an MCP server to try your client as you build it. Don’t worry if you don’t have one available—you can use the server provided in step 2.

In this tutorial, you won’t get into the details of creating MCP servers. To learn more about this topic, check out the Python MCP Server: Connect LLMs to Your Data tutorial. Finally, you can download the project’s source code and related files by clicking the link below.

Get Your Code: Click here to download the free sample code you’ll use to build a Python MCP client to test servers from your terminal.

Take the Quiz: Test your knowledge with our interactive “Build a Python MCP Client to Test Servers From Your Terminal” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Build a Python MCP Client to Test Servers From Your Terminal

Learn how to create a Python MCP client, start an AI-powered chat session, and run it from the command line. Check your understanding.

Step 1: Set Up the Project and the Environment

To manage your MCP client project, you’ll use uv, a command-line tool for Python project management. If you don’t have this tool on your current system, then it’s worth checking out the Managing Python Projects With uv: An All-in-One Solution tutorial.

Note: If you prefer not to use uv, then you can use a combination of alternative tools such as pyenv, venv, pip, or poetry.

Once you have uv or another tool set up, go ahead and open a terminal window. Then, move to a directory where you typically store your projects. From there, run the following commands to scaffold and initialize a new mcp-client/ project:

Shell
$ uv init mcp-client
$ cd mcp-client/
$ uv add mcp openai

The first command creates a new Python project in an mcp-client/ directory. The resulting directory will have the following structure:

mcp-client/
├── .git/
├── .gitignore
├── .python-version
├── README.md
├── main.py
└── pyproject.toml

First, you have the .git/ directory and the .gitignore file, which will help you version-control the project.

The .python-version file contains the default Python version for the current project. This file tells uv which Python version to use when creating a dedicated virtual environment for the project. This file will contain the version number of the Python interpreter you’re currently using.

Next, you have an empty README.md file that you can use to provide basic documentation for your project. The main.py file provides a Python script that you can optionally use as the project’s entry point. You won’t use this file in this tutorial, so feel free to remove it.

Finally, you have the pyproject.toml file, which you’ll use to prepare your project for building and distribution.

Read the full article at https://realpython.com/python-mcp-client/ »


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

November 19, 2025 02:00 PM UTC


PyCharm

At JetBrains, we love seeing the developer community grow and thrive. That’s why we support open-source projects that make a real difference — the ones that help developers learn, build, and create better software together. We’re proud to back open-source maintainers with free licenses and to contribute to initiatives that strengthen the ecosystem and the people behind it.

In this post, we highlight five open‑source projects from different ecosystems, written in established languages like Python and JavaScript or fast‑growing ones like Rust. Different as they are, each shares the same goal: elevating the developer experience. Together, they show how the right tools boost productivity and make workflows more enjoyable.

Ratatui

Born as the community-driven successor to the discontinued tui-rs library, Ratatui brings elegance to terminal UIs. It’s modular, ergonomic, and designed to help developers build interactive dashboards, widgets, and even embedded interfaces that go beyond the terminal.

JetBrains IDEs help me focus on the code rather than the tooling. They’re self-contained, so I don’t need to configure much to get started – they just work. With powerful code highlighting, automatic fixes, refactorings, and structural search, I can easily jump around the codebase and make edits.

— Orhun Parmaksız, Ratatui Core Maintainer

The upcoming 0.30.0 release focuses on modularity, splitting the main crate into smaller, independently usable packages. This change simplifies maintenance and makes it easier to use widgets in other contexts. And with new no_std support, Ratatui is expanding to power a wide range of use cases beyond the terminal.

Django

If Ratatui brings usability to the terminal, Django brings it to the web. Originally created in 2003 to meet both fast-paced newsroom deadlines and the demands of experienced developers, Django remains the go-to framework for “perfectionists with deadlines”. It eliminates repetitive tasks, enforces clean, pragmatic design, and provides built-in solutions for security, scalability, and database management – helping developers write less code and achieve more.

JetBrains IDEs, especially PyCharm, boost productivity with built-in Django support – including project templates, automatic settings detection, and model-to-database migrations – as well as integrated debugging and testing tools that simplify finding and fixing issues. The version control integration also makes it easier for contributors to refine and polish their work.

— Sarah Boyce, Django Fellow

Backed by a thriving global community, Django’s roadmap includes composite primary key support, built-in CSP integration, and a focus on making Django accessible by default. Every eight-month release delivers incremental improvements while maintaining backward compatibility – clear proof that long-term stability and innovation can coexist.

JHipster

Both Django and JHipster help developers move fast, but they take different paths. JHipster began as the “anti-mullet stack” – serious in the back, party in the front – created to help developers quickly bootstrap full-stack applications with Spring on the backend and Angular.js on the frontend. Today, it’s still one of the most comprehensive open-source generators, offering a complete full-stack solution with built-in security, performance, and best practices.

JHipster has always been about great productivity and great tooling, so naturally, we’ve always been IntelliJ IDEA fans – we even have our own JHipster IntelliJ IDEA plugin! What I love most is the clean UI, the performance, and all the plugins that make my life so much easier. I use Maven and Docker support all the time, and they’re both absolutely top-notch.

— Julien Dubois, JHipster Creator

The project is now split into two teams – JHipster Classic, which focuses on the original full-stack generator written in JavaScript, and JHipster Lite, which develops a modernized, DDD-oriented version written in Java and targeted primarily at the backend. This structure allows the community to experiment more freely and attract new contributors.

As AI-assisted generation evolves, JHipster’s mission remains the same: empowering developers with the latest cutting-edge technology and a true full-stack approach.

Biome

Once the structure is in place, consistency becomes the next challenge. That’s where Biome, a modern, all-in-one toolchain for maintaining web projects, comes in. It supports every major web language and maintains a consistent experience between the CLI and the editor. The goal of its creators was simple: make a tool that can handle everything from development to production, with fewer dependencies, less setup time, faster CI runs, and clear, helpful diagnostics.

I’m a long-term user of JetBrains IDEs! RustRover has greatly improved since launch – its debugging features and new JavaScript module mean I can maintain all Biome projects, even our Astro-based website, in a single IDE. It’s great that JetBrains really listens to users and their feedback.

— Emanuele Stoppa, Biome Creator

Biome’s roadmap includes adding Markdown support, type inference, .d.ts file generation, JSDoc support, and embedded-language support. As a community-led project, Biome welcomes contributions of all kinds – every bit of help makes a difference.

Vuestic UI

When it’s time to polish the frontend, Vuestic UI takes over. This open-source project focuses on accessibility, theming, and a delightful developer experience. Built for Vue 3, it offers a flexible, easy-to-use component library that scales effortlessly from quick prototypes to enterprise-grade dashboards.

The right development environment makes a huge difference when building complex open-source tools like Vuestic UI and Vuestic Admin. Our team relies on JetBrains IDEs every day for their best-in-class refactoring tools that let us make bold changes with confidence, fast and reliable code navigation, and rock-solid performance. Most of what we need works right out of the box – no extra plugins or setup required. For us, JetBrains isn’t just a preference – it’s a productivity multiplier.

— Maxim Kobetz, Senior Vue.js Developer

After 12 years in frontend development, WebStorm – along with IntelliJ IDEA and PyCharm – has always been my trusted toolkit. Even now, when I’m not coding every day, I know I can rely on WebStorm for quick tweaks – every update feels smooth and never disrupts my workflow. It’s intuitive, beautiful, and just works the way I expect it to. I know switching IDEs is always a time sink, but with JetBrains, it’s absolutely worth it – you’ll never want to switch again.

— Anastasiia Zvenigorodskaia, Community Manager at Vuestic UI & Viuestic Admin

These projects showcase a common truth: Great developer experience happens when tools get out of your way. With JetBrains IDEs enhancing everything from code navigation to collaboration, these teams turn ideas into usable, elegant tools.

Whether you’re crafting a command-line tool, prototyping a web service, or polishing a UI library, these tools highlight a shared goal – making development faster, more intuitive, and more enjoyable. Explore them, contribute if you can, and keep shaping the kind of developer experience you’d want for yourself.

November 19, 2025 01:40 PM UTC


Django Weblog

Django 6.0 release candidate 1 released

Django 6.0 release candidate 1 is now available. It represents the final opportunity for you to try out a mosaic of modern tools and thoughtful design before Django 6.0 is released.

The release candidate stage marks the string freeze and the call for translators to submit translations. Provided no major bugs are discovered that can't be solved in the next two weeks, Django 6.0 will be released on or around December 3. Any delays will be communicated on the on the Django forum.

Please use this opportunity to help find and fix bugs (which should be reported to the issue tracker), you can grab a copy of the release candidate package from our downloads page or on PyPI.

The PGP key ID used for this release is Natalia Bidart: 2EE82A8D9470983E

November 19, 2025 12:00 PM UTC


Real Python

Quiz: Build a Python MCP Client to Test Servers From Your Terminal

In this quiz, you’ll test your understanding of how to Build a Python MCP Client to Test Servers From Your Terminal.

By working through this quiz, you’ll revisit how to add a minimal chat interface, create an AI handler to power the chat, handle runtime errors, and update the entry point to run the chat from the command line.

You will confirm when to initialize the AI handler and how to surface clear error messages to users. For a guided review, see the linked tutorial.


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

November 19, 2025 12:00 PM UTC


Django Weblog

Going build-free with native JavaScript modules

For the last decade and more, we've been bundling CSS and JavaScript files. These build tools allowed us to utilize new browser capabilities in CSS and JS while still supporting older browsers. They also helped with client-side network performance, minimizing the content to be as small as possible and combining files into one large bundle to reduce network handshakes. We've gone through a lot of build tools iterations in the process; from Grunt (2012) to Gulp (2013) to Webpack (2014) to Parcel (2017) to esbuild (2020) and Vite (2020).

And with modern browser technologies there is less need for these build tools.

These build processes are complex, particularly for beginners to Django. The tools and associated best practices move quickly. There is a lot to learn and you need to understand how to utilize them with your Django project. You can build a workflow that stores the build results in your static folder, but there is no core Django support for a build pipeline, so this largely requires selecting from a number of third party packages and integrating them into your project.

The benefit this complexity adds is no longer as clear cut, especially for beginners. There are still advantages to build tools, but you can can create professional results without having to use or learn any build processes.

Build-free JavaScript tutorial

To demonstrate modern capabilities, let's expand Django’s polls tutorial with some newer JavaScript. We’ll use modern JS modules and we won’t require a build system.

To give us a reason to need JS let's add a new requirement to the polls; to allow our users to add their own suggestions, instead of only being able to vote on the existing options. We update our form to have a new option under the selection code:

or add your own <input type="text" name="choice_text" maxlength="200" />

Now our users can add their own options to polls if the existing ones don't fit. We can update the voting view to handle this new option. We add a new choice_text input, and if there is no vote selection we will potentially handle adding the new option, while still providing an error message if neither is supplied. We also provide an error if both are selected.

def vote(request, question_id):
    if request.POST['choice'] and request.POST['choice_text']:
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You can't vote and provide a new option.",
        })

    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        if request.POST['choice_text']:
            selected_choice = Choice.objects.create(
                question=question,
                choice_text=request.POST['choice_text'],
            )
        else:
            return render(request, 'polls/detail.html', {
                'question': question,
                'error_message': "You didn't select a choice or provide a new one.",
            })
    selected_choice.votes += 1
    selected_choice.save()
    return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

Now that our logic is a bit more complex it would be nicer if we had some JavaScript to do this. We can build a script that handles some of the form validation for us.

function noChoices(choices, choice_text) {
  return (
    Array.from(choices).some((radio) => radio.checked) ||
    (choice_text[0] && choice_text[0].value.trim() !== "")
  );
}

function allChoices(choices, choice_text) {
  return (
    !Array.from(choices).some((radio) => radio.checked) &&
    choice_text[0] &&
    choice_text[0].value.trim() !== ""
  );
}

export default function initFormValidation() {
  document.getElementById("polls").addEventListener("submit", function (e) {
    const choices = this.querySelectorAll('input[name="choice"]');
    const choice_text = this.querySelectorAll('input[name="choice_text"]');

    if (!noChoices(choices, choice_text)) {
      e.preventDefault();
      alert("You didn't select a choice or provide a new one.");
    }
    if (!allChoices(choices, choice_text)) {
      e.preventDefault();
      alert("You can't select a choice and also provide a new option.");
    }
  });
}

Note how we use export default in the above code. This means form_validation.js is a JavaScript module. When we create our main.js file, we can import it with the import statement:

import initFormValidation from "./form_validation.js";

initFormValidation();

Lastly, we add the script to the bottom of our details.html file, using Django’s usual static template tag. Note the type="module" this is needed to tell the browser we will be using import/export statements.

<script type="module" src="{% static 'polls/js/main.js' %}"></script>

That’s it! We got the modularity benefits of modern JavaScript without needing any build process. The browser handles the module loading for us. And thanks to parallel requests since HTTP/2, this can scale to many modules without a performance hit.

In production

To deploy, all we need is Django's support for collecting static files into one place and its support for adding hashes to filenames. In production it is a good idea to use ManifestStaticFilesStorage storage backend. It stores the file names it handles by appending the MD5 hash of the file’s content to the filename. This allows you to set far future cache expiries, which is good for performance, while still guaranteeing new versions of the file will make it to users’ browsers.

This backend is also able to update the reference to form_validation.js in the import statement, with its new versioned file name.

Future work

ManifestStaticFilesStorage works, but a lot of its implementation details get in the way. It could be easier to use as a developer.

We discussed those possible improvements at the Django on the Med đŸ–ïž sprints and I’m hopeful we can make progress.

I built django-manifeststaticfiles-enhanced to attempt to fix all these. The core work is to switch to a lexer for CSS and JS, based on Ned Batchelder’s JsLex that was used in Django previously. It was expanded to cover modern JS and CSS by working with Claude Code to do the grunt work of covering the syntax.

It also switches to using a topological sort to find dependencies, whereas before we used a more brute force approach of repeated processing until we saw no more changes, which lead to more work, particularly on storages that used the network. It also meant we couldn't handle circular dependencies.

To validate it works, I ran a performance benchmark on 50+ projects, it’s been tested issues and with similar (often improved) performance. On average, it’s about 30% faster.


While those improvements would be welcome, do go ahead with trying build-free JavaScript and CSS in your Django projects today! Modern browsers make it possible to create great frontend experiences without the complexity.

November 19, 2025 08:13 AM UTC


Python GUIs

Getting Started With DearPyGui for GUI Development — Your First Steps With the DearPyGui Library for Desktop Python GUIs

Getting started with a new GUI framework can feel daunting. This guide walks you through the essentials of DearPyGui. From installation and first app to widgets, layouts, theming, and advanced tooling.

With DearPyGui, you can quickly build modern, high‑performance desktop interfaces using Python.

Getting to Know DearPyGui

DearPyGui is a GPU‑accelerated and cross‑platform GUI framework for Python, built on Dear ImGui with a retained‑mode Python API. It renders all UI using the GPU rather than native OS widgets, ensuring consistent, high‑performance UI across Windows, Linux, macOS, and even Raspberry Pi 4.

Note that official wheels for Raspberry Pi may lag behind. Users sometimes compile from source.

DearPyGui's key features include the following:

This GUI framework is ideal for building interfaces ranging from simple utilities to real-time dashboards, data‑science tools, or interactive games.

Installing and Setting Up DearPyGui

You can install DearPyGui from PyPI using pip:

sh
$ pip install dearpygui

This command installs DearPyGui from PyPI.

Writing Your First GUI App

In general, DearPyGui apps follow the following structure:

  1. dpg.create_context() — Initialize DearPyGui and call it before anything else
  2. dpg.create_viewport() — Create the main application window or viewport
  3. Define UI widgets within windows or groups — Add and configure widgets and containers to build your interface
  4. dpg.setup_dearpygui() — Set up DearPyGui internals and resources before showing the viewport
  5. dpg.show_viewport() — Make the viewport window visible to the user
  6. dpg.start_dearpygui() — Start the DearPyGui main event and render loop
  7. dpg.destroy_context() — Clean up and release all DearPyGui resources on exit

Here's a quick application displaying a window with basic widgets:

python
import dearpygui.dearpygui as dpg

def main():
    dpg.create_context()
    dpg.create_viewport(title="Viewport", width=300, height=100)

    with dpg.window(label="DearPyGui Demo", width=300, height=100):
        dpg.add_text("Hello, World!")

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()

Inside main(), we initialize the library with dpg.create_context(), create a window (viewport) via dpg.create_viewport(), define the GUI, set up the library with dpg.setup_dearpygui(), show the viewport with dpg.show_viewport(), and run the render loop using dpg.start_dearpygui(). When you close the window, dpg.destroy_context() cleans up resources.

You define the GUI itself inside a dpg.window() context block, which parents the a text label with the "Hello, World!" Text.

Always follow the lifecycle order: create context → viewport → setup → show → start → destroy. Otherwise, the app may crash.

Run it! Here's what your first app looks like.

DearPyGui first app DearPyGui first app

Exploring Widgets

DearPyGui includes a wide variety of widgets:

Here's an example that showcases some basic DearPyGui widgets:

python
import dearpygui.dearpygui as dpg

def main():
    dpg.create_context()
    dpg.create_viewport(title="Widgets Demo", width=400, height=450)

    with dpg.window(
        label="Common DearPyGui Widgets",
        width=380,
        height=420,
        pos=(10, 10),
    ):
        dpg.add_text("Static label")
        dpg.add_input_text(
            label="Text Input",
            default_value="Type some text here...",
            tag="widget_input",
        )
        dpg.add_button(label="Click Me!")
        dpg.add_checkbox(label="Check Me!")
        dpg.add_radio_button(
            ("DearPyGui", "PyQt6", "PySide6"),
        )

        dpg.add_slider_int(
            label="Int Slider",
            default_value=5,
            min_value=0,
            max_value=10,
        )
        dpg.add_slider_float(
            label="Float Slider",
            default_value=0.5,
            min_value=0.0,
            max_value=1.0,
        )

        dpg.add_combo(
            ("DearPyGui", "PyQt6", "PySide6"),
            label="GUI Library",
        )
        dpg.add_color_picker(label="Pick a Color")
        dpg.add_progress_bar(
            label="Progress",
            default_value=0.5,
            width=250,
        )

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()

This code uses the following functions to add the widgets to the GUI:

Run it! Here's what the app will look like.

DearPyGui basic widgets DearPyGui basic widgets

Laying Out the GUI

By default, DearPyGui stacks widgets vertically. However, positioning options include the following:

Widgets go inside containers like dpg.window(). You can nest containers to build complex GUI layouts:

python
import dearpygui.dearpygui as dpg

def main():
    dpg.create_context()
    dpg.create_viewport(title="Layout Demo", width=520, height=420)

    with dpg.window(
        label="Layout Demo",
        width=500,
        height=380,
        pos=(10, 10),
    ):
        dpg.add_text("1) Vertical layout:")
        dpg.add_button(label="Top")
        dpg.add_button(label="Middle")
        dpg.add_button(label="Bottom")

        dpg.add_spacer(height=12)

        dpg.add_text("2) Horizontal layout:")
        with dpg.group(horizontal=True):
            dpg.add_button(label="Left")
            dpg.add_button(label="Center")
            dpg.add_button(label="Right")

        dpg.add_spacer(height=12)

        dpg.add_text("3) Indentation:")
        dpg.add_checkbox(label="Indented at creation (30px)", indent=30)
        dpg.add_checkbox(label="Indented after creation (35px)", tag="indent_b")
        dpg.configure_item("indent_b", indent=35)

        dpg.add_spacer(height=12)

        dpg.add_text("4) Absolute positioning:")
        dpg.add_text("Positioned at creation: (x=100, y=300)", pos=(100, 300))
        dpg.add_text("Positioned after creation: (x=100, y=320)", tag="move_me")
        dpg.set_item_pos("move_me", [100, 320])

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()

In this example, we create an app that showcases basic layout options in DearPyGui. The first section of widgets shows the default vertical stacking by adding three buttons one after another. Then, you use dpg.add_spacer(height=12) to insert vertical whitespace between sections.

Then, we create a horizontal row of buttons with dpg.group(horizontal=True), which groups items side-by-side. Next, we have an indentation section that demonstrates how to indent widgets at creation (indent=30) and after creation using dpg.configure_item().

Finally, we use absolute positioning by placing one text item at a fixed coordinate using pos=(100, 300) and moving another after creation with dpg.set_item_pos(). These patterns are all part of DearPyGui’s container and item-configuration model, which we can use to arrange the widgets in a user-friendly GUI.

Run it! You'll get a window like the following.

DearPyGui layouts DearPyGui layouts

Event Handling with Callbacks

DearPyGui uses callbacks to handle events. Most widgets accept a callback argument, which is executed when we interact with the widget itself.

The example below provides a text input and a button. When you click the button, it launches a dialog with the input text:

python
import dearpygui.dearpygui as dpg

def on_click_callback(sender, app_data, user_data):
    text = dpg.get_value("input_text")
    dpg.set_value("dialog_text", f'You typed: "{text}"')
    dpg.configure_item("dialog", show=True)

def main() -> None:
    dpg.create_context()
    dpg.create_viewport(title="Callback Example", width=270, height=120)

    with dpg.window(label="Callback Example", width=250, height=80, pos=(10, 10)):
        dpg.add_text("Type something and press Click Me!")
        dpg.add_input_text(label="Input", tag="input_text")
        dpg.add_button(label="Click Me!", callback=on_click_callback)
        with dpg.window(
            label="Dialog",
            modal=True,
            show=False,
            width=230,
            height=80,
            tag="dialog",
            no_close=True,
            pos=(10, 10),
        ):
            dpg.add_text("", tag="dialog_text")
            dpg.add_button(
                label="OK",
                callback=lambda s, a, u: dpg.configure_item("dialog", show=False),
            )

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()

The button takes the on_click_callback() callback as an argument. When we click the button, DearPyGui invokes the callback with three standard arguments:

  1. sender, which holds the button's ID
  2. app_data, which holds extra data specific to certain widgets
  3. user_data, which holds custom data you could have supplied

Inside the callback, we pull the current text from the input widget using dpg.get_value(), and finally, we display the input text in a modal window.

Run it! You'll get a window like the following.

DearPyGui callbacks DearPyGui callbacks

To see this app in action, type some text into the input and click the Click Me! button.

Drawing Shapes and Plotting

DearPyGui comes with powerful plotting capabilities. It includes high-performance plots, including lines, bars, scatter, and histograms. These plots allow interactive zoom and pan and real-time data updates, making them excellent for scientific visualizations and dashboards.

Here's a quick example of how to create a plot using DearPyGui's plotting widgets:

python
import dearpygui.dearpygui as dpg
import numpy as np

def main() -> None:
    dpg.create_context()
    dpg.create_viewport(title="Plotting Example", width=420, height=320)

    x = np.linspace(0, 2 * np.pi, 100)
    y1 = np.sin(x)
    y2 = np.cos(x)

    with dpg.window(label="Plot Window", width=400, height=280, pos=(10, 10)):
        with dpg.plot(label="Sine and Cosine Plot", height=200, width=360):
            dpg.add_plot_legend()
            dpg.add_plot_axis(dpg.mvXAxis, label="X")
            with dpg.plot_axis(dpg.mvYAxis, label="Y"):
                dpg.add_line_series(x.tolist(), y1.tolist(), label="sin(x)")
                dpg.add_line_series(x.tolist(), y2.tolist(), label="cos(x)")

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()

In this example, we create two line series: sine and cosine curves. To plot them, we use NumPy‑generated data. We also add X and Y axes, plus a legend for clarity. You can update the series in a callback for live data dashboards.

Run it! You'll get a plot like the one shown below.

DearPyGui plotting demo DearPyGui plotting demo

Conclusion

DearPyGui offers a powerful and highly customizable GUI toolkit for desktop Python applications. With a rich widget set, interactive plotting, node editors, and built-in developer tools, it's a great choice for both simple and complex interfaces.

Try building your first DearPyGui app and experimenting with widgets, callbacks, layouts, and other interesting features!

November 19, 2025 08:00 AM UTC

November 18, 2025


The Python Coding Stack

I Don’t Like Magic ‱ Exploring The Class Attributes That Aren’t Really Class Attributes ‱ [Club]

I don’t like magic. I don’t mean the magic of the Harry Potter kind—that one I’d like if only I could have it. It’s the “magic” that happens behind the scenes when a programming language like Python does things out of sight. You’ll often find things you have to “just learn” along the Python learning journey. “That’s the way things are,” you’re told.

That’s the kind of magic I don’t like. I want to know how things work. So let me take you back to when I first learnt about named tuples—the NamedTuple in the typing module, not the other one—and data classes. They share a similar syntax, and it’s this shared syntax that confused me at first. I found these topics harder to understand because of this.

Their syntax is different from other stuff I had learnt up to that point. And I could not reconcile it with the stuff I knew. That bothered me. It also made me doubt the stuff I already knew. Here’s what I mean. Let’s look at a standard class first:

class Person:
    classification = “Human”
​
    def __init__(self, name, age, profession):
        self.name = name
        self.age = age
        self.profession = profession

You define a class attribute, .classification, inside the class block, but outside any of the special methods. All instances will share this class attribute. Then you define the .__init__() special method and create three instance attributes: .name, .age, and .profession. Each instance will have its own versions of these instance attributes. If you’re not familiar with class attributes and instance attributes, you can read my seven-part series on object-oriented programming: A Magical Tour Through Object-Oriented Programming in Python • Hogwarts School of Codecraft and Algorithmancy

Now, let’s assume you don’t actually need the class attribute and that this class will only store data. It won’t have any additional methods. You decide to use a data class instead:

from dataclasses import dataclass
​
@dataclass
class Person:
    name: str
    age: int
    profession: str

Or you prefer to use a named tuple, and you reach out for typing.NamedTuple:

from typing import NamedTuple
​
class Person(NamedTuple):
    name: str
    age: int
    profession: str

The syntax is similar. I’ll tell you why I used to find this confusing soon.

Whichever option you choose, you can create an instance using Person(”Matthew”, 30, “Python Programmer”). And each instance you create will have its own instance attributes .name, .age, and .profession.

But wait a minute! The data class and the named tuple use syntax that’s similar to creating class attributes. You define these just inside the class block and not in an .__init__() method. How come they create instance attributes? “That’s just how they work” is not good enough for me.

These aren’t class attributes. Not yet. There’s no value associated with these identifiers. Therefore, they can’t be class attributes, even though you write them where you’d normally add class attributes in a standard class. However, they can be class attributes if you include a default value:

@dataclass
class Person:
    name: str
    age: int
    profession: str = “Python Programmer”

The .profession attribute now has a string assigned to it. In a data class, this represents the default value. But if this weren’t a data class, you’d look at .profession and recognise it as a class attribute. But in a data class, it’s not a class attribute, it’s an instance attribute, as are .name and .age, which look like…what do they look like, really? They’re just type hints. Yes, type hints without any object assigned. Python type hints allow you to do this:

>>> first_name: str

This line is valid in Python. It does not create the variable name. You can confirm this:

>>> first_name
Traceback (most recent call last):
  File “<input>”, line 1, in <module>
NameError: name ‘first_name’ is not defined

Although you cannot just write first_name if the identifier doesn’t exist, you can use first_name: str. This creates an annotation which serves as the type hint. Third-party tools now know that when you create the variable first_name and assign it a value, it ought to be a string.

So, let’s go back to the latest version of the Person data class with the default value for one of the attributes:

@dataclass
class Person:
    name: str
    age: int
    profession: str = “Python Programmer”

But let’s ignore the @dataclass decorator for now. Indeed, let’s remove this decorator:

class Person:
    name: str
    age: int
    profession: str = “Python Programmer”

You define a class with one class attribute, .profession and three type hints:

How can we convert this information into instance attributes when creating an instance of the class? I won’t try to reverse engineer NamedTuple or data classes here. Instead, I’ll explore my own path to get a sense of what might be happening in those tools.

Let’s start hacking away…

Read more

November 18, 2025 10:01 PM UTC


PyCoder’s Weekly

Issue #709: deepcopy(), JIT, REPL Tricks, and More (Nov. 18, 2025)

#709 – NOVEMBER 18, 2025
View in Browser »

The PyCoder’s Weekly Logo


Why Python’s deepcopy Can Be So Slow

“Python’s copy.deepcopy() creates a fully independent clone of an object, traversing every nested element of the object graph.” That can be expensive. Learn what it is doing and how you can sometimes avoid the cost.
SAURABH MISRA

A Plan for 5-10%* Faster Free-Threaded JIT by Python 3.16

Just In Time compilation is under active development in the CPython interpreter. This blog post outlines the targets for the next two Python releases.
KEN JIN

Fast Container Builds: 202 - Check out the Deep Dive

alt

This blog explores the causes and consequences of slow container builds, with a focus on understanding how BuildKit’s capabilities support faster container builds. →
DEPOT sponsor

The Python Standard REPL: Try Out Code and Ideas Quickly

The Python REPL gives you instant feedback as you code. Learn to use this powerful tool to type, run, debug, edit, and explore Python interactively.
REAL PYTHON

Join in the PSF Year-End Fundraiser & Membership Drive!

PYTHON SOFTWARE FOUNDATION

PEP 814: Add Frozendict Built-in Type (Added)

PYTHON.ORG

PyBay 2025 Videos Released

YOUTUBE.COM

DjangoCon US 2025 Videos Released

YOUTUBE.COM

Python Jobs

Python Video Course Instructor (Anywhere)

Real Python

Python Tutorial Writer (Anywhere)

Real Python

More Python Jobs >>>

Articles & Tutorials

Preparing Data Science Projects for Production

How do you prepare your Python data science projects for production? What are the essential tools and techniques to make your code reproducible, organized, and testable? This week on the show, Khuyen Tran from CodeCut discusses her new book, “Production Ready Data Science.”
REAL PYTHON podcast

Becoming a Core Developer

Throughout your open source journey, you have no doubt been interacting with the core development team of the projects to which you have been contributing. Have you ever wondered how people become core developers of a project?
STEFANIE MOLIN

Modern, Self-Hosted Authentication

alt

Keep your users, your data and your stack with PropelAuth BYO. Easily add Enterprise authentication features like Enterprise SSO, SCIM and session management. Keep your sales team happy and give your CISO piece of mind →
PROPELAUTH sponsor

38 Things Python Developers Should Learn in 2025

Talk Python interviews Peter Wang and Calvin Hendrix-Parker and they discuss loads of things in the Python ecosystem that are worth learning, including free-threaded CPython, MCP, DuckDB, Arrow, and much more.
TALK PYTHON podcast

Trusted Publishing for GitLab Self-Managed and Organizations

The Trusted Publishing system for PyPI is seeing rapid adoption. This post talks about its growth along with the next steps: adding GitLab and handling organizations.
MIKE FIELDER

Decompression Is Up to 30% Faster in CPython 3.15

Zstandard compression got added in Python 3.14, but work is on-going. Python 3.15 is showing performance improvements in both zstd and other compression modules.
EMMA SMITH

__slots__ for Optimizing Classes

Most Python objects store their attributes in __dict__, which is a dictionary. Modules and classes always use __dict__, but not everything does.
TREY HUNNER

Convert Documents Into LLM-Ready Markdown

Get started with Python MarkItDown to turn PDFs, Office files, images, and URLs into clean, LLM-ready Markdown in seconds.
REAL PYTHON

Quiz: Convert Documents Into LLM-Ready Markdown

Practice MarkItDown basics. Convert PDFs, Word documents, Excel documents, and HTML documents to Markdown. Try the quiz.
REAL PYTHON

Convert Scikit-learn Pipelines into SQL Queries with Orbital

Orbital is a new library that converts Scikit-learn pipelines into SQL queries, enabling machine learning model inference directly within SQL databases.
POSIT sponsor

Python Operators and Expressions

Operators let you combine objects to create expressions that perform computations – the core of how Python works.
REAL PYTHON course

A Generator, Duck Typing, and a Branchless Conditional Walk Into a Bar

What’s your favorite line of code? Rodrigo expounds about generators, duck typing, and branchless conditionals.
RODRIGO GIRÃO SERRÃO

Projects & Code

fastapi-voyager: Explore Your API Interactively

GITHUB.COM/ALLMONDAY

Narwhals-daft: A Narwhals Plugin for Daft Dataframes

MARCO GORELLI ‱ Shared by Marco Gorelli

zensical: A Modern Static Site Generator

GITHUB.COM/ZENSICAL

django-subatomic: Control Transaction Logic in Django

GITHUB.COM/KRAKEN-TECH

httptap: CLI Measuring HTTP Request Phases

GITHUB.COM/OZERANSKII

Events

Weekly Real Python Office Hours Q&A (Virtual)

November 19, 2025
REALPYTHON.COM

DELSU Tech Invasion 3.0

November 19 to November 21, 2025
HAMPLUSTECH.COM

PyData Bristol Meetup

November 20, 2025
MEETUP.COM

PyLadies Dublin

November 20, 2025
PYLADIES.COM

Python Sul 2025

November 21 to November 24, 2025
PYTHON.ORG.BR


Happy Pythoning!
This was PyCoder’s Weekly Issue #709.
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 ]

November 18, 2025 07:30 PM UTC


Real Python

Break Out of Loops With Python's break Keyword

In Python, the break statement lets you exit a loop prematurely, transferring control to the code that follows the loop. This tutorial guides you through using break in both for and while loops. You’ll also briefly explore the continue keyword, which complements break by skipping the current loop iteration.

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


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

November 18, 2025 02:00 PM UTC


Mike Driscoll

Black Friday Python Deals Came Early

Black Friday deals came early this year. You can get 50% off of any of my Python books or courses until the end of November. You can use this coupon code at checkout: BLACKISBACK 

The following links already have the discount applied:

Python eBooks

Python Courses

 

The post Black Friday Python Deals Came Early appeared first on Mouse Vs Python.

November 18, 2025 01:41 PM UTC


PyCharm

Open Source in Focus: Projects We’re Proud to Support

November 18, 2025 12:07 PM UTC


PyCon

Join us in “Trailblazing Python Security” at PyCon US 2026

PyCon US 2026 is coming to Long Beach, California! PyCon US is the premiere conference for the Python programming language in North America. Python experts and enthusiasts from around the globe will gather in Long Beach to discuss and learn about the latest developments to the Python programming language and massive ecosystem of Python projects.

New in 2026: Security and AI talk tracks!

Brand new this year are two themed talk tracks: “Trailblazing Python Security” and “Python and the Future of AI”. We want to hear talks from you! The PyCon US Call for Proposals (CFP) for PyCon US 2026 is now open through December 19th, 2025. Don’t wait to submit your talks, the earlier you submit the better.

If your company or organization would like to show support for a more secure Python ecosystem by sponsoring the “Trailblazing Python Security” talk track check out the PyCon US 2026 Sponsor Prospectus or reach out via email to sponsors@python.org. We’ve made three sponsor slots available for the track: one lead sponsor and two co-sponsors, so act fast!

We’re also looking for mentors! If you’re an experienced speaker and want to help someone with their proposal, the PyCon US Proposal Mentorship Program is for you! We typically get twice the number of mentees seeking support than we do for volunteer mentors. Sign up to mentor via this form by November 21, 2025. 

What’s next for Python security?

If you're interested in Python and security: why should you attend PyCon US 2026?

Many Pythonistas use the Open Source software available on the Python Package Index (PyPI). PyCon US is the flagship conference hosted by the Python Software Foundation, the stewards of the Python Package Index. Many brand-new security features are announced and demoed live at PyCon US, such as “PyPI Digital Attestations”, “PyPI Organizations”, and “Trusted Publishers”.

You’ll be among the first Pythonistas to hear about these new features and chat with the developers and maintainers of PyPI.

Open Space about handling vulnerabilities in Python projects

PyCon US always has many opportunities to learn about the latest in Python security. Last year at PyCon US 2025 hosted a “Python Security Mini-Summit” Open Space with speakers discussing the Cyber Resilience Act (CRA), CVE and Open Source, and supply-chain within the Scientific Python community. Expect even more security content this year!

The conference talk schedule includes many talks about using memory-safe systems programming languages like Rust with Python, authentication with popular Web frameworks, how to handle security vulnerabilities as an Open Source project, and how the “Phantom Dependency” problem affects the Python package ecosystem.

We hope you’ll consider joining us in Long Beach, CA for PyCon US 2026. See you there! đŸ”„đŸ•ïž

November 18, 2025 11:17 AM UTC


Seth Michael Larson

BrotliCFFI has two new maintainers

Quick post announcing that the Python package brotlicffi has two new maintainers: Nathan Goldbaum and Christian Clauss. Thank you both for stepping up to help me with this package.

Both these folks (along with a few others) have shown up and gotten straight to work in adding support for Python 3.14, free-threaded Python, and the latest release of Brotli. I’ve given myself the task to change the PyPI publishing workflow to allow a group of contributors to make new releases to mostly get out of their way!

I’m grateful that the project lives under the python-hyper organization which is structured in such a way that allows onboarding new contributors quickly after they’ve shown interest in contributing to a project meaningfully.



Thanks for keeping RSS alive! ♄

November 18, 2025 12:00 AM UTC

November 17, 2025


Rodrigo GirĂŁo SerrĂŁo

Floodfill algorithm in Python

Learn how to implement and use the floodfill algorithm in Python.

What is the floodfill algorithm?

Click the image below to randomly colour the region you click.

Go ahead, try it!

IMG_WIDTH = 160 IMG_HEIGHT = 160 PIXEL_SIZE = 2 import asyncio import collections import random from pyscript import display from pyodide.ffi import create_proxy import js from js import fetch canvas = js.document.getElementById("bitmap") ctx = canvas.getContext("2d") URL = "/blog/floodfill-algorithm-in-python/_python.txt" async def load_bitmap(url: str) -> list[list[int]]: # Fetch the text file from the URL response = await fetch(url) text = await response.text() bitmap: list[list[int]] = [] for line in text.splitlines(): line = line.strip() if not line: continue row = [int(ch) for ch in line if ch in "01"] if row: bitmap.append(row) return bitmap def draw_bitmap(bitmap): rows = len(bitmap) cols = len(bitmap[0]) if rows > 0 else 0 if rows == 0 or cols == 0: return for y, row in enumerate(bitmap): for x, value in enumerate(row): if value == 1: ctx.fillStyle = "black" else: ctx.fillStyle = "white" ctx.fillRect(x * PIXEL_SIZE, y * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE) _neighbours = [(1, 0), (-1, 0), (0, 1), (0, -1)] async def fill_bitmap(bitmap, x, y): if bitmap[y][x] == 1: return ctx = canvas.getContext("2d") r, g, b = (random.randint(0, 255) for _ in range(3)) ctx.fillStyle = f"rgb({r}, {g}, {b})" def draw_pixel(x, y): ctx.fillRect(x * PIXEL_SIZE, y * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE) pixels = collections.deque([(x, y)]) seen = set((x, y)) while pixels: nx, ny = pixels.pop() draw_pixel(nx, ny) for dx, dy in _neighbours: x_, y_ = nx + dx, ny + dy if x_ < 0 or x_ >= IMG_WIDTH or y_ < 0 or y_ >= IMG_HEIGHT or (x_, y_) in seen: continue if bitmap[y_][x_] == 0: seen.add((x_, y_)) pixels.appendleft((x_, y_)) await asyncio.sleep(0.0001) is_running = False def get_event_coords(event): """Return (clientX, clientY) for mouse/pointer/touch events.""" # PointerEvent / MouseEvent: clientX/clientY directly available if hasattr(event, "clientX") and hasattr(event, "clientY") and event.clientX is not None: return event.clientX, event.clientY # TouchEvent: use the first touch point if hasattr(event, "touches") and event.touches.length > 0: touch = event.touches.item(0) return touch.clientX, touch.clientY # Fallback: try changedTouches if hasattr(event, "changedTouches") and event.changedTouches.length > 0: touch = event.changedTouches.item(0) return touch.clientX, touch.clientY return None, None async def on_canvas_press(event): global is_running if is_running: return is_running = True try: # Avoid scrolling / zooming taking over on touch if hasattr(event, "preventDefault"): event.preventDefault() clientX, clientY = get_event_coords(event) if clientX is None: # Could not read coordinates; bail out gracefully return rect = canvas.getBoundingClientRect() # Account for CSS scaling: map from displayed size to canvas units scale_x = canvas.width / rect.width scale_y = canvas.height / rect.height x_canvas = (clientX - rect.left) * scale_x y_canvas = (clientY - rect.top) * scale_y x_idx = int(x_canvas // PIXEL_SIZE) y_idx...

November 17, 2025 03:49 PM UTC


Real Python

How to Serve a Website With FastAPI Using HTML and Jinja2

By the end of this guide, you’ll be able to serve dynamic websites from FastAPI endpoints using Jinja2 templates powered by CSS and JavaScript. By leveraging FastAPI’s HTMLResponse, StaticFiles, and Jinja2Templates classes, you’ll use FastAPI like a traditional Python web framework.

You’ll start by returning basic HTML from your endpoints, then add Jinja2 templating for dynamic content, and finally create a complete website with external CSS and JavaScript files to copy hex color codes:

To follow along, you should be comfortable with Python functions and have a basic understanding of HTML and CSS. Experience with FastAPI is helpful but not required.

Get Your Code: Click here to download the free sample code that shows you how to serve a website with FastAPI using HTML and Jinja2.

Take the Quiz: Test your knowledge with our interactive “How to Serve a Website With FastAPI Using HTML and Jinja2” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

How to Serve a Website With FastAPI Using HTML and Jinja2

Review how to build dynamic websites with FastAPI and Jinja2, and serve HTML, CSS, and JS with HTMLResponse and StaticFiles.

Prerequisites

Before you start building your HTML-serving FastAPI application, you’ll need to set up your development environment with the required packages. You’ll install FastAPI along with its standard dependencies, including the ASGI server you need to run your application.

Select your operating system below and install FastAPI with all the standard dependencies inside a virtual environment:

Windows PowerShell
PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS> python -m pip install "fastapi[standard]"
Shell
$ python -m venv venv
$ source venv/bin/activate
(venv) $ python -m pip install "fastapi[standard]"

These commands create and activate a virtual environment, then install FastAPI along with Uvicorn as the ASGI server, and additional dependencies that enhance FastAPI’s functionality. The standard option ensures you have everything you need for this tutorial, including Jinja2 for templating.

Step 1: Return Basic HTML Over an API Endpoint

When you take a close look at a FastAPI example application, you commonly encounter functions returning dictionaries, which the framework transparently serializes into JSON responses.

However, FastAPI’s flexibility allows you to serve various custom responses besides that—for example, HTMLResponse to return content as a text/html type, which your browser interprets as a web page.

To explore returning HTML with FastAPI, create a new file called main.py and build your first HTML-returning endpoint:

Python main.py
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def home():
    html_content = """
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Home</title>
    </head>
    <body>
        <h1>Welcome to FastAPI!</h1>
    </body>
    </html>
    """
    return html_content

The HTMLResponse class tells FastAPI to return your content with the text/html content type instead of the default application/json response. This ensures that browsers interpret your response as HTML rather than plain text.

Before you can visit your home page, you need to start your FastAPI development server to see the HTML response in action:

Read the full article at https://realpython.com/fastapi-jinja2-template/ »


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

November 17, 2025 02:00 PM UTC

Quiz: How to Serve a Website With FastAPI Using HTML and Jinja2

In this quiz, you’ll test your understanding of building dynamic websites with FastAPI and Jinja2 Templates.

By working through this quiz, you’ll revisit how to return HTML with HTMLResponse, serve assets with StaticFiles, render Jinja2 templates with context, and include CSS and JavaScript for interactivity like copying hex color codes.

If you are new to FastAPI, review Get Started With FastAPI. You can also brush up on Python functions and HTML and CSS.


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

November 17, 2025 12:00 PM UTC


Python Bytes

#458 I will install Linux on your computer

<strong>Topics covered in this episode:</strong><br> <ul> <li><strong>Possibility of a new website for Django</strong></li> <li><strong><a href="https://github.com/slaily/aiosqlitepool?featured_on=pythonbytes">aiosqlitepool</a></strong></li> <li><strong><a href="https://deptry.com?featured_on=pythonbytes">deptry</a></strong></li> <li><strong><a href="https://github.com/juftin/browsr?featured_on=pythonbytes">browsr</a></strong></li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=s2HlckfeBCs' style='font-weight: bold;'data-umami-event="Livestream-Past" data-umami-event-episode="458">Watch on YouTube</a><br> <p><strong>About the show</strong></p> <p>Sponsored by us! Support our work through:</p> <ul> <li>Our <a href="https://training.talkpython.fm/?featured_on=pythonbytes"><strong>courses at Talk Python Training</strong></a></li> <li><a href="https://courses.pythontest.com/p/the-complete-pytest-course?featured_on=pythonbytes"><strong>The Complete pytest Course</strong></a></li> <li><a href="https://www.patreon.com/pythonbytes"><strong>Patreon Supporters</strong></a></li> </ul> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy">@mkennedy@fosstodon.org</a> / <a href="https://bsky.app/profile/mkennedy.codes?featured_on=pythonbytes">@mkennedy.codes</a> (bsky)</li> <li>Brian: <a href="https://fosstodon.org/@brianokken">@brianokken@fosstodon.org</a> / <a href="https://bsky.app/profile/brianokken.bsky.social?featured_on=pythonbytes">@brianokken.bsky.social</a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes">@pythonbytes@fosstodon.org</a> / <a href="https://bsky.app/profile/pythonbytes.fm">@pythonbytes.fm</a> (bsky)</li> </ul> <p>Join us on YouTube at <a href="https://pythonbytes.fm/stream/live"><strong>pythonbytes.fm/live</strong></a> to be part of the audience. Usually <strong>Monday</strong> at 10am PT. Older video versions available there too.</p> <p>Finally, if you want an artisanal, hand-crafted digest of every week of the show notes in email form? Add your name and email to <a href="https://pythonbytes.fm/friends-of-the-show">our friends of the show list</a>, we'll never share it.</p> <p><strong>Brian #1: Possibility of a new website for Django</strong></p> <ul> <li>Current Django site: <a href="https://www.djangoproject.com?featured_on=pythonbytes">djangoproject.com</a></li> <li>Adam Hill’s in progress redesign idea: <a href="https://django-homepage.adamghill.com?featured_on=pythonbytes">django-homepage.adamghill.com</a></li> <li>Commentary in the <a href="https://forum.djangoproject.com/t/want-to-work-on-a-homepage-site-redesign/42909/35?featured_on=pythonbytes">Want to work on a homepage site redesign? discussion</a></li> </ul> <p><strong>Michael #2: <a href="https://github.com/slaily/aiosqlitepool?featured_on=pythonbytes">aiosqlitepool</a></strong></p> <ul> <li>đŸ›ĄïžA resilient, high-performance asynchronous connection pool layer for SQLite, designed for efficient and scalable database operations.</li> <li>About 2x better than regular SQLite.</li> <li>Pairs with <a href="https://github.com/omnilib/aiosqlite?featured_on=pythonbytes">aiosqlite</a></li> <li><code>aiosqlitepool</code> in three points: <ul> <li><strong>Eliminates connection overhead</strong>: It avoids repeated database connection setup (syscalls, memory allocation) and teardown (syscalls, deallocation) by reusing long-lived connections.</li> <li><strong>Faster queries via "hot" cache</strong>: Long-lived connections keep SQLite's in-memory page cache "hot." This serves frequently requested data directly from memory, speeding up repetitive queries and reducing I/O operations.</li> <li><strong>Maximizes concurrent throughput</strong>: Allows your application to process significantly more database queries per second under heavy load.</li> </ul></li> </ul> <p><strong>Brian #3: <a href="https://deptry.com?featured_on=pythonbytes">deptry</a></strong></p> <ul> <li>“deptry is a command line tool to check for issues with dependencies in a Python project, such as unused or missing dependencies. It supports projects using Poetry, pip, PDM, uv, and more generally any project supporting PEP 621 specification.”</li> <li>“Dependency issues are detected by scanning for imported modules within all Python files in a directory and its subdirectories, and comparing those to the dependencies listed in the project's requirements.”</li> <li><p>Note if you use <code>project.optional-dependencies</code></p> <div class="codehilite"> <pre><span></span><code><span class="k">[project.optional-dependencies]</span> <span class="n">plot</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;matplotlib&quot;</span><span class="p">]</span> <span class="n">test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;pytest&quot;</span><span class="p">]</span> </code></pre> </div></li> <li><p>you have to set a config setting to get it to work right:</p> <div class="codehilite"> <pre><span></span><code><span class="k">[tool.deptry]</span> <span class="n">pep621_dev_dependency_groups</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;test&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;docs&quot;</span><span class="p">]</span> </code></pre> </div></li> </ul> <p><strong>Michael #4: <a href="https://github.com/juftin/browsr?featured_on=pythonbytes">browsr</a></strong></p> <ul> <li><strong><code>browsr</code></strong> đŸ—‚ïž is a pleasant <strong>file explorer</strong> in your terminal. It's a command line <strong>TUI</strong> (text-based user interface) application that empowers you to browse the contents of local and remote filesystems with your keyboard or mouse.</li> <li>You can quickly navigate through directories and peek at files whether they're hosted <strong>locally</strong>, in <strong>GitHub</strong>, over <strong>SSH</strong>, in <strong>AWS S3</strong>, <strong>Google Cloud Storage</strong>, or <strong>Azure Blob Storage</strong>.</li> <li>View code files with syntax highlighting, format JSON files, render images, convert data files to navigable datatables, and more.</li> </ul> <p><strong>Extras</strong></p> <p>Brian:</p> <ul> <li>Understanding the MICRO</li> <li>TDD chapter coming out later today or maybe tomorrow, but it’s close.</li> </ul> <p>Michael:</p> <ul> <li><a href="https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock&featured_on=pythonbytes">Peacock</a> is excellent</li> </ul> <p><strong>Joke: <a href="https://x.com/thatstraw/status/1977317574779048171?featured_on=pythonbytes">I will find you</a></strong></p>

November 17, 2025 08:00 AM UTC

November 16, 2025


Ned Batchelder

Why your mock breaks later

In Why your mock doesn’t work I explained this rule of mocking:

Mock where the object is used, not where it’s defined.

That blog post explained why that rule was important: often a mock doesn’t work at all if you do it wrong. But in some cases, the mock will work even if you don’t follow this rule, and then it can break much later. Why?

Let’s say you have code like this:

# user.py

def get_user_settings():
    with open(Path("~/settings.json").expanduser()) as f:
        return json.load(f)

def add_two_settings():
    settings = get_user_settings()
    return settings["opt1"] + settings["opt2"]

You write a simple test:

def test_add_two_settings():
    # NOTE: need to create ~/settings.json for this to work:
    #   {"opt1": 10, "opt2": 7}
    assert add_two_settings() == 17

As the comment in the test points out, the test will only pass if you create the correct settings.json file in your home directory. This is bad: you don’t want to require finicky environments for your tests to pass.

The thing we want to avoid is opening a real file, so it’s a natural impulse to mock out open():

# test_user.py

from io import StringIO
from unittest.mock import patch

@patch("builtins.open")
def test_add_two_settings(mock_open):
    mock_open.return_value = StringIO('{"opt1": 10, "opt2": 7}')
    assert add_two_settings() == 17

Nice, the test works without needing to create a file in our home directory!

Much later...

One day your test suite fails with an error like:

...
  File ".../site-packages/coverage/python.py", line 55, in get_python_source
    source_bytes = read_python_source(try_filename)
  File ".../site-packages/coverage/python.py", line 39, in read_python_source
    return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
TypeErrorreplace() argument 1 must be str, not bytes

What happened!? Coverage.py code runs during your tests, invoked by the Python interpreter. The mock in the test changed the builtin open, so any use of it anywhere during the test is affected. In some cases, coverage.py needs to read your source code to record the execution properly. When that happens, coverage.py unknowingly uses the mocked open, and bad things happen.

When you use a mock, patch it where it’s used, not where it’s defined. In this case, the patch would be:

@patch("myproduct.user.open")
def test_add_two_settings(mock_open):
    ... etc ...

With a mock like this, the coverage.py code would be unaffected.

Keep in mind: it’s not just coverage.py that could trip over this mock. There could be other libraries used by your code, or you might use open yourself in another part of your product. Mocking the definition means anything using the object will be affected. Your intent is to only mock in one place, so target that place.

Postscript

I decided to add some code to coverage.py to defend against this kind of over-mocking. There is a lot of over-mocking out there, and this problem only shows up in coverage.py with Python 3.14. It’s not happening to many people yet, but it will happen more and more as people start testing with 3.14. I didn’t want to have to answer this question many times, and I didn’t want to force people to fix their mocks.

From a certain perspective, I shouldn’t have to do this. They are in the wrong, not me. But this will reduce the overall friction in the universe. And the fix was really simple:

open = open

This is a top-level statement in my module, so it runs when the module is imported, long before any tests are run. The assignment to open will create a global in my module, using the current value of open, the one found in the builtins. This saves the original open for use in my module later, isolated from how builtins might be changed later.

This is an ad-hoc fix: it only defends one builtin. Mocking other builtins could still break coverage.py. But open is a common one, and this will keep things working smoothly for those cases. And there’s precedent: I’ve already been using a more involved technique to defend against mocking of the os module for ten years.

Even better!

No blog post about mocking is complete without encouraging a number of other best practices, some of which could get you out of the mocking mess:

November 16, 2025 12:55 PM UTC

November 15, 2025


Kay Hayen

Nuitka Release 2.8

This is to inform you about the new stable release of Nuitka. It is the extremely compatible Python compiler, “download now”.

This release adds a ton of new features and corrections.

Bug Fixes

Package Support

New Features

Optimization

Anti-Bloat

Organizational

Tests

Cleanups

Summary

This release was supposed to focus on scalability, but that didn’t happen again due to a variety of important issues coming up as well as a created downtime after high private difficulties after a planned surgery. However, the upcoming release will have it finally.

The onefile DLL mode as used on Windows has driven a lot of need for corrections, some of which are only in the final release, and this is probably the first time it should be usable for everything.

For compatibility, working with the popular (yet - not yes recommended UV-Python), Windows UI fixes for temporary onefile and macOS improvements, as well as improved Android support are excellent.

The next release of Nuitka however will have to focus on scalability and maintenance only. But as usual, not sure if it can happen.

November 15, 2025 01:52 PM UTC

November 14, 2025


Real Python

The Real Python Podcast – Episode #274: Preparing Data Science Projects for Production

How do you prepare your Python data science projects for production? What are the essential tools and techniques to make your code reproducible, organized, and testable? This week on the show, Khuyen Tran from CodeCut discusses her new book, "Production Ready Data Science."


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

November 14, 2025 12:00 PM UTC


EuroPython Society

Recognising Michael Foord as an Honorary EuroPython Society Fellow

Hi everyone. Today, we are honoured to announce a very special recognition.

The EuroPython Society has posthumously elected Michael Foord (aka voidspace) as an Honorary EuroPython Society Fellow.


Michael Foord (1974–2025)

Michael was a long-time and deeply influential member of the Python community. He began using Python in 2002, became a Python core developer, and left a lasting mark on the language through his work on unittest and the creation of the mock library. He also started the tradition of the Python Language Summits at PyCon US, and he consistently supported and connected the Python community across Europe and beyond.

However, his legacy extends far beyond code. Many of us first met Michael through his writing and tools, but what stayed with people was the example he set through his contributions, and how he showed up for others. He answered questions with patience, welcomed newcomers, and cared about doing the right thing in small, everyday ways. He made space for people to learn. He helped the Python community in Europe grow stronger and more connected. He made our community feel like a community.

His impact was celebrated widely across the community, with many tributes reflecting his kindness, humour, and dedication:

At EuroPython 2025, we held a memorial and kept a seat for him in the Forum Hall:


A lasting tribute

EuroPython Society Fellows are people whose work and care move our mission forward. By naming Michael an Honorary Fellow, we acknowledge his technical contributions and also the kindness and curiosity that defined his presence among us. We are grateful for the example he set, and we miss him.

Our thoughts and thanks are with Michael&aposs friends, collaborators, and family. His work lives on in our tools. His spirit lives on in how we treat each other.

With gratitude,
Your friends at EuroPython Society

November 14, 2025 09:00 AM UTC

November 13, 2025


Paolo Melchiorre

How to use UUIDv7 in Python, Django and PostgreSQL

Learn how to use UUIDv7 today with stable releases of Python 3.14, Django 5.2 and PostgreSQL 18. A step by step guide showing how to generate UUIDv7 in Python, store them in Django models, use PostgreSQL native functions and build time ordered primary keys without writing SQL.

November 13, 2025 11:00 PM UTC


Python Engineering at Microsoft

Python in Visual Studio Code – November 2025 Release

We’re excited to announce that the November 2025 release of the Python extension for Visual Studio Code is now available!

This release includes the following announcements:

If you’re interested, you can check the full list of improvements in our changelogs for the Python and Pylance extensions.

Add Copilot Hover Summaries as docstring

You can now add your AI-generated documentation directly into your code as a docstring using the new Add as docstring command in Copilot Hover Summaries. When you generate a summary for a function or class, navigate to the symbol definition and hover over it to access the Add as docstring command, which inserts the summary below your cursor formatted as a proper docstring.

This streamlines the process of documenting your code, allowing you to quickly enhance readability and maintainability without retyping.

Add as docstring command in Copilot Hover Summaries

Localized Copilot Hover Summaries

GitHub Copilot Hover Summaries inside Pylance now respect your display language within VS Code. When you invoke an AI-generated summary, you’ll get strings in the language you’ve set for your editor, making it easier to understand the generated documentation.

Copilot Hover Summary generated in Portuguese

Convert wildcard imports into Code Action

Wildcard imports (from module import *) are often discouraged in Python because they can clutter your namespace and make it unclear where names come from, reducing code clarity and maintainability. Pylance now helps you clean up modules that still rely on from module import * via a new Code Action. It replaces the wildcard with the explicit symbols, preserving aliases and keeping the import to a single statement. To try it out, you can click on the line with the wildcard import and press Ctrl + . (or Cmd + . on macOS) to select the Convert to explicit imports Code Action.

Convert wildcard imports Code Action

Debugger support for multiple interpreters via the Python Environments Extension

The Python Debugger extension now leverages the APIs from the Python Environments Extension (vscode-python-debugger#849). When enabled, the debugger can recognize and use different interpreters for each project within a workspace. If you have multiple folders configured as projects—each with its own interpreter – the debugger will now respect these selections and use the interpreter shown in the status bar when debugging.

To enable this functionality, set “python.useEnvironmentsExtension”: true in your user settings. The new API integration is only active when this setting is turned on.

Please report any issues you encounter to the Python Debugger repository.

Other Changes and Enhancements

We have also added small enhancements and fixed issues requested by users that should improve your experience working with Python in Visual Studio Code. Some notable changes include:

We would also like to extend special thanks to this month’s contributors:

Try out these new improvements by downloading the Python extension from the Marketplace, or install them directly from the extensions view in Visual Studio Code (Ctrl + Shift + X or ⌘ + ⇧ + X). You can learn more about Python support in Visual Studio Code in the documentation. If you run into any problems or have suggestions, please file an issue on the Python VS Code GitHub page.

The post Python in Visual Studio Code – November 2025 Release appeared first on Microsoft for Python Developers Blog.

November 13, 2025 06:41 PM UTC

November 12, 2025


Python Software Foundation

Python is for everyone: Join in the PSF year-end fundraiser & membership drive!

The Python Software Foundation (PSF) is the charitable organization behind Python, dedicated to advancing, supporting, and protecting the Python programming language and the community that sustains it. That mission and cause are more than just words we believe in. Our tiny but mighty team works hard to deliver the projects and services that allow Python to be the thriving, independent, community-driven language it is today. Some of what the PSF does includes producing PyCon US, hosting the Python Package Index (PyPI), supporting 5 Developers-in-Residence, maintaining critical community infrastructure, and more.

Python is for teaching, learning, playing, researching, exploring, creating, working– the list goes on and on and on! Support this year's fundraiser with your donations and memberships to help the PSF, the Python community, and the language stay strong and sustainable. Because Python is for everyone, thanks to you.

There are two direct ways to join through donate.python.org

 

>>> Donate or Become a Member Today! <<<

 

If you already donated and/or you’re already a member, you can:

 

Your donations and support:

 

Highlights from 2025:

November 12, 2025 05:03 PM UTC