15 Jul 2026

feedPlanet Python

Python Software Foundation: Affirm Your PSF Membership Voting Status

Every Python Software Foundation (PSF) voting-eligible Member (Supporting, Contributing, and Fellow) needs to affirm their membership to vote in this year's PSF Board and Python Packaging Council (PPC) elections.

If you wish to vote in either the PSF Board or Python Packaging Council elections, you must affirm your intention to vote for each election no later than Tuesday, August 25th, 2:00 pm UTC, to participate in this year's elections. This year's election votes begin Tuesday, September 1st, 2:00 pm UTC, and close on Tuesday, September 15th, 2:00 pm UTC.

Election communications from psfmember.org

You should have received an email from "psf@psfmember.org <Python Software Foundation>" with the subject "[Action Required] Affirm your PSF Membership voting intention for the 2026 PSF Board Election" and/or "2026 Python Packaging Council Inaugural Election Information & Schedule" that contains information on how to affirm your voting status. If you were expecting to receive the email but have not (make sure to check your spam!), please email psf-elections@pyfound.org for the PSF Board election or pc-elections@python.org, and we'll assist you. Please note: If you opted out of emails related to your membership, you did not receive these emails.

PSF Members should review their communication preferences on psfmember.org if you would like to opt in or out of receiving emails about the PSF Board, PPC elections, or both. Here's how:

If you had previously opted out of communications from the PSF through psfmember.org and would like to review or change your preference, we encourage you to update them using the instructions above. The PSF only sends a handful of election and fundraising related communications every year via psfmember.org. The PSF newsletter runs through a separate mailing list (and we welcome you to sign up!).

How to affirm your intention to vote

You can affirm your voting intention by following the steps in our video tutorial:

Need to check your membership status?

Log on to psfmember.org and visit your PSF Member User Information page to see your membership record and status. If you are a voting-eligible member (active Supporting, Contributing, and Fellow members of the PSF) and do not already have a login, please create an account on psfmember.org and then email psf-elections@pyfound.org so we can link your membership to your account. Please ensure you have an account linked to your membership so that we can have the most up-to-date contact information for you in the future.

PSF Bylaws

Section 4.2 of the PSF Bylaws requires that "Members of any membership class with voting rights must affirm each year to the corporation in writing that such member intends to be a voting member for such year."

Our motivation is to ensure that our elections can meet quorum as required by Section 3.9 of our bylaws. As our membership has grown, we have seen that an increasing number of Contributing and Fellow members with indefinite membership do not engage with our annual election, making quorum difficult to reach.

An election that does not reach quorum is invalid. This would cause the whole voting process to be re-held, resulting in fewer voters and an undue amount of effort on the part of the PSF Staff.

Reminders about membership and voting

Reminder: If you were formerly a Managing member, your membership type was changed last year to Contributing per 2024's Bylaw change that merged Managing and Contributing memberships.

Per another recent Bylaw change that allows for simplifying the voter affirmation process by treating past voting activity as intent to continue voting, if you voted last year, you will automatically be added to the 2026 voter roll. Please note: If you removed or changed your email on psfmember.org, you may not automatically be added to this year's voter roll.

What happens next?

You'll get an email from OpaVote with a ballot (or two!) on or right before September 1st, and then you can vote!

Check out our PSF Membership page to learn more. If you have questions about membership, nominations, or this year's Board election, please email psf-elections@pyfound.org or join the PSF Discord for the upcoming Board Office Hours on August 11th, 9 PM UTC. You are also welcome to join the discussion about the PSF Board election on the Python Discuss forum.

15 Jul 2026 11:06am GMT

Django Weblog: Supporting the Triptych Project

The Django Steering Council - in its role as the DSF's arm for technical governance - has provided a Letter of Collaboration in support of a funding application by Carson Gross and Alex Petros to advance the Triptych Project: three proposals to make HTML itself more expressive, in every browser, by default.

Here's why, and how you can help.

HTML over the wire, and Django

The last few years have seen a move back towards serving multipage applications, with server-rendered templates. The HTMX library has probably had the biggest impact in the Django space, but Unpoly, Turbo, and others are part of the same story: send HTML over the wire, let the browser do what browsers do, and skip the client-side application layer where you don't really need it. It's a simpler model of the web - and it's one that speaks to Django's heart.

This isn't a movement Django has watched from the sidelines. Template partials, added in Django 6.0, were directly inspired by the patterns these libraries make natural.

The Triptych Project

The Triptych Project takes the core insights from HTMX (and the related libraries) and proposes them for the HTML standard itself. Three small additions:

  1. PUT, PATCH, and DELETE methods for forms - completing HTML's HTTP vocabulary.
  2. Button actions - buttons that make HTTP requests without a wrapping form. This is the current focus.
  3. Partial page replacement - links, forms, and buttons that target part of the DOM.

Together these aim to make it possible to build far more of the web with plain HTML - no JavaScript dependency, no library, nothing to ship or maintain.

Button actions

The current proposal (WHATWG #12330, full proposal) adds the action and method attributes to <button>. The canonical example is logout. Today there's no semantic way to write a logout button; you have to wrap it in a form:

<form action=/logout method=POST>
  <button>Logout</button>
</form>

Every Django developer has written this kind of thing. With button actions we could write the simpler single line:

<button action=/logout method=POST>Logout</button>

This isn't abstract for us. The Django admin's submit row holds multiple buttons, and a link disguised as a button:

<div class="submit-row">
  <input type="submit" value="Save" class="default" name="_save">
  <input type="submit" value="Save and add another" name="_addanother">
  <input type="submit" value="Save and continue editing" name="_continue">
  <a href="/admin/auth/user/.../delete/" class="deletelink">Delete</a>
</div>

Here, all the save inputs lead to the same action URL from the wrapping form. The view then branches on the submitted name value. That, of course, works, but we can imagine simpler, more flexible ideas being enabled via the Multi-Action Pages examples in the proposal.

The disguised submit link leads to the deletion confirmation page, where we then submit a form to confirm the action. That's the correct behaviour, but the markup confuses the intent: this isn't (really) a navigation to a new page, it's the first step of an action - deleting the object. The proposal's discussion of Buttons vs Links describes situations we come up against writing applications regularly.

The goal here is simpler patterns that will help us write better markup.

Why we're supporting this

The Django Software Foundation's mission includes a commitment to "advance the state of the art in Web development". Standards work is that in its purest form: an improvement to HTML lands for everyone, in every framework, in every browser, indefinitely.

It's also slow, painstaking work - specification, implementer engagement, web platform tests - that needs sustained attention. Carson and Alex are applying for funds so that people can devote real time to it. Our Letter of Collaboration is a concrete contribution to that application.

How you can help

If your company builds on Django, or indeed any other framework - with HTMX, Unpoly, Turbo, or plain HTML forms - this work benefits you directly. Carson and Alex are seeking non-binding letters of support on official letterhead for the funding application. Details and contacts are on the Triptych Project site.

Individually, do read the proposals, weigh in constructively on the WHATWG issues, and spread the word.

A simpler web is a better web. We're glad to support work that moves HTML in that direction.

15 Jul 2026 11:00am GMT

feedDjango community aggregator: Community blog posts

Django: introducing django-orjson

Just as cars painted red are known to be faster, libraries implemented in Rust are also known to be faster. Today's example is orjson, a Rusty replacement for Python's built-in json module, boasting 10x faster serialization and 2x faster deserialization.

Such a library is great, but adopting it isn't easy, especially when your framework uses json in many different parts. To help Django developers adopt orjson, I have created django-orjson, which provides a whole bunch of drop-in replacements for Django and Django REST Framework (DRF) components backed by orjson.

For example, there's a version of JsonResponse:

from django_orjson.http import JsonResponse


def index(request):
    return JsonResponse({"title": "Hello, world!"})

…a test client with matching test case classes:

from django_orjson.test import SimpleTestCase


class IndexTests(SimpleTestCase):
    def test_index(self):
        response = self.client.get("/", headers={"accept": "application/json"})
        assert response.status_code == 200
        # response.json() uses orjson to parse the response body
        assert response.json() == {"title": "Hello, world!"}

…a version of Django's json_script template tag:

{% load django_orjson %}
{{ chart_data|json_script:"chart-data" }}

…and plenty more! All tested against the currently supported versions of Python and Django with 100% branch coverage.

While database queries tend to dominate the typical Django application's runtime, the time spent in serialization and deserialization can still be significant. That can make adopting orjson a nearly free performance win, which I hope django-orjson makes almost trivial for you.

Django proposal

After seeing the initial version of django-orjson, Paolo Melchiorre decided to push for adding orjson support to Django itself, in the new feature proposal Pluggable JSON serialization/deserialization backend. He made a thorough list of all the places in Django that could use orjson, and the proposal has gathered 14 thumbs-ups at the time of writing.

If you're interested in the topic of speeding up Django's JSON handling, check out the proposal and add your thoughts to the discussion.

Fin

Please try out django-orjson today and send me feedback.

YAML, TOML, or JSON?

-Adam

15 Jul 2026 4:00am GMT

14 Jul 2026

feedPlanet Python

PyCoder’s Weekly: Issue #743: Stacks & Queues, Django F-Expressions, MCP Clients, and More (2026-07-14)

#743 - JULY 14, 2026
View in Browser »

The PyCoder’s Weekly Logo


Stacks and Queues in Python

This post shows you how to use a Python list for stack operations (last-in, first-out) and a deque from the collections module for queue operations (first-in, first-out).
TREY HUNNER

Nifty Django Feature: F Expressions

Django's F-Expression provides a way of querying fields from the ORM. They're particularly handy to traverse relationships in more complex queries.
TIM SCHILLING

Secure Your Code, Wherever, or However You Write It

alt

AI coding agents have blind spots, they reproduce patterns but struggle with security context. AURI by Endor Labs plugs into your editor via MCP, catching flaws, signaling exposed secrets, and spotting malicious dependencies. Ship secure by default. Try AURI Free →
ENDOR LABS sponsor

Testing MCP Servers With a Python MCP Client

Learn how to build a Python MCP client that tests MCP servers from your terminal. List their tools, prompts, and resources, then call each one.
REAL PYTHON course

Quiz: Testing MCP Servers With a Python MCP Client

REAL PYTHON

PEP 797: Shared Object Proxies (Rejected)

PYTHON.ORG

Django Security Releases Issued: 6.0.7 and 5.2.16

DJANGO SOFTWARE FOUNDATION

Articles & Tutorials

Constructing and Judging Modern Agentic Workflows

How can you improve your LLM agent systems through specification enrichment? What are the advantages of having an LLM act as a judge within an agent system? This week on the show, Senior IEEE Member and Quality Engineer Suneet Malhotra joins us to discuss building and evaluating agentic architecture.
REAL PYTHON podcast

What for x in y Hides From You

An explanation of how Python's for x in y syntax is a thin wrapper around the iterator protocol: iter(...), next(...), and StopIteration. Using examples from Memphis, a Python interpreter written in Rust, it shows how this design makes lists, ranges, and generators feel unified rather than magical.
TYLER GREEN • Shared by Tyler Green

Stop stitching 5 different systems together for your agents.

alt

Dev teams spend weeks fitting together vector DBs, graph DBs, relational stores, filesystem primitives and optimizing cache. How about everything via a single API? P90 sub-200ms recall - the fastest graph database to unlock true agent memory, knowledge graphs and user-personalization. Click Here to Try HydraDB Out for Free
HYDRADB sponsor

Building a Fast HTML Toolkit in C for Python

turbohtml began as a patch to speed up html.escape and html.unescape in CPython. When the core team declined to maintain SIMD in the standard library, it became a third party library instead. This post is its story.
BERNÁT GÁBOR • Shared by Bernát Gábor

How to Publish to PyPI Using GitHub Actions Securely

If you're using GitHub Actions to publish your Python libraries, this article is for you. Learn what are the best practices to ensure the process is secure and what tools you can use to validate it.
BRETT CANNON

Python 3.15's Ultra-Low Overhead Interpreter Profiling Mode

Ken is one of the key contributors to the experimental JIT. This post talks about how Python 3.15's interpreter profiling mode is helping them figure out what is working with the JIT and what isn't.
KEN JIN

PEP 814: Add Frozendict Built-in Type

Victor has been involved in multiple attempts to add a frozen dict type to Python. His latest PEP has been accepted and frozen dictionaries will be added to Python 3.15. This post is his story.
VICTOR STINNER

How to Clean Messy CSV Files With Python

This introductory article shows you how to do data cleaning on CSV files using pandas, including dealing with duplicate rows, missing values, mixed date formats, and more.
ABID ALI AWAN,

PSF News: Security, Elections, and PyCon US 2026

This post is the monthly news round up of all things PSF. It covers a re-cap of PyCon US, several security fixes, updates from the PSF board, and more.
PYTHON SOFTWARE FOUNDATION

How to Use GitHub

Learn how to use GitHub step by step to create a remote repository, push your local Python project, and collaborate with others using GitHub Issues.
REAL PYTHON

Quiz: How to Use GitHub

REAL PYTHON

Projects & Code

pyStrich: 1D and 2D Barcode Generator Library

GITHUB.COM/MMULQUEEN • Shared by Michael Mulqueen

Snakie: Cross-Platform MicroPython IDE

GITHUB.COM/KEVINMCALEER

Notion2Pandas: Import Notion Databases Into pandas

GitLab.com
GITLAB.COM/JAEGER87

envgap: Find Gaps Between .env, Shell Env, and Python Code

GITHUB.COM/PINAK-DATTA • Shared by Pinak Datta

CLI-based Text-to-Speech Tool

GITHUB.COM/REALPACIFIC • Shared by Prashant Barahi

Events

Weekly Real Python Office Hours Q&A (Virtual)

July 15, 2026
REALPYTHON.COM

PyData Bristol Meetup

July 16, 2026
MEETUP.COM

PyLadies Dublin

July 16, 2026
PYLADIES.COM

DjangoGirls Tamale 2026

July 17 to July 19, 2026
DJANGOGIRLS.ORG

EuroSciPy 2026

July 18 to July 24, 2026
EUROSCIPY.ORG

PyData PyCon Armenia 2026

July 24 to July 26, 2026
PYCON.AM


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

14 Jul 2026 7:30pm GMT

feedDjango community aggregator: Community blog posts

How to use a list/tuple/array in Django with a raw SQL cursor

This does not work:


from django.db import connection

list_of_values = [1, 2, 3]
with connection.cursor() as cursor:
    cursor.execute("""
        SELECT *
        FROM my_model_table
        WHERE some_value IN %s
    """, [
        tuple(list_of_values),
    ])
    results = cursor.fetchall()

It will give you:

django.db.utils.ProgrammingError: syntax error at or near "'(1,2,3)'"
LINE 4:         WHERE id IN '(1,2,3)'

It used to work with psycopg v2. Now, in psycopg v3, you have to use the ANY operator. See "You cannot use IN %s with a tuple"

This will work:


from django.db import connection

list_of_values = [1, 2, 3]
with connection.cursor() as cursor:
    cursor.execute(
        """
        SELECT *
        FROM my_model_table
        WHERE some_value = ANY(%s)
    """,
        [
            list_of_values,
        ],
    )
    results = cursor.fetchall()

Note the ANY(%s), and instead of a list that has a tuple, it's a list that has a list.

What About a List of Strings

Consider...


from django.db import connection

-list_of_values = [1, 2, 3]
+list_of_values = ['foo', 'bar', 'fiz']
with connection.cursor() as cursor:
    cursor.execute(
        """
        SELECT *
        FROM my_model_table
        WHERE some_value = ANY(%s)
    """,
        [
            list_of_values,
        ],
    )
    results = cursor.fetchall()

That will result in:

django.db.utils.DataError: invalid input syntax for type integer: "foo"
LINE 4:         WHERE some_value = ANY('{foo,bar,fiz}')

My solution was to rewrite the SQL string itself and treat each value as a parameter each. In other words, the SQL string, before being sent to cursor.execute(...) will contain something like this:


AND (
  some_value = % OR
  some_value = % OR
  some_value = % OR
  some_value = % OR
  -- ...etc...
  some_value = %
)

This will work and is safe:


from django.db import connection

list_of_values = ["foo", "bar", "fiz"]
with connection.cursor() as cursor:
    cursor.execute(
        f"""
        SELECT *
        FROM my_model_table
        WHERE ({" OR ".join(["some_value = %s" for _ in list_of_values])})
    """,
        list_of_values,
    )
    results = cursor.fetchall()

14 Jul 2026 6:14pm GMT

10 Jul 2026

feedDjango community aggregator: Community blog posts

Issue 345: Django security releases issued: 6.0.7 and 5.2.16

News

Django security releases issued: 6.0.7 and 5.2.16

Three new CEVs have been addressed in the latest security releases. We encourage all users of Django to upgrade as soon as possible.

Django on the Med: Venue and Hotel Details for Edition 2!

A few more confirmed details for Django on the Med 🏖️ 2026, which will take place from September 23 to 25, 2026 in Pescara, Italy 🇮🇹.

Thank you Lacey - Django Commons

Django Commons credits Lacey Henschel for helping shape the admin team from day one, including onboarding Django REST Framework, building the recruitment pipeline, and creating project check-ins that prevent stagnation. Her decision to step down is framed as proof that sustainability includes taking breaks without guilt, with hard judgment calls rooted in respecting maintainers and community trust.


Django Software Foundation

Last Call 2026 Django Developer Survey

The 2026 survey is ending next week on July 13th. Thank you to everyone who already filled it out. Please encourage all your friends and colleagues to do the same. This is the single most important tool for collecting data from the Django community and directly influences the work of Fellows and new features.


Updates to Django

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

Last week we had 6 pull requests merged into Django by 5 different contributors

Some interesting post from the Django Forum:


Sponsored Link

Level up with mentorship

You can have a great manager and still want an outside perspective. I explain why in my FAQ.


Articles

The Missing Link: Thinking in Use Cases with Django Query Selectors

Where your queries should live - and how selectors keep your business logic lean and readable.

The Missing Link: Thinking in Use Cases with Django Query Selectors

Atomic, chainable queryset methods answer how you fetch; selectors answer what you are fetching for. Move each read use case into a named, testable function that composes CQS methods, so views and services stay thin and business logic stops spreading across views and forms.

Nifty Django Feature: resolve function

Django's resolve() turns a request path into a ResolverMatch, giving you the target view function, extracted kwargs like pet_id, and the URL name. The same mechanism can be applied to your web server logs to reconstruct which views users hit over time, as long as you track URL changes between deployments.

How to Read Postgres EXPLAIN: A Guide to Scan Types

Scan type in a Postgres EXPLAIN plan tells you whether the database reads the whole table, walks an index, builds a bitmap, or even satisfies the query entirely from an index (index-only scan). This guide walks through sequential, index, bitmap heap, parallel variants, and index-only scans so you can spot why a query is slow and what the planner is optimizing for.

Why we built yet another Postgres connection pooler

Connection poolers often break session state, forcing apps to stop relying on SET and sidelining LISTEN/NOTIFY semantics. PgDog adds a SQL-aware layer that tracks SET variables per client and proxies LISTEN/NOTIFY across processes while preserving transactional behavior, so scaling doesn't mean rewriting core Postgres usage.

A small proposal to form rendering in Django

A code example around this new feature idea, which is an extension to Django's form rendering capabilities to include widgets templates inside a form renderer.

Fixing the dictionary with Python 3.14

A Hugo van Kemenade look at "And now for something completely different" in the Python 3.14 cycle starts with the π symbol and an Oxford English Dictionary markup mistake. The reported rendering bug was fixed within about a year, highlighting how even reference sites can need careful dictionary-grade scrutiny.

How to publish to PyPI using GitHub Actions securely

GitHub Actions incidents have pushed many teams to tighten publishing workflows, and this guide lays out three practical steps for PyPI publishing: run zizmor, remove overly broad GITHUB_TOKEN permissions and persisted checkout credentials, and pin actions to commit SHAs. It also recommends using PyPI Trusted Publishing with a GitHub environment that requires an approval gate before releases.


Videos

Updates on Django's Async Story - Talk Python Live Stream

Carlton Gibson joined host Michael Kennedy to provide an in-depth look at Django's ongoing async story, where it stands now, and what to expect in future releases.


Django Fellow Reports

Jacob Walls

Jacob is on vacation this week.

Natalia Bidart

Intense week! ✨ I was mostly covering solo this week ⛑️, so it was a mix of keeping everything moving and diving deep where needed. A big chunk of time went into tracking down and fixing a docs build regression for the website (thanks Carlton for spotting it and Tobias for the help debugging), which uncovered a subtle mismatch between how Django (core) builds docs and how the website consumes them. Alongside that, I spent time on a few deeper investigations that had been lingering (snoozed over and over in my inbox ⏰), finally unblocking design questions and follow-ups that needed proper attention. On the security side 🔐, I handled prenotifications and a wave of incoming reports, closing out a number of invalid ones and keeping things tidy.

Overall, a very hands-on week 🧰 balancing throughput with some worthwhile deep dives that should pay off going forward ⚖️.


Projects

otto-torino/django-baton

A cool, modern and responsive django admin application based on bootstrap 5 that brings AI to the Django admin.

unfoldadmin/django-unfold

A modern Django Admin approach.

10 Jul 2026 3:00pm 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