What’s up Python? d-strings, SSE in Pydantic and... a new Python?
Not sure if LLM or aging make time go faster
Summary
Wow, it’s March already?
I blinked, and a whole month passed while:
Pydantic decided to rewrite Python just in case AI turns out to be useful.
FastAPI when full retro and implemented a tech from the 2000’ for notifications and giggles.
d-strings appeared out of nowhere. Because t-strings and f-strings are so last year.
And we may have inline dictionary type hints now.
Substack finally added code coloration \o/
Ok. Ok. I’m ready.
Pydantic creates a whole new Python implementation
Pydantic started as a validation library. Then the team moved to release Logfire, an observability SaaS. And, still using the validation models as a way to structure inputs and outputs, they came up with an AI workflow library.
It’s already Game of Thrones level of character development here, but then Samuel Colvin went on to announce that they have created Monty, an entirely new Python implementation in Rust.
Now this is a twist!
They have been using and advocating for AI coding for some time, hence Pydantic AI, and the AI capabilities in LogFire. One of the challenges with AI is to:
Get reliable results.
Fast, without burning through a lot of tokens.
Without exposing yourself wide open.
There is a race to provide the platform that will give programmers the best experience for this. Projects like godonlin and spritz are popping right and left.
The thesis of Pydantic’s team is that models are more efficient when programming than when trying to stitch together a bunch of CLI tools or calling MCP. Something that OpenClaw hinted at.
So they went on and created a new Python VM that:
Is completely sandboxed. By default, it can’t do I/O, system calls, etc. You have to give it permissions and provide implementations.
Starts under 1μs.
Can be paused and resumed.
Can be snapshotted (you can save its entire state to disk, and reload it).
Can be called from Rust, Python, or Javascript.
Can control RAM usage and execution time.
Of course, to be able to do all that, Monty doesn’t implement the entirety of Python. Right now, it doesn’t even have classes, and can only access a small subset of the standard library.
In this sense, it has a bit of the characteristics of:
Pyiodide for the full sandbox
Starlark for the selective exposure of features. Remember, we had an article on this.
Docker for the ability to mount scoped filesystems and snapshotting.
MicroPython for the small footprints and fast startup.
You can install it with pip install pydantic-monty, and start sending code to it:
>>> import pydantic_monty
>>> limits = pydantic_monty.ResourceLimits(max_duration_secs=5.0, max_memory=1024 * 1024)
>>> vm = pydantic_monty.Monty('x * y * z', inputs=['x', 'y', 'z'])
>>> result = vm.run(inputs={"x": 2, "y": 3, "z": 4}, limits=limits)
>>> print(f'Calculation with generous limits: {result}')
Calculation with generous limits: 24Give the very alpha status of the tech, expect lots of bugs.
It’s a bit of a weird beast, and honestly, I don’t know if it’s going to be the ideal interface for LLM it’s supposed to be, but I don’t care because even outside of AI, it has wild potential!
You can use it as a user-exposed scripting language, and risk nothing while offering a full-featured programming language for custom behavior.
You can use it for small devices that need to be switched on and off often because, hey, it starts fast, and it can be snapshotted.
It’s in Rust, so you can WASM it, which means you could use that to program in Python in the browser.
And of course, as a configuration language to replace Starlark.
It’s a bold strategy Cotton. Let’s see if it pays off...
PEP 821, a proposal for a dedented strings
You know how creating multi-line strings suck in Python?
>>> def broken_description():
... return """
... This is a superb text
... but it will be completely
... indented.
... """
...
... print(broken_description())
...
... def ugly_but_correct():
... return """This is a superb text
... but it will be completely
... indented."""
...
... print(ugly_but_correct())
This is a superb text
but it will be completely
indented.
This is a superb text
but it will be completely
indented.We do have the dedent function, but do you really know how to use it?
>>> from textwrap import dedent
...
... def are_you_sure():
...
... return dedent(
... """This is a superb text
... but it will be completely
... indented.
... """)
...
... print(are_you_sure())
...
... def like_this_maybe():
...
... return dedent("""This is a superb text
... but it will be completely
... indented.
... """)
...
... print(like_this_maybe())
...
... def no_it_was_like_this():
... # need a backslash to avoid additional line break
... return dedent("""\
... This is a superb text
... but it will be completely
... indented.
... """)
...
... print(no_it_was_like_this())
This is a superb text
but it will be completely
indented.
This is a superb text
but it will be completely
indented.
This is a superb text
but it will be completely
indented.So, the PEP proposal is to add d-strings:
def nice_solution():
return d"""
This is a superb text
but it will be completely
indented.
"""It’s very natural, doesn’t require an import, is nicely dedented and will not require \ to avoid a sneaky line break. It also removes indentation BEFORE processing escape sequences, allowing the use of continuations with \ if you want to.
Can’t wait to use rdt"{}" in a codebase. Hitting 3.15, hopefully.
PEP 764 – Inline typed dictionaries
This one is simple.
You know how it’s really verbose to type a little dictionary in Python?
from typing import TypedDict
class MovieDict(TypedDict):
name: str:
year: int
def get_movie() -> MovieDict:
return {
'name': 'Blade Runner',
'year': 1982,
}
Well, this PEPS says let’s skip the intermediary:
from typing import TypedDict
def get_movie() -> TypedDict[{'name': str, 'year': int}]:
return {
'name': 'Blade Runner',
'year': 1982,
}Yes, please.
Fast API gets Server Side Events
Sometimes, you want to notify the browser in real time, and you don’t want to setup a full duplex communication and deal with the disconnections that come with that, plus the stateful nature of the whole deal.
I know, right?
What I mean is that 99% of the time, you don’t want websockets, you just want to say “hey browser, I got something new” and that’s it.
For this, there is an old tech called Server Side Event, that lets the browser just do a basic HTTP request to your server, and then keep a connection alive to get a stream of events.
Seems simple. Easy. Could solve tons of sync and notification problems.
And it is. And it does.
SSE are the forgotten child of the Web, and an undervalued tool that can do a lot with very little.
And finally, FastAPI lets you use them:
from collections.abc import AsyncIterable, Iterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None
items = [
Item(name="Plumbus", description="A multi-purpose household device."),
Item(name="Portal Gun", description="A portal opening device."),
Item(name="Meeseeks Box", description="A box that summons a Meeseeks."),
]
# When a browser does a GET on this, it will receive the results as a stream
# of events
@app.get("/items/stream", response_class=EventSourceResponse)
async def sse_items() -> AsyncIterable[Item]:
# There can be minutes between yields in real like. Here they are
# one after the other, but you can imagine items as beeing a generator
# fetching from any source of events
for item in items:
yield item
On the JS side, calling it would look like this:
const source = new EventSource("http://your_website.tld/logs/stream");
// This will pop for each item you yield
// Data is in the JSON format by default
source.onmessage = (event) => {
console.log("log line:", event.data);
};SSE has the nice quality of working through most proxies, being cacheable, using a standard port, and transparently working with regular keep-alive infra without having complicated reconnection semantics on the Python side.
It’s basically polling, without the overhead.
Hope we’ll get a good story for it one day in Django. Would be nice to catch up with 2009 innovations.
Which was 17 years ago, in case you forgot how bloody old you are.
And Moar
PEP 821 suggests a new type hints:
Unpack[TypedDict]. The goal is to be able to type**kwargswithout the verbosity of a protocol.The Python dev survey is here. Give a piece of your mind.
PEP 747 - Annotating Type Forms has been accepted. This is a bit meta, as those are types meant to type function that accept types as parameters. Libraries like attrs and pydantic need this. You probably don’t.
