16 Jun 2026
Planet Python
PyCoder’s Weekly: Issue #739: JIT Delayed, Sandboxes, OpenRouter, and More (2026-06-16)
#739 - JUNE 16, 2026
View in Browser »
Steering Council Announcement Regarding the JIT
The Python Steering Council has announced that the work on the JIT needs to be paused until a new PEP gets written. There are many unresolved questions about the approach and integration with other tools and the work on the JIT has reached a stage where these questions need to be answered. Additional discussion
PYTHON.ORG
Python in a Sandbox With MicroPython and WASM
Simon's been in search of the perfect code sandbox. This article is about his latest attempt and covers why he wants a sandbox and what tech he's used to achieve it.
SIMON WILLISON
Wallaby for Python runs Tests as you Type and Streams Results Next to Code, Plus AI Context
Wallaby brings pytest / unittest results, runtime values, coverage, errors, and time-travel debugging into VS Code, so you can fix Python faster and give Copilot, Cursor, or Claude the execution context they need to stop guessing. Try it free, now in beta →
WALLABY sponsor
Accessing Multiple AI Models With the OpenRouter API
Access models from popular AI providers in Python through OpenRouter's unified API with smart routing, fallbacks, and cost controls.
REAL PYTHON course
Articles & Tutorials
Skip Jupyter's Hidden State: Reactive Notebooks With Marimo
Marimo is a reactive Python notebook designed to make data science workflows more reproducible. This article shows how it avoids hidden execution state, saves notebooks as plain .py files for cleaner Git diffs, isolates dependencies with uv, supports pytest cells, and exports notebooks into reusable formats including scripts, HTML, and WASM dashboards.
CODECUT.AI • Shared by Khuyen Tran
EuroPython 2026: Celebrating 25 Years
What's happening at EuroPython 2026? The conference celebrates its 25th anniversary this year in Kraków, Poland. This week on the show, organizers Mia Bajić and Daria Linhart Grudzien join me to discuss this year's conference.
REAL PYTHON podcast
SQLPyHelper: Unified DataBase API
SQLPyHelper is a Python library that provides a unified API across SQLite, PostgreSQL, MySQL, SQL Server, and Oracle. It has async support for FastAPI, cross-database migration, connection pooling, and transactions.
DEV.TO • Shared by Adebayo Olaonipekun
Stroll Down Startup Lane
PyCon's Startup Row is a stretch of booths where early-stage companies built on Python show off what they're creating. In this episode, Talk Python interviews a host of folks from this year's booths.
TALK PYTHON podcast
Pyodide 314.0 Release
This post announces the Pyodide 314.0 release and describes its features, including a focus on standardization and packaging. You can now build Pyodide wheels and post them to PyPI.
PYODIDE.ORG
The Smallest Brain You Can Build
A perceptron explained from scratch in Python, with interactive demos. Learn weights, bias, the decision boundary, epochs, learning rate, and why you normalize data.
DEVARSH RANPARA
Are You Expected to Run 5 Type-Checkers Now?
Library maintainers may feel overwhelmed by the plurality of type checkers that exist. We offer some guidance on how to focus their efforts where they matter most.
MARCO GORELLI
How to Tell if Your Python Mock Is Actually Working
A test that passes because the real API returned an error is not a passing test. Here's how to verify your mock is intercepting, and fix it when it isn't.
BOB BELDERBOS
Cursor vs Windsurf: Which AI Code Editor Is Best for Python?
Compare Cursor vs Windsurf for Python across code completion, multi-file editing, and debugging to choose the right editor for your workflow.
REAL PYTHON
Tricky Python Quiz
A tricky Python quiz game about surprising edge cases, weird outputs, and traps with questions from the popular WTFPython GitHub repo.
ADARSHD.DEV • Shared by Adarsh Divakaran
Free Threading Internals: Deferred Reference Counting
This is a follow up to Victor's article on reference counting covering more complex counting mechanisms including immortal objects.
VICTOR STINNER
Projects & Code
uuid-utils: Rust-Based Replacement for Python's UUID
GITHUB.COM/AMINALAEE • Shared by Amin
django-deploy-probes: Django Health & Startup Endpoints
GITHUB.COM/EMFPDLZJ • Shared by minjeong bak
NumCircBuf: High-Performance Numerical Circular Buffers
GITHUB.COM/BASIMALI-AI • Shared by Syed Basim Ali
Events
Weekly Real Python Office Hours Q&A (Virtual)
June 17, 2026
REALPYTHON.COM
PyData Bristol Meetup
June 18, 2026
MEETUP.COM
PyLadies Dublin
June 18, 2026
PYLADIES.COM
Python for (Almost) Everything
June 18 to June 19, 2026
MEETUP.COM
PyCon Singapore 2026
June 19 to June 22, 2026
PYCON.SG
Happy Pythoning!
This was PyCoder's Weekly Issue #739.
View in Browser »
[ Subscribe to 🐍 PyCoder's Weekly 💌 - Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]
16 Jun 2026 7:30pm GMT
Ari Lamstein: Upcoming O’Reilly Workshop: Building Data Apps with Streamlit and Copilot (July 2026)
On July 9 (9am-1pm Pacific), I'll be teaching a 4‑hour live workshop for O'Reilly: Building Data Apps with Streamlit and Copilot.
This is the second time I've run this workshop, and I've made several improvements based on what I learned the first time.
If you work in Python and want to turn your analyses into interactive, shareable tools, this workshop is designed for you. We'll start from a Jupyter notebook and build a complete Streamlit app that lets users explore a dataset through interactive controls, charts, and maps. Along the way, we'll use Copilot to speed up development and discover Streamlit features more efficiently.
What we'll cover
- Structuring a Streamlit app
- Working with user input (select boxes, filters, etc.)
- Creating interactive graphics with Plotly
- Organizing the UI with columns and tabs
- Deploying your app to Streamlit Cloud
The workshop is hands‑on: you'll build the app step‑by‑step, and by the end you'll have a working project you can adapt to your own data.
What You'll Build
Here's a screenshot from the app we'll build together:
The app lets users choose a state and demographic statistic, explore how it changes over time, and view the data as a chart, map, or table.
And while the example uses demographic data, the skills you'll learn-structuring an app, building interactive controls, and creating dynamic visualizations-apply to any Streamlit project you want to build.
Who is this for?
- Data scientists and analysts who want to make their work more interactive
- Python users who want to build dashboards without learning web development
- Anyone curious about Streamlit or Copilot
How to Register
The workshop is hosted on O'Reilly, which is a membership platform. If you're not already a member, I have a 30-day free trial you can use. To register for the workshop with the free trial:
- Start your free trial at this link
- Then register for the workshop itself
Also worth knowing: the workshop is recorded. So if July 9 doesn't work for you, it's still worth registering - you'll have access to the recording.
I'd love to see you there.
16 Jun 2026 6:38pm GMT
Mariatta: Waitlisted for the Core Devs Sprint: When the Bad News was Also the Good News
Last week, I learned that I was one of 17 people waitlisted for the Python Core Devs Sprint at OpenAI this year.
A waitlist that long realistically means I probably won't get in. I was sad, of course. The sprint alternates between Europe and the US. Traveling to Europe is … hard and complicated. I couldn't go to last year's in Europe, because of the location and work conflict. I was really hoping to go to this year's US sprint. Next year it will be back in Europe, out of reach again. That's potentially not sprinting for three years in a row.
16 Jun 2026 4:00pm GMT
Django community aggregator: Community blog posts
Cheating as a programming discipline
Great programmers cheat. A hard problem gets quietly swapped for an easier one; a transaction-grade database is replaced by a flat file nobody misses; machinery everyone else considers mandatory simply never gets built. They know a lot - and that's exactly why they get away with it.

16 Jun 2026 11:00am GMT
15 Jun 2026
Django community aggregator: Community blog posts
LLM Inspired Development
How Claude inadvertently suggested new features for my personal site.
15 Jun 2026 3:28pm GMT
14 Jun 2026
Django community aggregator: Community blog posts
Understanding Memory Usage in Django Webserver Workers

If you are coming from the PHP world, you might be used to thinking that when a request reaches the web server, everything is parsed and processed from scratch. In Python, however, the behavior is a little different.
A Python web server (for example, Gunicorn) starts one or more worker processes and then continuously accepts and processes requests as a running server application. In this article, I will explore how memory is managed in that environment.
Startup time vs. request execution
When you run a Django web server (either the development server or a Gunicorn-based deployment), there are two phases of execution: startup, when the server application is initialized, and the request/response cycle, when a request is received and processed to return a response.
During startup, wsgi.py and get_wsgi_application() are executed once per worker at boot time, Django settings are evaluated, applications listed in INSTALLED_APPS are imported and registered, ready() methods of app configs are called, and URL patterns are compiled.
During request execution, the middleware chain processes the request, the URL is resolved, the view is executed, context managers run within the view, the template is rendered, the middleware chain processes the response, and the response is returned.
The Django development server runs a single worker process but uses threads to handle concurrent requests. Gunicorn runs multiple worker processes (separate copies of the server application). A common rule of thumb for Gunicorn configuration is (2 × number_of_CPU_cores) + 1 workers.
Little demo of shared memory between requests
If you have mutable globals in your Django code, they will be shared within the same worker process, even across threads.
Here's a simple example for demonstration purposes (note that this is an antipattern and should never be used in production):
from django.http import JsonResponse
from django.utils.timezone import now
TIMESTAMPS = []
def show_timestamps(request):
TIMESTAMPS.append(now())
return JsonResponse({"timestamps": TIMESTAMPS})
The output would be (manually indented for readability):
{"timestamps": [
"2026-06-08T19:48:42.044Z",
"2026-06-08T19:48:43.875Z",
"2026-06-08T19:48:44.776Z"
]}
If you serve this application with Gunicorn using multiple workers and open the view in several tabs, refreshing them a few times, you will see that the timestamp list keeps growing. However, because each request may be served by a different worker, the timestamps returned by each worker will differ.
How RAM is managed
1. Compilation/setup state is persistent
When Django starts and the application server is initialized, the following objects live in process memory for the lifetime of the worker process:
django.apps.registry.Apps(the app registry with all models)- URL resolvers (
urlpatterns) - Middleware instances (if instantiated at startup)
- Cached template engines
- Database connection configurations (not connections themselves)
This memory normally remains allocated until the worker process restarts.
2. Execution state lives per-request
Each request creates objects that are local to that request's call stack-views, forms, querysets, serializers, and so on. Once the response is sent:
- Python's garbage collector normally reclaims those objects.
- There is no explicit Django cleanup; Django relies on Python's normal memory management.
- Request-scoped objects are cleaned up only after they go out of scope and no references to them remain.
Common bugs to avoid
1. Mutable module-level state
A mutable object defined at the module level persists across all requests handled by that worker process and will accumulate data from every user.
❌ BAD:
_recent_users = []
def dashboard(request):
# leaks across requests!
_recent_users.append(request.user.id)
return render(
request,
"dashboard.html",
{"recent": _recent_users}
)
✅ GOOD - state belongs in the DB, cache, or session:
def dashboard(request):
recent_users = get_recent_users_from_db()
return render(
request,
"dashboard.html",
{"recent": recent_users}
)
2. Storing per-request data on a shared instance
Middleware instances live for the lifetime of the worker process. Storing anything request-specific on self means it will be shared across every request handled by that worker.
❌ BAD:
class AuditMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# shared across all requests!
self.current_user = None
def __call__(self, request):
# User A's data visible when User B's request runs
self.current_user = request.user
return self.get_response(request)
✅ GOOD - attach data to the request object, which is scoped to a single request:
class AuditMiddleware:
def __init__(self, get_response):
# only immutable config on self
self.get_response = get_response
def __call__(self, request):
# scoped to this request's lifetime
request.current_user = request.user
return self.get_response(request)
3. Class-level cache shared across instances
A class attribute is shared across all instances of that class and therefore across all requests. The first user's data may be cached and returned to everyone else.
❌ BAD:
class ReportGenerator:
# class attribute, shared across all instances and all requests!
_cache = {}
def get_data(self, user):
if "data" not in self._cache:
# first user's data cached for everyone
self._cache["data"] = expensive_query(user)
return self._cache["data"]
✅ GOOD - use the cache framework with a user-scoped key:
from django.core.cache import cache
class ReportGenerator:
def get_data(self, user):
key = f"report:{user.id}"
return cache.get_or_set(
key,
lambda: expensive_query(user),
timeout=300,
)
Mental model
Before writing module-level code, ask yourself:
"If 1000 different users hit this worker, and this variable exists for all of them, is that safe?"
| Location | Lifetime | Safe for user data? |
|---|---|---|
| Local variable in a view/function | Single request | ✅ Yes |
request object attributes |
Single request | ✅ Yes |
threading.local() |
Single thread | ✅ Yes |
| Database / cache with scoped keys | Persistent but keyed | ✅ Yes |
self on a middleware/class instance |
Worker lifetime | ⚠️ Only immutable config |
| Module-level mutable variable | Worker lifetime | ❌ Never user data |
| Class-level mutable attribute | Worker lifetime | ❌ Never user data |
Rule of thumb: immutable configuration (numbers, booleans, strings, bytes, tuples, frozensets, and None) belongs at module level; anything that varies by request, user, or time belongs in the request cycle.
Conclusion
A Django worker process has two separate memory areas: a startup state that lives for the lifetime of the worker process (app registry, URL resolvers, middleware instances, template engines) and a request state that exists only while handling a request (views, querysets, forms) and is reclaimed by Python's garbage collector afterward.
The main rule is that immutable configuration can live at the module level, but anything that depends on the current user or request should stay inside the request cycle, such as local variables or the request object.
That's because module-level variables, class attributes, and middleware instance state are shared by all requests handled by the same worker, making them unsafe for user-specific data and a common cause of memory leaks and data exposure bugs.
Cover Picture by Jon Tyson
14 Jun 2026 5:00pm GMT
09 Jun 2026
Planet 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
Planet 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 |
|
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:
- We need a type that our client code can use in its type annotations; it needs to be public.
- 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.
- 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:
- a public
NewType, which gives us our public name... - which wraps a private class with entirely private attributes, to give us an actual data structure, while not exposing the constructor,
- 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 |
|
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 |
|
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.
-
The overhead is minimal, but it is not completely zero. The suggested idiom for converting to a
NewTypeis 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
04 Apr 2026
Planet Twisted
Donovan Preston: Using osascript with terminal agents on macOS
Here is a useful trick that is unreasonably effective for simple computer use goals using modern terminal agents. On macOS, there has been a terminal osascript command since the original release of Mac OS X. All you have to do is suggest your agent use it and it can perform any application control action available in any AppleScript dictionary for any Mac app. No MCP set up or tools required at all. Agents are much more adapt at using rod terminal commands, especially ones that haven't changed in 30 years. Having a computer control interface that hasn't changed in 30 years and has extensive examples in the Internet corpus makes modern models understand how to use these tools basically Effortlessly. macOS locks down these permissions pretty heavily nowadays though, so you will have to grant the application control permission to terminal. But once you have done that, the range of possibilities for commanding applications using natural language is quite extensive. Also, for both Safari and chrome on Mac, you are going to want to turn on JavaScript over AppleScript permission. This basically allows claude or another agent to debug your web applications live for you as you are using them.In chrome, go to the view menu, developer submenu, and choose "Allow JavaScript from Apple events". In Safari, it's under the safari menu, settings, developer, "Allow JavaScript from Apple events". Then you can do something like "Hey Claude, would you Please use osascript to navigate the front chrome tab to hacker news". Once you suggest using OSA script in a session it will figure out pretty quickly what it can do with it. Of course you can ask it to do casual things like open your mail app or whatever. Then you can figure out what other things will work like please click around my web app or check the JavaScript Console for errors. Another very important tips for using modern agents is to try to practice using speech to text. I think speaking might be something like five times faster than typing. It takes a lot of time to get used to, especially after a lifetime of programming by typing, but it's a very interesting and a different experience and once you have a lot of practice It starts to to feel effortless.
04 Apr 2026 1:31pm GMT