2025-10-09 11:37:06
TIL: Testing different Python versions with uv with-editable and uv-test
While tinkering with upgrading various projects to handle Python 3.14 I finally figured out a universaluv
recipe for running the tests for the current project in any specified version of Python:
uv run --python 3.14 --isolated --with-editable '.[test]' pytest
This should work in any directory with a pyproject.toml
(or even a setup.py
) that defines a test
set of extra dependencies and uses pytest
.
The --with-editable '.[test]'
bit ensures that changes you make to that directory will be picked up by future test runs. The --isolated
flag ensures no other environments will affect your test run.
I like this pattern so much I built a little shell script that uses it, shown here. Now I can change to any Python project directory and run:
uv-test
Or for a different Python version:
uv-test -p 3.11
I can pass additional pytest
options too:
uv-test -p 3.11 -k permissions
2025-10-09 07:43:43
This isn't necessarily surprising, but it's worth noting anyway. Claude Sonnet 4.5 is capable of building a full Datasette plugin now.
I've seen models complete aspects of this in the past, but today is the first time I've shipped a new plugin where every line of code and test was written by Claude, with minimal prompting from myself.
The plugin is called datasette-os-info. It's a simple debugging tool - all it does is add a /-/os
JSON page which dumps out as much information as it can about the OS it's running on. Here's a live demo on my TIL website.
I built it to help experiment with changing the Docker base container that Datasette uses to publish images to one that uses Python 3.14.
Here's the full set of commands I used to create the plugin. I started with my datasette-plugin cookiecutter template:
uvx cookiecutter gh:simonw/datasette-plugin
[1/8] plugin_name (): os-info
[2/8] description (): Information about the current OS
[3/8] hyphenated (os-info):
[4/8] underscored (os_info):
[5/8] github_username (): datasette
[6/8] author_name (): Simon Willison
[7/8] include_static_directory ():
[8/8] include_templates_directory ():
This created a datasette-os-info
directory with the initial pyproject.toml
and tests/
and datasette_os_info/__init__.py
files. Here's an example of that starter template.
I created a uv
virtual environment for it, installed the initial test dependencies and ran pytest
to check that worked:
cd datasette-os-info
uv venv
uv sync --extra test
uv run pytest
Then I fired up Claude Code in that directory in YOLO mode:
claude --dangerously-skip-permissions
(I actually used my claude-yolo
shortcut which runs the above.)
Then, in Claude, I told it how to run the tests:
Run uv run pytest
When that worked, I told it to build the plugin:
This is a Datasette plugin which should add a new page /-/os which returns pretty-printed JSON about the current operating system - implement it. I want to pick up as many details as possible across as many OS as possible, including if possible figuring out the base image if it is in a docker container - otherwise the Debian OS release name and suchlike would be good
... and that was it! Claude implemented the plugin using Datasette's register_routes() plugin hook to add the /-/os
page,and then without me prompting it to do so built this basic test as well.
It ran the new test, spotted a bug (it had guessed a non-existent Response(..., default_repr=)
parameter), fixed the bug and declared itself done.
I built myself a wheel:
uv pip install build
uv run python -m build
Then uploaded that to an S3 bucket and deployed it to test it out using datasette publish ... --install URL-to-wheel
. It did exactly what I had hoped - here's what that /-/os
page looked like:
{
"platform": {
"system": "Linux",
"release": "4.4.0",
"version": "#1 SMP Sun Jan 10 15:06:54 PST 2016",
"machine": "x86_64",
"processor": "",
"architecture": [
"64bit",
""
],
"platform": "Linux-4.4.0-x86_64-with-glibc2.41",
"python_version": "3.14.0",
"python_implementation": "CPython"
},
"hostname": "localhost",
"cpu_count": 2,
"linux": {
"os_release": {
"PRETTY_NAME": "Debian GNU/Linux 13 (trixie)",
"NAME": "Debian GNU/Linux",
"VERSION_ID": "13",
"VERSION": "13 (trixie)",
"VERSION_CODENAME": "trixie",
"DEBIAN_VERSION_FULL": "13.1",
"ID": "debian",
"HOME_URL": "https://www.debian.org/",
"SUPPORT_URL": "https://www.debian.org/support",
"BUG_REPORT_URL": "https://bugs.debian.org/"
},
"debian_version": "13.1",
"kernel": {
"sysname": "Linux",
"nodename": "localhost",
"release": "4.4.0",
"version": "#1 SMP Sun Jan 10 15:06:54 PST 2016",
"machine": "x86_64"
},
"cpu_model": "unknown",
"memory": {
"MemTotal": "1048576 kB",
"MemFree": "958244 kB",
"MemAvailable": "958244 kB",
"SwapTotal": "0 kB",
"SwapFree": "0 kB"
}
},
"environment": {
"HOME": "/root",
"PATH": "/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
},
"python_executable": "/usr/local/bin/python3.14"
}
I dread to think how long it would have taken me to figure out all of those different useful OS details!
To finish up, I had Claude fill out the README via a couple of extra prompts:
Add usage instructions to the README (about the /-/os page)
Then had a brief conversation about how safe the output was:
Is there anything potentially sensitive in that output?
Claude said:
Yes, there are several potentially sensitive items in the output:
Most sensitive:
- hostname - Could reveal internal naming schemes or organization details
- Environment variables:
- HOME - Reveals user's home directory path
- USER - Reveals the username
- PATH - Reveals filesystem structure and software installation locations
- VIRTUAL_ENV - Could reveal project paths and directory structure
- python_executable - Reveals filesystem paths and user directories
Moderately sensitive:
- CPU count and memory info - Could reveal infrastructure capacity
- Kernel version details - Could potentially be used to identify specific vulnerabilities
Considerations:
- This endpoint is accessible without authentication by default (at /-/os)
- If you're running Datasette on a public server, this information would be visible to anyone
- The information could help attackers understand your infrastructure setup
Suggestions:
You might want to:
- Add authentication requirements to this endpoint
- Make certain fields optional/configurable
- Add a warning in the README that this exposes system information
- Consider filtering out or redacting sensitive paths (replace usernames with ***, etc.)
Would you like me to add any of these protections to the plugin?
In this case I'm fine with the current behavior provided people understand the risks, so I told it:
Add the warning to the README
And then (because the warning it added was quite long):
Drop the bit about configuring the permission system, just have a short warning telling people to review what it exposes
And requested an extra README note:
Add a note that you can also see the output by running: datasette --get /-/os
Three last prompts:
Add uv.lock to gitignore
Drop Python 3.9 and add Python 3.14 - to the GitHub workflows, also min version in pyproject.toml
Bump to setup-python@v6
... and that was the project finished. I pushed it to GitHub, configured Trusted Publishing for it on PyPI and posted the 0.1 release, which ran this GitHub Actions publish.yml and deployed that release to datasette-os-info on PyPI.
Now that it's live you can try it out without even installing Datasette using a uv
one-liner like this:
uv run --isolated \
--with datasette-os-info \
datasette --get /-/os
That's using the --get PATH
CLI option to show what that path in the Datasette instance would return, as described in the Datasette documentation.
I've shared my full Claude Code transcript in a Gist.
A year ago I'd have been very impressed by this. Today I wasn't even particularly surprised that this worked - the coding agent pattern implemented by Claude Code is spectacularly effective when you combine it with pre-existing templates, and Datasette has been aroung for long enough now that plenty of examples of plugins have made it into the training data for the leading models.
Tags: plugins, projects, python, ai, datasette, generative-ai, llms, ai-assisted-programming, anthropic, claude, uv, coding-agents, claude-code
2025-10-09 02:36:33
Python 3.14 Is Here. How Fast Is It?
Miguel Grinberg uses some basic benchmarks (likefib(40)
) to test the new Python 3.14 on Linux and macOS and finds some substantial speedups over Python 3.13 - around 27% faster.
The optional JIT didn't make a meaningful difference to his benchmarks. On a threaded benchmark he got 3.09x speedup with 4 threads using the free threading build - for Python 3.13 the free threading build only provided a 2.2x improvement.
Via lobste.rs
Tags: gil, performance, python
2025-10-09 02:08:32
The cognitive debt of LLM-laden coding extends beyond disengagement of our craft. We’ve all heard the stories. Hyped up, vibed up, slop-jockeys with attention spans shorter than the framework-hopping JavaScript devs of the early 2010s, sling their sludge in pull requests and design docs, discouraging collaboration and disrupting teams. Code reviewing coworkers are rapidly losing their minds as they come to the crushing realization that they are now the first layer of quality control instead of one of the last. Asked to review; forced to pick apart. Calling out freshly added functions that are never called, hallucinated library additions, and obvious runtime or compilation errors. All while the author—who clearly only skimmed their “own” code—is taking no responsibility, going “whoopsie, Claude wrote that. Silly AI, ha-ha.”
— Simon Højberg, The Programmer Identity Crisis
Tags: llms, generative-ai, ai, code-review, ai-ethics
2025-10-09 00:12:14
Why NetNewsWire Is Not a Web App
In the wake of Apple removing ICEBlock from the App Store, Brent Simmons talks about why he still thinks his veteran (and actively maintained) NetNewsWire feed reader app should remain a native application.Part of the reason is cost - NetNewsWire is free these days (MIT licensed in fact) and the cost to Brent is an annual Apple developer subscription:
If it were a web app instead, I could drop the developer membership, but I’d have to pay way more money for web and database hosting. [...] I could charge for NetNewsWire, but that would go against my political goal of making sure there’s a good and free RSS reader available to everyone.
A bigger reason is around privacy and protecting users:
Second issue. Right now, if law enforcement comes to me and demands I turn over a given user’s subscriptions list, I can’t. Literally can’t. I don’t have an encrypted version, even — I have nothing at all. The list lives on their machine (iOS or macOS).
And finally it's about the principle of what a personal computing device should mean:
My computer is not a terminal. It’s a world I get to control, and I can use — and, especially, make — whatever I want. I’m not stuck using just what’s provided to me on some other machines elsewhere: I’m not dialing into a mainframe or doing the modern equivalent of using only websites that other people control.
Tags: apple, brent-simmons, macos, netnewswire, ios
2025-10-08 12:10:06
This year's major Python version, Python 3.14, just made its first stable release!
As usual the what's new in Python 3.14 document is the best place to get familiar with the new release:
The biggest changes include template string literals, deferred evaluation of annotations, and support for subinterpreters in the standard library.
The library changes include significantly improved capabilities for introspection in asyncio, support for Zstandard via a new compression.zstd module, syntax highlighting in the REPL, as well as the usual deprecations and removals, and improvements in user-friendliness and correctness.
Subinterpreters look particularly interesting as a way to use multiple CPU cores to run Python code despite the continued existence of the GIL. If you're feeling brave and your dependencies cooperate you can also use the free-threaded build of Python 3.14 - now officially supported - to skip the GIL entirely.
A new major Python release means an older release hits the end of its support lifecycle - in this case that's Python 3.9. If you maintain open source libraries that target every supported Python versions (as I do) this means features introduced in Python 3.10 can now be depended on! What's new in Python 3.10 lists those - I'm most excited by structured pattern matching (the match/case
statement) and the union type operator, allowing int | float | None
as a type annotation in place of Optional[Union[int, float]]
.
If you use uv
you can grab a copy of 3.14 using:
uv self update
uv python upgrade 3.14
uvx [email protected]
Or for free-threaded Python 3.1;:
uvx [email protected]
The uv
team wrote about their Python 3.14 highlights in their announcement of Python 3.14's availability via uv
.
The GitHub Actions setup-python action includes Python 3.14 now too, so the following YAML snippet in will run tests on all currently supported versions:
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
Full example here for one of my many Datasette plugin repos.
Tags: gil, open-source, python, github-actions, psf, uv