28 Jul 2026

feedPlanet Python

PyCoder’s Weekly: Issue #745: PyPI UI, Finding Classes with the GC, pylock.toml, and More (2026-07-28)

#745 - JULY 28, 2026
View in Browser »

The PyCoder’s Weekly Logo


Planned Updates to the PyPI User Interface

Over the next few months a new user interface will be rolled out for the Python packaging website, PyPI. The rollout will be done in phases to make sure it is rock solid and to get community feedback. This post talks about the history of PyPI's UI and what is changing.
NICOLE HARRIS

Find All Instances of a Class With gc.get_objects()

If you're debugging a situation with multiple references to an object and you want to hunt down all instances, the garbage collector module can help you out.
ADAM JOHNSON

Let AI Agents Into Your B2B App. Securely

alt

More of your users are asking to connect AI agents to your product, and you want to say yes. PropelAuth lets you give each agent scoped, revocable access, so you stay in control of what it can do. Learn More →
PROPELAUTH sponsor

Tool-Agnostic Python Lock Files With PEP 751 and pylock.toml

Learn how PEP 751 standardizes Python lock files with pylock.toml: generate one with pip or uv, install it with uv or pdm, and retire requirements.txt.
REAL PYTHON

Quiz: Tool-Agnostic Python Lock Files With pylock.toml

REAL PYTHON

Django 6.1 Release Candidate 1 Released

DJANGO SOFTWARE FOUNDATION

Articles & Tutorials

A Versatile LLM Harness & Scraping the Web With Scrapy

Which is more important, the model or the "harness" around an LLM? What are ways to assemble an efficient agentic developer workflow? This week on the show, Ayan Pahwa joins us to discuss harnessing, web scraping, and self-hosting Python applications.
REAL PYTHON podcast

Pip 26.2: -only-deps Solves Years of Deployment Hacks

When working with scripts and simpler projects, sometimes you need dependencies installed without the full package. There have been work arounds for years, but now pip 26.2 has a new flag to support this.
JAMES O'CLAIRE

[Registration Closing] Claude Code for Python Developers

alt

By Sunday evening, you'll have built, debugged, and shipped a complete Python project with an AI agent, and you'll know how to bring that agentic engineering workflow to your own codebase on Monday. Live on August 1-2, doors close this Friday. Claim Your Spot →
REAL PYTHON sponsor

PyPI Releases Now Reject New Files After 14 Days

"The Python Package Index (PyPI) now rejects new files being uploaded to releases that are older than 14 days. This restriction was put in place to prevent old and long-stable releases from being poisoned"
PYPI.ORG

Nifty Django Feature: Form Templates

Form templates in Django allow you to make reusable pieces for forms, giving a separation between the view's template and how the form gets rendered.
TIM SCHILLING

FastAPI: Python API Development With Light Speed

Learn FastAPI from the ground up. Build REST APIs, serve web pages with Jinja2 templates, and create a complete URL shortener project in Python.
REAL PYTHON

Quiz: FastAPI: Python API Development With Light Speed

REAL PYTHON

Using NumPy reshape() to Change the Shape of an Array

Learn how to use NumPy reshape() in Python to change an array's shape, add or remove dimensions, and control how the data is rearranged.
REAL PYTHON

Quiz: Using NumPy reshape() to Change the Shape of an Array

REAL PYTHON

Security: Line Goes Up

CPython is experiencing a huge increase in security reports. This post talks about why that is happening and how it is being handled.
HUGO VAN KEMENADE

What Our AI Guiding Principles Actually Mean

Wagtail's five AI principles, from policy / guidelines to practice and how they steer responsible AI adoption for the project.
THIBAUD COLAS

Exploring Python's Built-in Functions

Learn Python's built-in functions for math, data types, iterables, and I/O, and when to use each to write more Pythonic code.
REAL PYTHON course

Quiz: Exploring Python's Built-in Functions

REAL PYTHON

Projects & Code

django-query-doctor: Diagnose Slow Django Queries

GITHUB.COM/HASSANZAIBHAY

ast-explore: Explore the AST of Your Python

GITHUB.COM/STEFMOLIN • Shared by Stefanie Molin

interlock: Circuit Breaker On Failure Rate and Latency

GITHUB.COM/BAGOWIX • Shared by Bogdan Galushko

tsauditor: Statistical Auditor for Temporal Data Leakage

GITHUB.COM/IMANN128 • Shared by Iman Naeem

darnlink: Fix Relative Markdown Links When Files Move

GITHUB.COM/TXEMI • Shared by txemi

Events

Weekly Real Python Office Hours Q&A (Virtual)

July 29, 2026
REALPYTHON.COM

Melbourne Python Users Group, Australia

August 3, 2026
J.MP

PyBodensee Monthly Meetup

August 3, 2026
PYBODENSEE.COM

STL Python

August 6, 2026
MEETUP.COM

Canberra Python Meetup

August 6, 2026
MEETUP.COM

Sydney Python User Group (SyPy)

August 6, 2026
SYPY.ORG

PyCon Indonesia 2026

August 8 to August 10, 2026
PYCON.ID


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

28 Jul 2026 7:30pm GMT

Python Software Foundation: Announcing a 2026 PSF Grants Program Funding Round

The Python Software Foundation (PSF) is excited to announce a 2026 PSF Grants Program funding round. This is not a full reopening of the Grants Program as it existed before. Rather, it's what the PSF is able to sustainably offer right now, given where we stand financially and operationally. The PSF Board, PSF Staff, and PSF Grants Work Group (GWG) are deeply passionate about the program and understand how important it is to the Python community. It's our honor to have the opportunity to disburse grant funding in 2026.

In keeping with our focus on sustainability, this round of grant funding has a set budget capped at $90,000 USD, a limited scope, and a different structure and timeline for applying and reviewing. We will be accepting applications from August 4 - 25 AoE, for Conferences and Workshops that are scheduled between December 1, 2026, and April 30, 2027. Our top priority is getting available funds to the regional communities who need it most: those who have had to pause their events and initiatives because of lack of funding from PSF Grants or loss of sponsors.

Context

As folks following along with the PSF may remember, the last couple of years have been financially challenging, with the PSF's assets and yearly revenue declining and costs increasing across the board. At the same time, the demand for our work has continued to multiply. Making the decision to pause the Grants Program last year was difficult, but a necessary step to protect both the future of the program and the short- and long-term sustainability of the PSF.

The PSF acknowledges the pause created challenging situations for the many community groups that had planned to apply for the grants program. We also recognize and appreciate the community's support-both in response to the announcement of the pause and through the outstanding results of the 2025 end-of-year fundraiser.

The Python community showed up with understanding and solidarity when the pause happened and helped us come up with ideas on how the PSF could serve the community in non-financial ways. Those ideas were the seeds that grew into the PSF Community Partner Program, a non-monetary partnership offered to qualifying applicants. This program assists Community Partners by attaching the PSF name to the event or initiative, which lends credibility, helps attract sponsors, and provides promotional support through reposts on PSF social media accounts.

Funding Round Eligibility, Caps, and Criteria

Eligibility Timeframe

The 2026 Grants Program Funding Round will be narrowly scoped to Conferences and Workshops of all types that are scheduled between December 1, 2026, and April 30, 2027. If all goes well, the PSF intends to run future rounds of funding, so please do not be discouraged if your event doesn't fit within this time frame. This time frame reflects the PSF's current finances, our staff capacity, and our goal of getting funds to recipients while they're useful.

Categories of grants that will be considered (includes virtual):

The PSF also wants to acknowledge that this timeframe may exclude some events and initiatives that also missed out on funding in 2025. Please know exclusion is not our intent. If we are able to offer later rounds, we plan to prioritize events and initiatives that missed out on funding in 2025 and 2026 due to the timing windows. Getting the program back up and running is a lot of work for our small team, and we have experienced significant staffing changes in the last year. These changes have made it harder to keep pace with our regular activities, let alone get the Grants Program up and running again. What felt the most important was getting at least some funds out, even if we couldn't kick the program off right at the same time of year it was paused last year.

Adjustments to Grant Category Caps

The 2026 Grants Program Funding Round will adjust the cap for Conference type grants down to $2,000 USD and maintain the Workshop type grant cap at $1,500 USD. This change reflects a focus on supporting hyper-local communities that had to halt their activities due to the PSF Grants Program pause.

The PSF has observed, through social media, Grants Program Office Hours, and informal conversations, that many large and long-standing international PyCons are still taking place without PSF Grants, while workshops and smaller regional initiatives have completely paused or slowed down significantly. Based on these observations, the PSF estimates that $1,500 will make an impact for those workshops and $2000 could help fill in some gaps in PyCon budgets. Our hope is to empower as many groups as possible with this round of funding.

Please note that the caps are the maximum amount applicants can request. If you don't need that amount, please ask for less. The guidelines the Grants Work Group observes are generally as follows:

Notes on Scope, Criteria, and Communication

The PSF wants to highlight that consolidated grant types will not be considered during the 2026 Grants Funding Round. While this was a great addition for when the Grants Program was running on a rolling basis, for this limited funding round, the PSF Grants Work Group needs to look at applications on a singular level. We ask that communities that previously submitted consolidated grants submit individual applications for up to 5 conferences or workshops that are scheduled to take place during the eligibility timeframe.

All previous criteria and guidelines for the PSF Grants Program will be applied to this funding round. This post won't go over every single piece of information required on the application, but we want to highlight a couple of things:

Grants Funding Round Schedule

Listed in the table below is the anticipated schedule for the 2026 PSF Grants Program Funding Round. The timeline is tight (applications open next week!), but our team hopes that three weeks to get applications in is reasonable and accommodates events and initiatives that fall in the eligibility timeframe.

Date Phase Description
August 4 - 25 AoE Application Applications open; PSF Staff performs initial reviews as applications are received; any missing information is collected
August 25 - September 11 Review Grants Work Group review; clarifying information collected as needed; Grants Work Group votes
September 14 Decision Decisions communicated to all applicants
September 14 and onwards Disbursement Funds disbursed

.table { display: block; overflow-y: hidden; overflow-x: auto; scroll-behavior: smooth; } .table table { table-layout: auto; border-collapse: collapse; } .table thead { display: table-header-group; vertical-align: middle; border-color: inherit; color: white; background: darkcyan; } .table tr { display: table-row; vertical-align: inherit; border-color: inherit; } .table th { padding: 16px; text-align: inherit; border-bottom: 1px solid black; color: white !important; white-space: nowrap; } .table td:nth-child(2) { white-space: nowrap; padding: 16px; } .table td { padding: 16px; border-bottom: 1px solid #ddd; } .table tbody { display: table-row-group; vertical-align: middle; border-color: inherit; } .table table:not(.tr-caption-container) { min-width: 100%; border-radius: 3px; }

After things kick off, the PSF may need to adjust dates by a couple days here and there. This program is dependent on just a couple of staff (Hi, Marie and Laura!) and our wonderful Grants Work Group (Thank you, team!) that is composed of volunteers. If dates need to be adjusted, we will be sure to communicate that in multiple places (Emails direct to applicants, Discuss, PSF Discord, and PSF social media accounts: LinkedIn, Mastodon, Bluesky, X).

The PSF asks that applicants closely monitor their emails from the point they submit their application to the end of the review phase. We would be disappointed to see events and initiatives miss out on grant funding due to gaps in their application. The more responsive applicants can be, the better!

How to Apply

Submit your applications via the PSF Grants Program application form. Before August 4 and after August 25, the form is still available but only taking applications for the PSF's Meetup Pro Network.

Questions or feedback?

Phew-that was a lot of information! The PSF expects questions about the 2026 Grants Program Funding Round. In fact, there may be things we've overlooked, and we would appreciate you sharing anything you think we're missing. Your feedback will help us improve during the process and for future rounds. There are multiple ways for you to reach out to us with your questions, feedback, and comments:


Due to the accelerated nature of this grants funding round, we are holding supplemental PSF Grants Program Office Hours on the PSF Discord:

Check out what times these are for you using this timezone converter. We welcome you to join us to ask your questions, discuss the process, suggest ideas for future rounds, or anything else related to the PSF Grants Program.

Final Thoughts and Thanks

This is a big change for the PSF Grants Program. It's moved from a rolling basis, to a pause, and now to a limited window to receive, review, and make decisions about applications. Will the process be perfectly smooth? Probably not. But we are committed to doing it as efficiently as possible, keeping the community and applicants informed of any changes, and when possible, integrating feedback we receive throughout the process.

The PSF also wants to thank you, the Python community, for your understanding and generous backing, in actions, words, and donations. We could not fulfill our mission without the community's support and without each individual out there championing the PSF's work. The PSF is so very grateful to be in community with each and every one of you.

About the Python Software Foundation

The Python Software Foundation is a US non-profit whose mission is to promote, protect, and advance the Python programming language, and to support and facilitate the growth of a diverse and international community of Python programmers. The PSF supports the Python community using corporate sponsorships, grants, and donations. Are you interested in sponsoring or donating to the PSF so we can continue supporting Python and its community? Check out our sponsorship program, donate directly, or contact our team at sponsors@python.org

28 Jul 2026 8:18am GMT

Python Bytes: #490 It’s a vibe coding party

<strong>Topics covered in this episode:</strong><br> <ul> <li><strong><a href="https://jvns.ca/blog/2026/07/21/more-nice-django-things/?featured_on=pythonbytes">Some more things about Django I've been enjoying</a></strong></li> <li><strong><a href="https://www.ft.com/content/cec8df9e-b43b-4cd1-8feb-c07e804e8d33?featured_on=pythonbytes">Who cleans up after the vibe-coding party</a>?</strong></li> <li><strong>Where Did All Your AI Tokens Go? <a href="https://github.com/kenn-io/agentsview?featured_on=pythonbytes">AgentsView</a> to the rescue!</strong></li> <li><strong>Careful with phishing all</strong></li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=fVWWd7zvcTg' style='font-weight: bold;'data-umami-event="Livestream-Past" data-umami-event-episode="490">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</strong></a></li> <li>Consulting from <a href="https://sixfeetup.com/?featured_on=pythonbytes"><strong>Six Feet Up</strong></a></li> </ul> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy">Mastodon</a> / <a href="https://bsky.app/profile/mkennedy.codes?featured_on=pythonbytes">BlueSky</a> / <a href="https://x.com/mkennedy?featured_on=pythonbytes">X</a> / <a href="https://www.linkedin.com/in/mkennedy/?featured_on=pythonbytes">LinkedIn</a></li> <li>Calvin: <a href="https://sixfeetup.social/@calvin?featured_on=pythonbytes">Mastodon</a> / <a href="https://bsky.app/profile/calvinhp.com?featured_on=pythonbytes">BlueSky</a> / <a href="https://x.com/calvinhp?featured_on=pythonbytes">X</a> / <a href="https://www.linkedin.com/in/calvinhp/?featured_on=pythonbytes">LinkedIn</a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes">Mastodon</a> / <a href="https://bsky.app/profile/pythonbytes.fm">BlueSky</a> / <a href="https://x.com/PythonBytes?featured_on=pythonbytes">X</a></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>Tuesday at 7am PT</strong>. 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>Calvin #1: <a href="https://jvns.ca/blog/2026/07/21/more-nice-django-things/?featured_on=pythonbytes">Some more things about Django I've been enjoying</a></strong></p> <ul> <li><strong>Julia Evans</strong> is learning "2010-style" web dev (Django + SQL + server-rendered HTML) after years of Go backends and JS-heavy frontends</li> <li><strong>Query builders</strong>: likes defining custom QuerySet classes with chainable filter methods (<code>.approved().future().with_tags()</code>) - more readable than raw SQL</li> <li><strong>Template filters</strong>: highlights <code>urlize</code>, <code>linebreaksbr</code>, <code>json_script</code>, and especially <code>querystring</code> for building/modifying query-string links in templates</li> <li><strong>Migrations</strong>: still loves Django's auto-generated migrations - 19 and counting on her project</li> <li><strong>Skips inheritance</strong> for class-based views; prefers function-based views for sharing code, though fine using Django's own mixins/interfaces</li> <li><strong>Performance surprise</strong>: CPU profiling (via <code>py-spy</code>) - not slow DB queries - revealed the culprit; she'd accidentally disabled the cached template loader, and re-enabling it took throughput from ~2-3 req/s to ~12 req/s on a $10/mo VM</li> </ul> <p><strong>Michael #2: <a href="https://www.ft.com/content/cec8df9e-b43b-4cd1-8feb-c07e804e8d33?featured_on=pythonbytes">Who cleans up after the vibe-coding party</a>?</strong></p> <p>FT Magazine piece by Sam Learner (July 11) on AI coding tools overwhelming open source maintainers - sent in by listener Dylan McConnell, whose main point was that this ran in the <em>Financial Times</em>, not a dev blog.</p> <ul> <li><strong>cURL as the case study</strong> - Daniel Stenberg has been the only full-time person on it for years; libcurl has been installed an estimated 20+ billion times with 3,000+ listed contributors.</li> <li><strong>Bug bounty killed</strong> - cURL ended its paid security bounty program in January, citing an "explosion of AI slop reports" that take real time to debunk and drain morale.</li> <li><strong>Extractive contributions</strong> - authoring a PR is now nearly free, reviewing one still costs a human; tldraw's Steve Ruiz closed outside contributions entirely, asking why he'd want someone else writing the easy part.</li> <li><strong>Guido weighs in</strong> - van Rossum says projects are holding emergency meetings over the slop flow, and notes LLM patches tend to touch unrelated parts of a file, making review more tedious.</li> <li><strong>"Vibe Coding Kills Open Source"</strong> - paper from Miklós Koren's group: packages frequently recommended by coding models saw big download jumps with no matching engagement, breaking the reputation loop that sustains maintainers.</li> <li><strong>Stack Overflow flatlined</strong> - over 100,000 questions a month before ChatGPT, under 1,500 last month, with the response rate cut roughly in half; the public archive is now stale training data.</li> <li><strong>The course-creator angle</strong> - Josh Comeau's newest web dev course launched at about a third of prior enrollment, and he worries about devs who never learn which questions to ask.</li> </ul> <p>But <strong>the most interesting portion is what was omitted</strong>.</p> <ul> <li>Focused on: <a href="https://daniel.haxx.se/blog/2026/01/26/the-end-of-the-curl-bug-bounty/?featured_on=pythonbytes">The end of the curl bug-bounty</a></li> <li>Omitted: <a href="https://daniel.haxx.se/blog/2026/04/22/high-quality-chaos/?featured_on=pythonbytes">High-Quality Chaos</a></li> </ul> <p><strong>Why the omission is interesting</strong></p> <ul> <li>It fits a narrative. The FT piece is a maintenance-and-decline story, and January-Stenberg is a perfect witness for it. April-Stenberg complicates it - same person, same project, better data, opposite direction on the specific claim being used.</li> <li>The tell is already in the article. Learner quotes Stenberg saying AI tools are much better at finding problems than fixing them. That's the April thesis in one line, and it goes undeveloped.</li> <li>Reason for the shift is process, not vibes. Killing the bounty removed the cash incentive and the venue change filtered the rest. Worth saying out loud, because "AI reports got better" isn't quite it - "no bounty plus a real triage platform" is closer.</li> </ul> <p>Joke too: Sarah O'Connor <a href="https://blobs.pythonbytes.fm/recommended-on-ai-coding-takovers.jpeg?cache_id=0a8041">wrote a related piece</a> (is this just before skynet launches?)</p> <p><strong>Calvin #3: Where Did All Your AI Tokens Go? <a href="https://github.com/kenn-io/agentsview?featured_on=pythonbytes">AgentsView</a> to the rescue!</strong></p> <ul> <li>Local-first desktop/web app for browsing, searching, and analyzing your past AI coding agent sessions (Claude Code, Codex, Copilot, Cursor, Gemini, Aider, and dozens more)</li> <li>Auto-discovers session files on your machine - no config needed; everything stored locally in SQLite, no cloud/accounts</li> <li><code>agentsview usage</code> is a drop-in <code>ccusage</code> alternative - reads from pre-indexed SQLite, reports run 80-220× faster on large histories</li> <li>New <strong>Activity</strong> dashboard shows peak concurrency, active vs. idle time, agent-minutes, and cost - filterable by project/agent/machine, with a <code>-json</code> CLI report too</li> <li>Full-text + optional semantic search across every session; also imports <a href="http://Claude.ai/ChatGPT?featured_on=pythonbytes">Claude.ai/ChatGPT</a> chat exports</li> <li>Install via <code>pip install agentsview</code>, <code>uvx agentsview</code>, <code>brew install --cask agentsview</code>, or download desktop binaries from GitHub Releases</li> </ul> <p><strong>Michael #4: Careful with phishing all</strong></p> <p><strong>The situation</strong></p> <p>I pass this along because it was a pretty sneaky bit of targeted phishing, and happened to play off an old interaction in bandit's repo. As usual with phishing scams there are a bunch of tells that this isn't legitimate, but just enough plausibility that I could see falling for it in a weak moment. Relative nobodies like me haven't historically been worth the effort to hit with scams this specific. Agents change the game though :-/. Be careful out there folks!</p> <p><strong>Original message</strong></p> <p>From: "Patrick (Blacktrace)" [HTML_REMOVED] To: LISTENER EMAIL Subject: Your Bandit #1350 (B105 NextToken false positive) -- just fixed that exact case</p> <p>Date: Wednesday, July 15, 2026 12:02 AM</p> <p>Hi AJ,</p> <p>Saw your Bandit issue #1350 -- the B105 hardcoded-password false positive on the string NextToken. I build a deterministic gate that filters that class of Bandit noise, and #1350 was literally the case I just fixed: NextToken / next_token / page_token / nextPageToken now stay quiet, while a genuine hardcoded token like api_token="sk-live-..." still fires. Verified against your exact case.</p> <p>30-second paste: https://blacktrace.co/noise-eraser</p> <p>Where it still trips, published: https://blacktrace.co/kruc</p> <p>Curious whether it clears what you hit -- and if it trips on something of yours, that's the more useful reply.</p> <ul> <li>Patrick, Blacktrace</li> </ul> <hr /> <p>I asked Claude for some analysis too. It was pretty good at finding them.</p> <p>The message name-drops enough real detail to feel legit, but the structure is pure phishing - everything in it exists to get AJ onto <a href="http://blacktrace.co?featured_on=pythonbytes">blacktrace.co</a>. The strongest ones:</p> <ul> <li><strong>Freemail sender, corporate signoff.</strong> Signs as "Patrick, Blacktrace" but sends from <a href="mailto:emailpjv@gmail.com">emailpjv@gmail.com</a>. Real company outreach comes from the company domain, not a personal Gmail - and there's no last name.</li> <li><strong>Over-specific targeting.</strong> It mirrors AJ's exact public activity - issue #1350, the B105 rule, the NextToken false positive, even the token variants. That's the "just enough plausibility" AJ flagged, and it's exactly what agents make cheap: scrape a GitHub issue, auto-generate tailored bait. Legit cold outreach rarely reads your history back to you this precisely.</li> <li><strong>The entire payload is two links.</strong> Strip the technical flattery and the message is just "paste here" plus "see results here." When the whole point of an email is the click, that's the tell.</li> <li><strong>"30-second paste."</strong> Low-friction urgency, and "paste" most likely means paste your source into their tool - handing your code to a stranger's site. Exfiltration dressed as convenience.</li> <li><strong>Brand-new, no-reputation domain.</strong> <a href="http://blacktrace.co?featured_on=pythonbytes">blacktrace.co</a> has no track record, and the name is doing some ominous work. The <code>/kruc</code> slug is random noise, not how real product pages get named.</li> <li><strong>Precise-sounding jargon that's actually vague.</strong> "Deterministic gate," "noise-eraser" - impressive, empty. Bolted onto correct real details (B105 is the Bandit hardcoded-password test, <code>sk-live-</code> is a Stripe live-key prefix) to borrow credibility.</li> <li><strong>The disarming close.</strong> "if it trips on something of yours, that's the more useful reply" - engineered humility that flatters your expertise and baits a response. Makes engaging feel like you're doing <em>them</em> a favor, which drops your guard.</li> </ul> <p><strong>Extras</strong></p> <p>Calvin:</p> <ul> <li><a href="https://2026.djangocon.us/?featured_on=pythonbytes">DjangoCon US 2026</a> is rapidly approaching, <strong>August 24-28, Chicago</strong></li> <li><strong>Ruff v0.16.0 massively expands its default rule set</strong> <ul> <li>Ruff now enables 413 rules by default, up from 59</li> <li>https://astral.sh/blog/ruff-v0.16.0</li> </ul></li> </ul> <p>Michael:</p> <ul> <li>Completely <a href="https://pythonbytes.fm">redesigned the home page</a>.</li> <li>Try /insights in Claude Code (terminal)</li> </ul> <p><strong>Joke:</strong> <a href="https://x.com/pr0grammerhum0r/status/2013325616951517233?s=46&featured_on=pythonbytes">We're Safe</a></p>

28 Jul 2026 8:00am GMT

24 Jul 2026

feedDjango community aggregator: Community blog posts

Issue 347: Django 6.1 release candidate 1 released

News

Django 6.1 release candidate 1 released

This is the final opportunity to try out the new version before Django 6.1 is released. Try it, run your test suite, and report anything that breaks!

The DjangoCon US 2026 schedule has been released!

The talk lineup is out, covering Django 6.0 and 6.1 features, modern deployment patterns, GeoDjango at scale, and lightning talks across all three days.

PyPI Releases now reject new files after 14 days

PyPI will reject new files uploaded to releases older than 14 days to limit the impact of compromised publishing tokens or workflows.

Planned Updates to the PyPI User Interface

PyPI's first UI refresh since 2018 will roll out in phases over the coming months, surfacing more security signals on package pages. The first phase is staged on TestPyPI now and ready for your feedback.


Wagtail CMS News

What our AI guiding principles actually mean

Wagtail unpacks its refreshed AI guiding principles and how they steer adoption in practice, starting with a firm commitment: no AI dependency in Wagtail core, with AI features staying opt-in through packages like Wagtail AI.


Django Software Foundation

DSF Board monthly meeting, July 09, 2026

Minutes from this month's DSF Board meeting: a host for DjangoCon Europe 2027 was approved, a new Google Summer of Code Working Group was chartered, Executive Director hiring continues with guidance from the PSF, and grants went to PyCon Cameroon and PyCon Africa.


Updates to Django

Today, "Updates to Django" is presented by Raffaella from Djangonaut Space! 🚀

Last week we had 17 pull requests merged into Django by 11 different contributors - including 6 first-time contributors! Congratulations to Tom Most, CharulL00, Sina Chaichi Maleki, Harvey Bellini, Stephanie and Vismay for having their first commits merged into Django - welcome on board!

News in Django 6.2:

Thanks to the continuous efforts of the contributors, a SQLite regression test has also been added to inspectdb when a table has a foreign key that references sqlite_master. (#25243)

Support for prefers-color-scheme was also implemented, adding dark mode CSS overrides for the technical 500 (traceback) and 404 debug views. (#35875)


Django Fellow Reports

Django Fellow Report - Natalia

A security-heavy week: two patches for confirmed vulnerabilities, deep-dive reviews of two more, and prep for the August release with CVE metadata and prenotifications, plus continued iteration on EmailValidator improvements.

Django Fellow Report - Sarah

Reviews across Django and djangoproject.com, including the Selenium to Playwright migration and admin widget fixes, plus a new GitHub Action to test djangoproject.com against Django main and engagement on six security issues across Django and djangoproject.com.

Django Fellow Report - Jacob

Sustained attention on a couple of security reports, alongside triage and a long review list covering dark mode error pages, Oracle Test Pilot in CI, and the asgiref 3.12 update that enables free-threading tests.


Python Software Foundation

Get Ready: PSF Board Nominations Opening Soon!

PSF Board nominations open July 28, with voting September 1-15. If you're a voting member, affirm your intent to vote by August 25.

Get Ready: 2026 Python Packaging Council Nominations Opening Soon!

Nominations for the first-ever Python Packaging Council open July 28 and close August 11.


Events

Django Girls Chicago - August 22, 2026

Django Girls returns the Saturday before DjangoCon US in Chicago: build your first website, eat free food, and meet fellow aspiring Django developers. The free workshop is limited to 45 people and applications close August 12, so apply early.

Preparing for sprints as a project leader (at DjangoCon US)

As DjangoCon US approaches in just a few weeks time, here are some good tips on how to make the most out of the sprints following the tutorial talks.

A First-Timer's Guide to Navigating America

If you are attending DjangoCon US, please do follow the news section of the website, as it has helpful articles like this one, as well as info on childcare at the conference, and more.


Sponsored Link

When is it worth paying for a mentor?

Thinking about hiring a mentor to grow as a Django developer? Here are a few honest questions to help you get clarity on whether now's the right time.


Articles

Django: introducing django-crawl

Adam Johnson introduces django-crawl, a new package that crawls your whole site with Django's test client (via links, sitemaps, or a Python API) to surface broken pages before your users do.

Nifty Django Feature: Form Templates

Django's form templates separate a form's HTML from the view that processes it: set template_name on the form for one reusable layout, or on an individual field when a single input needs custom markup.

Some more things about Django I've been enjoying

Building a "2010 style" backend-heavy web app, this writeup highlights Django's readable query builders, handy template filters like querystring and json_script, and the comfort of automatic migrations. It also covers the author's practical performance questions, including a misconfigured cached template loader and why it mattered.

Browser Push Notifications for a Django Website

A step-by-step tutorial on adding browser Web Push notifications to a Django site using VAPID keys, a service worker, and a Huey background task, so you get OS-level notifications even when the admin tab is closed.

Is it time to go back to Django?

Some arguments for Django's opinionated, batteries-included approach in the AI-coding era, since it limits the decisions an AI agent has to make and reduces the chance of it going astray.

Deploying Web Apps in 2026: My EuroPython Conference Talk

The written version of Will Vincent's EuroPython talk, which maps today's hosting landscape and builds a ten-step mental model of everything your dev server quietly handles for you, from WSGI servers and static files to running migrations at release time.

My EuroPython 2026 - Paolo Melchiorre

A day-by-day recap of Paolo Melchiorre's EuroPython 2026 in Kraków, compiled from Mastodon posts and photos, from the Python Steering Council update to rethinking asyncio for free-threaded Python and time with the Django community at the booth.

EuroPython 2026 Recap - Will Vincent

Highlights from a packed week in Kraków, where the standout theme was agentic AI workflows, with teams split between off-the-shelf tools and heavy internal tooling.

PyCon US 2026 Recap - Katherine Michel

Katherine's famous PyCon recap is here! Security and AI front and center, PSF and PyPI updates, steering council priorities for free-threading, and lots of great pictures.


Events

Django on the Med

Three days of Django development sprints, September 23-25 in Pescara, Italy. The second edition is free to attend and gathers Fellows, board and Steering Council members, and contributors new and experienced to push Django forward.

Django Day Copenhagen 2026

October 2 in Copenhagen. The first three talks are by Marijke Luttekes, Efe Öge, and Denny Biasiolli.


Django Job Board

Three new remote openings join the board this week, from AI-native full-stack work at Hive Collective to Django backend engineering for genetic testing at MyOme and Python + TypeScript roles at Fusionbox.

Senior Full Stack Engineer at Hive Collective 🆕

Senior Backend Engineer at MyOme 🆕

Python + TypeScript Engineers at Fusionbox 🆕

Freelance Full-Stack Web App Developer at Mindrift


Projects

adamchainz/django-crawl

An in-process site crawler using Django's test client.

FROWNINGdev/django-orm-lens

See your entire Django schema (every model, field, and relationship) in your editor, terminal, or AI agent, one keystroke away from a live ER diagram.

24 Jul 2026 3:00pm GMT

Django: release code words up to 6.1

Did you know that each Django release has a "code word" associated with it? It's hidden in plain sight, in the announcement blog post describing the list of features coming in the next version. I think this is a lovely little tradition.

I last covered the list back in 2021, for Django 3.2 (post). This post expands the table up until Django 6.1, which is expected next month (the first release candidate came out earlier this week).

Each code word links to its Wiktionary entry so you can see the definition. The word frequency column is based on the English data in the wordfreq Python package, as occurrences per billion words, so higher numbers mean the word is more common.

Version Post author Quote with code word highlighted Word frequency
(per billion words)
1.7 James Bennett ...will bring several major new features to Django, along with a host of other improvements... 58,900
1.8 Tim Graham ...several major new features and a cornucopia of other improvements... 363
1.9 Tim Graham ... myriad of goodies... 3,090
1.10 Tim Graham ... panoply of new features... 209
1.11 Tim Graham ... medley of new features... 1,910
2.0 Tim Graham ... assortment of new features... 1,820
2.1 Tim Graham ... smorgasbord of new features... 263
2.2 Carlton Gibson ... salmagundi of new features... 36
3.0 Carlton Gibson ... raft of new features... 2,880
3.1 Mariusz Felisiak ... potpourri of new features... 245
3.2 Carlton Gibson ... mezcla of new features... N/A (not in English data)
4.0 Mariusz Felisiak ... abundance of new features... 7,240
4.1 Carlton Gibson ... profusion of new features... 331
4.2 Mariusz Felisiak ... farrago of new features... 83
5.0 Natalia Bidart ... deluge of exciting new features... 1,120
5.1 Natalia Bidart ... kaleidoscope of improvements... 692
5.2 Sarah Boyce ... composite of new features... 8,710
6.0 Natalia Bidart ...assembles a mosaic of modern tools and thoughtful design... 3,800
6.1 Jacob Walls ...a harmonious mélange of new features... 98

Some observations on the newer entries:

Fin

May you enjoy Django's ever-growing omnium-gatherum of features,

-Adam

24 Jul 2026 4:00am GMT

22 Jul 2026

feedDjango community aggregator: Community blog posts

Tracking Blips

bliptracker was a side project that I happened to produce during June and last week realised I hadn't written about it here, so here goes!

One annoyance I have with Claude.ai (or other web based LLM interfaces), is that I would start multiple conversations across multiple topics such as client work, organising my Todoist, an idea to explore, gifts to research, the list goes on, but I was keeping open tabs for each conversation to not lose track of the active conversations, but this didn't work as I still had those open loops in my head to follow up to move each conversation forwards.

I didn't want a full blown task manager (I pay for Todoist which fits perfectly), but I did want to track the state of each conversation in Claude from both the web app and the mobile. The result is a two fold solution, first there is a system prompt telling Claude to end each respond with either a 🔴, along with the next action required from me, or a ✅ which tells me the conversation is resolved. The second part of the solution is a Chrome extension which then automatically updates the title of any conversation with the red dot or green check mark, so I can tell at a glance which chats need work and which are done.

I do have a couple more features planned such as supporting other LLMs and a possible snooze feature. But for now it's a small working project that keeps my chats organised. It's available at bliptracker.xyz.

One final point on this project, I hope that eventually it gets replace by Anthropic building a better native product for tracking the status of chats, it's very limited right now. More widely this highlights that while new models are powerful and can do more, it still requires us as engineers to build products that solve actual problems in novel, tasteful and well designed solutions. That is what we pay for when buy a tool and what our users expect from us and something that no model as far as I can see will ever replace.

22 Jul 2026 5:00am GMT

23 Jun 2026

feedPlanet Twisted

Glyph Lefkowitz: Adversarial Communication

As I have discussed in previous posts, "AIs" can make mistakes. In fact, they do make mistakes, and their mistake-making patterns are such that where and how they will make mistakes is both uncertain and constantly changing.

Thus, in any scenario where you want to attempt to make "productive" use of "AI", you must have a system in place for checking every result. Not checking some results; checking every result. If each result might have a consequence for you (and if it didn't have a consequence, why bother automating it?) and you cannot predict in advance which kinds of results will need verification, then verification is always required.

The verification often ends up being just as expensive as doing the work in the first place, which means that if you want your usage of "AI" to be personally profitable, you have to find someone else to externalize the cost of verification onto. This person becomes your adversary, and, if you are successful, your "AI's" victim.

The Ladder-Climber And Their Reverse-Centaur Rungs

One way that this constellation of facts can straightforwardly assemble themselves into a dystopian nightmare is the phenomenon, described by Cory Doctorow, of the reverse centaur. This is when your employer non-consensually turns you into the verification system. The "AI" does the fun part of initially performing the work, and then you do the boring part where you check if the robot is right and clean up its messes, even if everyone already knows that it would, in aggregate, be cheaper for you to do the work in the first place.

Reverse centaurs can be made from any automation, not only "AI" automation. I think that there is a reason that this term happens to have emerged in the "age of AI", though, and not with earlier automation technologies (even those which were considerably more viscerally horrific). That reason is: the wrongness of "AI" output is not merely a technical feature that must be compensated for, it is a generalized externality.

As I mentioned above, if you are responsible for the entirety of the work, both extruding the "AI" output and checking it, it's usually cheaper to have humans do the entirety of the work to begin with. When humans do the writing directly, we can check as we go, and thus verification doesn't need to be as comprehensive.

When "AI" coding advocates say "code review is the bottleneck", what they are observing is that the LLM is still rolling the dice for each PR, and a human is still necessary to verify that each of those rolls is a winner. But calling this process "code review" is a bit of a misnomer; it's not really "code review" in the traditional sense, it's human understanding.

Before the advent of "AI", the human understanding was implicit in the process of writing the code in the first place1, and the code review was a way of diffusing and extending that understanding. Now that the code can be authored with no initial understanding taking place, that cost has not gone away, it has moved.

Human understanding was always the bottleneck.

However, this is taking a collaborative view of a software project, where satisfying the needs and solving the problems of your customers are the goals. We can see that "AI" is a bad tool to satisfy those goals, because all it's doing is converting the first half of the work, that of understanding the code as you write it, to understanding the agent's output as you read it.

What if, instead, we were to take the view that every software company is a Hobbesian nightmare, red in tooth and claw? In this view, the only goal of a software project is for the individual developers to make their promo cycles and get their bonuses. Given that there is only a certain amount of money to go around, this is a zero-sum game where each programmer wants to look more productive than their colleagues.

Pretty much every organization finds it easy to reward "productivity" as expressed by lines of code emitted, but the benefits of doing thorough and thoughtful design, analysis, and code review very difficult to reward. In this world, an LLM is an invaluable tool for the sociopathic ladder-climber, particularly if your legacy organization is still structuring their workflows as if the person prompting the bot is "writing" the code, and then they get to foist off the act of "reviewing" the code onto someone else.

Here, the prompter effectively externalizes the cost of the LLM's failures but internalizes any benefits. The prompter will vibe-code a big feature, so large that the assigned reviewer can't possibly comprehend it all effectively. When this happens, the reviewer will, eventually, be pressured to approve it, even if they can try to spot a few problems along the way. The reviewer has their own work to get back to, after all, the obligation to review the prompter's (read: the bot's) code is a drain on their time that they are not going to get rewarded for.

If this feature is a big success, the prompter gets a promotion. If it causes a big issue, well, the reviewer must not have been careful enough.

This is why LLMs are "good for coding", and also why their biggest promoters keep having outages.

The Generative Gish Galloper

Coding is the biggest "success story" of this type of adversarial communication, but it is by far not the only instance of such a thing. LLMs create a new form of leverage that can turn Brandolini's law from a linear advantage into an exponential one. If you are engaged in a political debate where you want to overwhelm the other side in nonsense, an LLM can generate bullshit faster than it is physically possible for a human being to type, let alone respond thoughtfully. There is an asymmetry to the utility of this weapon as well: only one side of the political spectrum wants to flood the zone and destroy trust in institutions and the concept of truth. There's a good reason that the fascists love it.

Straightforward Spam and Fraud

This is kind of obvious, but LLMs can generate lightly-customized, plausible-looking text much more quickly than any human being. This facilitates their use in fraud, spam, and scams. In a spamming or fraudulent interaction, once again, the costs are externalized onto the victim: the recipient of a spam message has to do all the work of "checking" the LLM's output. Spammers already expect very low hit rates from boilerplate, and if the LLM can increase those percentages from 1% to 5% the technology will pay for itself; they don't need anything like reliable accuracy.

Customer "Support"

If you have any kind of commercial relationship with a company, I probably don't even need to mention this: customer "support" bots are a misery. Everybody knows it at this point. But customer support is usually conceptualized by businesses as an adversarial interaction, because it is a cost center. They maintain internal metrics on time-to-resolution and try to optimize them. Implicitly, this creates a dynamic where the goal of the customer service agent's job is not to solve your problem, but to emit noise that will cause you to think your problem is resolved, or to give up, as fast as possible. Unsurprisingly, LLMs can emit this noise faster than humans can, getting those customers off the phone. But those customers will remember those interactions, and the story outside the TTR metrics is horrible.

Similarly to the situation in software development, LLMs can look very good on paper for customer support, but mostly what they are doing is illuminating the problems with the industry's existing metrics, by turning "winning the metrics battle against the customer" into a more obvious and immediate defeat for the company's long term reputation.

"Education"

In 2026 it is sadly a fact of life that students cheat all the time using "AI", and that this cheating is very successful, in that the teachers find it very hard to detect.

LLMs are great for cheating on schoolwork because the student is externalizing the work of the checking onto the teachers, who are often starting at a disadvantage to begin with, at least in the US.

My view is that this is happening because of a divergence in the way that students vs. teachers (or, more accurately, "the broader educational system") view grading.

When a student is asked to write an essay, the teachers see the effort as both intrinsically worthwhile for the student, as well as useful as a pedagogical tool to evaluate and react to the student's progress. The student, by contrast, sees a stumbling block designed to knock them off the path to success and into a permanent underclass. It is no wonder that the student sees "AI" as useful to their own goals and has no compunction about deploying it.

There is a bitter irony that the ability to understand the inherent value of actually writing the essay on their own is the sort of thing that students can really only learn by writing a bunch of essays. There's no way that I can think of which makes the benefit legible as long as a shortcut is available.

The net effect here is a downward spiral, where the already-wobbling educational system is sustaining an attack that it doesn't have the resources to recover from. The individual students' attacks against their teachers and their schools' grading systems might appear to momentarily succeed, but they will win the battle and lose the war.

Spamming "For Good"?

Usually when we talk about someone unilaterally choosing to enter into an adversarial relationship, that's an "attack" and for good reasons we have a negative impression of the attacker. However, I would be remiss if I did not point out that there are some cases where the relationship was already adversarial; just because you're the attacker doesn't mean that you are evil.

For example we might imagine use-cases like automatically filing appeals for prior authorizations against health insurance. It's relatively well-known at this point that the main way for-profit insurers maintain their margins is by denying claims right up to the line of the policies themselves being fraud, so using a spamming tool to fight them might be entirely justifiable2 in that case.

Similarly, using an LLM could be justified in a fight against a company refusing to honor a warranty. One could imagine using an LLM to immediately generate replies and escalations.

However, even in imagined cases like these, the underlying problem is that the insurers and the vendors already have a tremendous amount of structural power, so it is more likely that they will have the advantage in deploying a communications weapon like an LLM, as well as enacting policies to simply ignore any LLM-based communication that you might submit. Worse, if these strategies were to become widespread, they might provide an excuse to reject any communications by feeding them into an unreliable "LLM detector" and issuing an automated "computer says no" even to hand-written correspondence.

It is also worth stressing that these cases are imagined, as compared to the very real coworker-abuse, spam, scam, fraud, and disinformation campaigns being waged in real life today.

Therefore, while legitimate uses might exist, it's hard to imagine that there's anywhere they would be genuinely valuable and sustainable. In the best case "AI" will provide a temporary advantage for underdogs that will provoke an arms race which the resource-advantaged adversaries will win in the long run, in the worst case the arms race itself will cement permanent structural change that will make things worse.

"Search" By Stealing

Most of the adversarial utility of "AI" is on the "write" side, since write-amplification is more obviously aggressive than reading. But the "read" side of LLMs - summarization and question-answering - can be a form of attack as well.

To begin with, the act of reading itself is currently enormously destructive, but that's arguably not a fundamental aspect of this technology. They could set reasonable rate-limits and respect things like robots.txt, as search engines have for decades now. They could also refrain from committing criminal levels of copyright infringement. But, today, using "AI" tools does suborn this sort of out-of-control crawling.

More insidiously, consider the scenario described in this YouTube video. The LTT Bros decided to try Linux again, and in the course of so doing, they had problems. When trying to solve these problems, they were faced with a choice: they could consult Reddit, or they could ask an LLM. Asking an LLM would "gaslight the heck out of" them, but they still found it preferable, because they would at least get an answer without getting yelled at.

Initially this sounds great. But it also means that you want to extract knowledge from a community, while mechanically eliding any values or norms that the community may want to impart as part of offering that knowledge. As someone who spent many years in a community tech support role, this is worrying. Many requests for support are people asking how to do things that will momentarily solve a superficial problem but create a long-term reliability problem or even an immediate security risk, that the question-asker doesn't want to hear about. Consider the question "I'm tired of entering my password so much, how do I make it so my laptop unlocks automatically". An obsequious chatbot will helpfully tell you how to do this without pushback.

But, this is also a sort of ethically murky area. The Linux community is somewhat famously, for many years now, a toxic cesspool of general hostility, misogyny, etc. It is certainly a good thing that people can get access to this knowledge without subjecting themselves to abuse. But it also means that the people with the power and the privilege to change the community for the better can just quietly withdraw, rather than fixing the problems. It also means that the positive elements of culture cannot be transmitted, and people will have no opportunity to learn about unknown unknowns.

In this case, the "adversarial" communication is with society. The thing that using an LLM for search lets you do is withdraw from society and avoid forming any personal connections. There are some personal connections which are painful and annoying, and so that can feel like a momentary balm. But the need to make connections in general is, like, the concept of society itself.

Who Am I Hurting?

LLMs are good at adversarial communication. They are so good at it, relative to their other benefits, that they will tend to make communications adversarial if you are not remaining vigilant about the possibility that it might do so. My request to you, dear reader, if you are going to use such tools, is to always ask yourself, "who might I be hurting, if I use an LLM for this?"

If you're using an "AI", who is its adversary? If you haven't given it one yet, who might the "AI" turn into an adversary? Who might you overwhelm with an asymmetric amount of output, or, if you're receiving information and not sending it, who are you taking that information from without consulting?

Figure out the answers to these questions and conduct yourself accordingly; the answer might be "yourself".

Acknowledgments

Thank you to my patrons who are supporting my writing on this blog. If you like what you've read here and you'd like to read more of it, or you'd like to support my various open-source endeavors, you can support my work as a sponsor!


  1. One of the reasons that software developers tend to prefer greenfield development is that when you are given a blank page, you can project your own specific understanding onto it. You can structure the codebase in a way that works for your brain, down to the variable naming conventions and the module layouts. LLM-assisted development makes everything into instant brownfield work, which makes developers instantly miserable; even those who are excited about the technology will frequently complain about how it feels like their agency has been stolen and their joy in the work has been diminished. But I digress.

  2. Modulo the massive amount of other externalities involved in using LLMs, of course, but I don't have the time or energy to get into those here.

23 Jun 2026 8:06pm GMT

09 Jun 2026

feedPlanet Twisted

Hynek Schlawack: How to Ditch Codecov for Python Projects

Codecov's unreliability breaking CI on my open source projects has been a constant source of frustration for me for years. I have found a way to enforce coverage over a whole GitHub Actions build matrix that doesn't rely on third-party services.

09 Jun 2026 12:00am GMT

22 May 2026

feedPlanet Twisted

Glyph Lefkowitz: Opaque Types in Python

Let's say you're writing a Python library.

In this library, you have some collection of state that represents "options" or "configuration" for a bunch of operations. Such a set of options is a bundle of potentially ever-increasing complexity. Thus, you will want it to have an extremely minimal compatibility surface, with a very carefully chosen public interface, that is either small, or perhaps nothing at all. Such an object conveys state and might have some private behavior, but all you want consumers to be able to do is build it in very constrained, specific ways, and then pass it along as a parameter to your own APIs.

By way of example, imagine that you're wrapping a library that handles shipping physical packages.

There are a zillion ways to do it ship a package. There are different carriers who can ship it for you. There's air freight, and ground freight, and sea freight. There's overnight shipping. There's the option to require a signature. There's package tracking and certified mail. Suffice it to say, lots of stuff.

If you are starting out to implement such a library, you might need an object called something like ShippingOptions that encapsulates some of this. At the core of your library you might have a function like this:

1
2
3
4
5
async def shipPackage(
        how: ShippingOptions,
        where: Address,
    ) -> ShippingStatus:
    ...

If you are starting out implementing such a library, you know that you're going to get the initial implementation of ShippingOptions wrong; or, at the very least, if not "wrong", then "incomplete". You should not want to commit to an expansive public API with a ton of different attributes until you really understand the problem domain pretty well.

Yet, ShippingOptions is absolutely vital to the rest of your library. You'll need to construct it and pass it to various methods like estimateShippingCost and shipPackage. So you're not going to want a ton of complexity and churn as you evolve it to be more complex.

Worse yet, this object has to hold a ton of state. It's got attributes, maybe even quite complex internal attributes that relate to different shipping services.

Right now, today, you need to add something so you can have "no rush", "standard" and "expedited" options. You can't just put off implementing that indefinitely until you can come up with the perfect shape. What to do?

The tool you want here is the opaque data type design pattern. C is lousy with such things (FILE, pthread_*_t, fd_set, etc). A typedef in a header file can easily achieve this.

But in Python, if you expose a dataclass - or any class, really - even if you keep all your fields private, the constructor is still, inherently, public. You can make it raise an exception or something, but your type checker still won't help your users; it'll still look like it's a normal class.

Luckily, Python typing provides a tool for this: typing.NewType.

Let's review our requirements:

  1. We need a type that our client code can use in its type annotations; it needs to be public.
  2. They need to be able to consruct it somehow, even if they shouldn't be able to see its attributes or its internal constructor arguments.
  3. To express high-level things (like "ship fast") that should stay supported as we add more nuanced and complex configurations in the future (like "ship with the fastest possible option provided by the lowest-cost carrier that supports signature verification").

In order to solve these problems respectively, we will use:

  1. a public NewType, which gives us our public name...
  2. which wraps a private class with entirely private attributes, to give us an actual data structure, while not exposing the constructor,
  3. a set of public constructor functions, which returns our NewType.

When we put that all together, it looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from dataclasses import dataclass
from typing import Literal, NewType

@dataclass
class _RealShipOpts:
    _speed: Literal["fast", "normal", "slow"]

ShippingOptions = NewType("ShippingOptions", _RealShipOpts)

def shipFast() -> ShippingOptions:
    return ShippingOptions(_RealShipOpts("fast"))

def shipNormal() -> ShippingOptions:
    return ShippingOptions(_RealShipOpts("normal"))

def shipSlow() -> ShippingOptions:
    return ShippingOptions(_RealShipOpts("slow"))

As a snapshot in time, this is not all that interesting; we could have just exposed _RealShipOpts as a public class and saved ourselves some time. The fact that this exposes a constructor that takes a string is not a big deal for the present moment. For an initial quick and dirty implementation, we can just do checks like if options._speed == "fast" in our shipping and estimation code.

However, the main thing we are doing here is preserving our flexibility to evolve the related APIs into the future, so let's see how we might do that. For example, let's allow the shipping options to contain a concrete and specific carrier and freight method:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from dataclasses import dataclass
from enum import Enum, auto
from typing import NewType

class Carrier(Enum):
    FedEx = auto()
    USPS = auto()
    DHL = auto()
    UPS = auto()

class Conveyance(Enum):
    air = auto()
    truck = auto()
    train = auto()

@dataclass
class _RealShipOpts:
    _carrier: Carrier
    _freight: Conveyance

ShippingOptions = NewType("ShippingOptions", _RealShipOpts)

def shipFast() -> ShippingOptions:
    return ShippingOptions(_RealShipOpts(Carrier.FedEx, Conveyance.air))

def shipNormal() -> ShippingOptions:
    return ShippingOptions(_RealShipOpts(Carrier.UPS, Conveyance.truck))

def shipSlow() -> ShippingOptions:
    return ShippingOptions(_RealShipOpts(Carrier.USPS, Conveyance.train))

def shippingDetailed(
    carrier: Carrier, conveyance: Conveyance
) -> ShippingOptions:
    return ShippingOptions(_RealShipOpts(carrier, conveyance))

As a NewType, our public ShippingOptions type doesn't have a constructor. Since _RealShipOpts is private, and all its attributes are private, we can completely remove the old versions.

Anything within our shipping library can still access the private variables on ShippingOptions; as a NewType, it's the same type as its base at runtime, so it presents minimal1 overhead.

Clients outside our shipping library can still call all of our public constructors: shipFast, shipNormal, and shipSlow all still work with the same (as far as calling code knows) signature and behavior.

If you need to build and convey some state within your public API, while avoiding breakages associated with compatibility churn, hopefully this technique can help you do that!


Acknowledgments

Thanks for reading, and thank you to my patrons who are supporting my writing on this blog. If you like what you've read here and you'd like to read more of it, or you'd like to support my various open-source endeavors, you can support my work as a sponsor.


  1. The overhead is minimal, but it is not completely zero. The suggested idiom for converting to a NewType is to call it like a function, as I've done in these examples, but if you are wanting to use this pattern inside of a hot loop, you can use # type: ignore[return-value] comments to avoid that small cost.

22 May 2026 12:33am GMT