<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: packaging</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/packaging.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2026-04-28T05:23:05+00:00</updated><author><name>Simon Willison</name></author><entry><title>What's new in pip 26.1 - lockfiles and dependency cooldowns!</title><link href="https://simonwillison.net/2026/Apr/28/pip-261/#atom-tag" rel="alternate"/><published>2026-04-28T05:23:05+00:00</published><updated>2026-04-28T05:23:05+00:00</updated><id>https://simonwillison.net/2026/Apr/28/pip-261/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://ichard26.github.io/blog/2026/04/whats-new-in-pip-26.1/"&gt;What&amp;#x27;s new in pip 26.1 - lockfiles and dependency cooldowns!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Richard Si describes an excellent set of upgrades to Python's default &lt;code&gt;pip&lt;/code&gt; tool for installing dependencies.&lt;/p&gt;
&lt;p&gt;This version drops support for Python 3.9 - fair enough, since it's been EOL &lt;a href="https://devguide.python.org/versions/"&gt;since October&lt;/a&gt;. macOS still ships with &lt;code&gt;python3&lt;/code&gt; as a default Python 3.9, so I tried out the new Python version against Python 3.14 like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv python install 3.14
mkdir /tmp/experiment
cd /tmp/experiment
python3.14 -m venv venv
source venv/bin/activate
pip install -U pip
pip --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This confirmed I had &lt;code&gt;pip 26.1&lt;/code&gt; - then I tried out the new lock files:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip lock datasette llm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This installs Datasette and LLM and all of their dependencies and writes the whole lot to a 519 line &lt;code&gt;pylock.toml&lt;/code&gt; file - &lt;a href="https://gist.github.com/simonw/ff52c33f4d3a381b8e53c6a3aa0213f8"&gt;here's the result&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new release also supports dependency cooldowns, &lt;a href="https://simonwillison.net/2026/Mar/24/package-managers-need-to-cool-down/"&gt;discussed here previously&lt;/a&gt;, via the new &lt;code&gt;--uploaded-prior-to PXD&lt;/code&gt; option where X is a number of days. The format is &lt;code&gt;P-number-of-days-D&lt;/code&gt;, following &lt;a href="https://en.wikipedia.org/wiki/ISO_8601#Durations"&gt;ISO duration format&lt;/a&gt; but only supporting days.&lt;/p&gt;
&lt;p&gt;I shipped a new release of LLM, version 0.31, &lt;a href="https://simonwillison.net/2026/Apr/24/llm/"&gt;three days ago&lt;/a&gt;. Here's how to use the new &lt;code&gt;--uploaded-prior-to P4D&lt;/code&gt; option to ask for a version that is at least 4 days old.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install llm --uploaded-prior-to P4D
venv/bin/llm --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gave me version 0.30.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/w2oiaq/what_s_new_pip_26_1_lockfiles_dependency"&gt;Lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pip"&gt;pip&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="pip"/><category term="python"/><category term="security"/><category term="supply-chain"/></entry><entry><title>The Axios supply chain attack used individually targeted social engineering</title><link href="https://simonwillison.net/2026/Apr/3/supply-chain-social-engineering/#atom-tag" rel="alternate"/><published>2026-04-03T13:54:53+00:00</published><updated>2026-04-03T13:54:53+00:00</updated><id>https://simonwillison.net/2026/Apr/3/supply-chain-social-engineering/#atom-tag</id><summary type="html">
    &lt;p&gt;The Axios team have published a &lt;a href="https://github.com/axios/axios/issues/10636"&gt;full postmortem&lt;/a&gt; on the supply chain attack which resulted in a malware dependency going out &lt;a href="https://simonwillison.net/2026/Mar/31/supply-chain-attack-on-axios/"&gt;in a release the other day&lt;/a&gt;, and it involved a sophisticated social engineering campaign targeting one of their maintainers directly. Here's Jason Saayman'a description of &lt;a href="https://github.com/axios/axios/issues/10636#issuecomment-4180237789"&gt;how that worked&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;so the attack vector mimics what google has documented here: &lt;a href="https://cloud.google.com/blog/topics/threat-intelligence/unc1069-targets-cryptocurrency-ai-social-engineering"&gt;https://cloud.google.com/blog/topics/threat-intelligence/unc1069-targets-cryptocurrency-ai-social-engineering&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;they tailored this process specifically to me by doing the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;they reached out masquerading as the founder of a company they had cloned the companys founders likeness as well as the company itself.&lt;/li&gt;
&lt;li&gt;they then invited me to a real slack workspace. this workspace was branded to the companies ci and named in a plausible manner. the slack was thought out very well, they had channels where they were sharing linked-in posts, the linked in posts i presume just went to the real companys account but it was super convincing etc. they even had what i presume were fake profiles of the team of the company but also number of other oss maintainers.&lt;/li&gt;
&lt;li&gt;they scheduled a meeting with me to connect. the meeting was on ms teams. the meeting had what seemed to be a group of people that were involved.&lt;/li&gt;
&lt;li&gt;the meeting said something on my system was out of date. i installed the missing item as i presumed it was something to do with teams, and this was the RAT.&lt;/li&gt;
&lt;li&gt;everything was extremely well co-ordinated looked legit and was done in a professional manner.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;A RAT is a Remote Access Trojan - this was the software which stole the developer's credentials which could then be used to publish the malicious package.&lt;/p&gt;
&lt;p&gt;That's a &lt;em&gt;very effective&lt;/em&gt; scam. I join a lot of meetings where I find myself needing to install Webex or Microsoft Teams or similar at the last moment and the time constraint means I always click "yes" to things as quickly as possible to make sure I don't join late.&lt;/p&gt;
&lt;p&gt;Every maintainer of open source software used by enough people to be worth taking in this way needs to be familiar with this attack strategy.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/social-engineering"&gt;social-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="open-source"/><category term="packaging"/><category term="security"/><category term="social-engineering"/><category term="supply-chain"/></entry><entry><title>LiteLLM Hack: Were You One of the 47,000?</title><link href="https://simonwillison.net/2026/Mar/25/litellm-hack/#atom-tag" rel="alternate"/><published>2026-03-25T17:21:04+00:00</published><updated>2026-03-25T17:21:04+00:00</updated><id>https://simonwillison.net/2026/Mar/25/litellm-hack/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://futuresearch.ai/blog/litellm-hack-were-you-one-of-the-47000/"&gt;LiteLLM Hack: Were You One of the 47,000?&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Daniel Hnyk used the &lt;a href="https://console.cloud.google.com/bigquery?p=bigquery-public-data&amp;amp;d=pypi"&gt;BigQuery PyPI dataset&lt;/a&gt; to determine how many downloads there were of &lt;a href="https://simonwillison.net/2026/Mar/24/malicious-litellm/"&gt;the exploited LiteLLM packages&lt;/a&gt; during the 46 minute period they were live on PyPI. The answer was 46,996 across the two compromised release versions (1.82.7 and 1.82.8).&lt;/p&gt;
&lt;p&gt;They also identified 2,337 packages that depended on LiteLLM - 88% of which did not pin versions in a way that would have avoided the exploited version.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/hnykda/status/2036834100342825369"&gt;@hnykda&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="pypi"/><category term="python"/><category term="security"/><category term="supply-chain"/></entry><entry><title>Package Managers Need to Cool Down</title><link href="https://simonwillison.net/2026/Mar/24/package-managers-need-to-cool-down/#atom-tag" rel="alternate"/><published>2026-03-24T21:11:38+00:00</published><updated>2026-03-24T21:11:38+00:00</updated><id>https://simonwillison.net/2026/Mar/24/package-managers-need-to-cool-down/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://nesbitt.io/2026/03/04/package-managers-need-to-cool-down.html"&gt;Package Managers Need to Cool Down&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Today's &lt;a href="https://simonwillison.net/2026/Mar/24/malicious-litellm/"&gt;LiteLLM supply chain attack&lt;/a&gt; inspired me to revisit the idea of &lt;a href="https://simonwillison.net/2025/Nov/21/dependency-cooldowns/"&gt;dependency cooldowns&lt;/a&gt;, the practice of only installing updated dependencies once they've been out in the wild for a few days to give the community a chance to spot if they've been subverted in some way.&lt;/p&gt;
&lt;p&gt;This recent piece (March 4th) piece by Andrew Nesbitt reviews the current state of dependency cooldown mechanisms across different packaging tools. It's surprisingly well supported! There's been a flurry of activity across major packaging tools, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pnpm.io/blog/releases/10.16#new-setting-for-delayed-dependency-updates"&gt;pnpm 10.16&lt;/a&gt; (September 2025) — &lt;code&gt;minimumReleaseAge&lt;/code&gt; with &lt;code&gt;minimumReleaseAgeExclude&lt;/code&gt; for trusted packages&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/yarnpkg/berry/releases/tag/%40yarnpkg%2Fcli%2F4.10.0"&gt;Yarn 4.10.0&lt;/a&gt; (September 2025) — &lt;code&gt;npmMinimalAgeGate&lt;/code&gt; (in minutes) with &lt;code&gt;npmPreapprovedPackages&lt;/code&gt; for exemptions&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bun.com/blog/bun-v1.3#minimum-release-age"&gt;Bun 1.3&lt;/a&gt; (October 2025) — &lt;code&gt;minimumReleaseAge&lt;/code&gt; via &lt;code&gt;bunfig.toml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://deno.com/blog/v2.6#controlling-dependency-stability"&gt;Deno 2.6&lt;/a&gt; (December 2025) — &lt;code&gt;--minimum-dependency-age&lt;/code&gt; for &lt;code&gt;deno update&lt;/code&gt; and &lt;code&gt;deno outdated&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/astral-sh/uv/releases/tag/0.9.17"&gt;uv 0.9.17&lt;/a&gt; (December 2025) — added relative duration support to existing &lt;code&gt;--exclude-newer&lt;/code&gt;, plus per-package overrides via &lt;code&gt;exclude-newer-package&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ichard26.github.io/blog/2026/01/whats-new-in-pip-26.0/"&gt;pip 26.0&lt;/a&gt; (January 2026) — &lt;code&gt;--uploaded-prior-to&lt;/code&gt; (absolute timestamps only; &lt;a href="https://github.com/pypa/pip/issues/13674"&gt;relative duration support requested&lt;/a&gt;, &lt;strong&gt;update&lt;/strong&gt;: and added &lt;a href="https://ichard26.github.io/blog/2026/04/whats-new-in-pip-26.1/"&gt;in pip 26.1 in April&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://socket.dev/blog/npm-introduces-minimumreleaseage-and-bulk-oidc-configuration"&gt;npm 11.10.0&lt;/a&gt; (February 2026) — &lt;code&gt;min-release-age&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; currently only supports absolute rather than relative dates but Seth Larson &lt;a href="https://sethmlarson.dev/pip-relative-dependency-cooling-with-crontab"&gt;has a workaround for that&lt;/a&gt; using a scheduled cron to update the absolute date in the &lt;code&gt;pip.conf&lt;/code&gt; config file.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pip"&gt;pip&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/npm"&gt;npm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/deno"&gt;deno&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="packaging"/><category term="pip"/><category term="pypi"/><category term="python"/><category term="security"/><category term="npm"/><category term="deno"/><category term="supply-chain"/><category term="uv"/></entry><entry><title>Distributing Go binaries like sqlite-scanner through PyPI using go-to-wheel</title><link href="https://simonwillison.net/2026/Feb/4/distributing-go-binaries/#atom-tag" rel="alternate"/><published>2026-02-04T14:59:47+00:00</published><updated>2026-02-04T14:59:47+00:00</updated><id>https://simonwillison.net/2026/Feb/4/distributing-go-binaries/#atom-tag</id><summary type="html">
    &lt;p&gt;I've been exploring Go for building small, fast and self-contained binary applications recently. I'm enjoying how there's generally one obvious way to do things and the resulting code is boring and readable - and something that LLMs are very competent at writing. The one catch is distribution, but it turns out publishing Go binaries to PyPI means any Go binary can be just a &lt;code&gt;uvx package-name&lt;/code&gt; call away.&lt;/p&gt;
&lt;h4 id="sqlite-scanner"&gt;sqlite-scanner&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/sqlite-scanner"&gt;sqlite-scanner&lt;/a&gt; is my new Go CLI tool for scanning a filesystem for SQLite database files.&lt;/p&gt;
&lt;p&gt;It works by checking if the first 16 bytes of the file exactly match the SQLite magic number sequence &lt;code&gt;SQLite format 3\x00&lt;/code&gt;. It can search one or more folders recursively, spinning up concurrent goroutines to accelerate the scan. It streams out results as it finds them in plain text, JSON or newline-delimited JSON. It can optionally display the file sizes as well.&lt;/p&gt;
&lt;p&gt;To try it out you can download a release from the &lt;a href="https://github.com/simonw/sqlite-scanner/releases"&gt;GitHub releases&lt;/a&gt; - and then &lt;a href="https://support.apple.com/en-us/102445"&gt;jump through macOS hoops&lt;/a&gt; to execute an "unsafe" binary. Or you can clone the repo and compile it with Go. Or... you can run the binary like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uvx sqlite-scanner
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default this will search your current directory for SQLite databases. You can pass one or more directories as arguments:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uvx sqlite-scanner ~ /tmp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add &lt;code&gt;--json&lt;/code&gt; for JSON output, &lt;code&gt;--size&lt;/code&gt; to include file sizes or &lt;code&gt;--jsonl&lt;/code&gt; for newline-delimited JSON. Here's a demo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uvx sqlite-scanner ~ --jsonl --size
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2025/sqlite-scanner-demo.gif" alt="running that command produces a sequence of JSON objects, each with a path and a size key" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;If you haven't been uv-pilled yet you can instead install &lt;code&gt;sqlite-scanner&lt;/code&gt; using &lt;code&gt;pip install sqlite-scanner&lt;/code&gt; and then run &lt;code&gt;sqlite-scanner&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get a permanent copy with &lt;code&gt;uv&lt;/code&gt; use &lt;code&gt;uv tool install sqlite-scanner&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="how-the-python-package-works"&gt;How the Python package works&lt;/h4&gt;
&lt;p&gt;The reason this is worth doing is that &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;uv&lt;/code&gt; and &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt; will work together to identify the correct compiled binary for your operating system and architecture.&lt;/p&gt;
&lt;p&gt;This is driven by file names. If you visit &lt;a href="https://pypi.org/project/sqlite-scanner/#files"&gt;the PyPI downloads for sqlite-scanner&lt;/a&gt; you'll see the following files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-win_arm64.whl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-win_amd64.whl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-musllinux_1_2_x86_64.whl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-musllinux_1_2_aarch64.whl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-manylinux_2_17_x86_64.whl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-manylinux_2_17_aarch64.whl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-macosx_11_0_arm64.whl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite_scanner-0.1.1-py3-none-macosx_10_9_x86_64.whl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I run &lt;code&gt;pip install sqlite-scanner&lt;/code&gt; or &lt;code&gt;uvx sqlite-scanner&lt;/code&gt; on my Apple Silicon Mac laptop Python's packaging magic ensures I get that &lt;code&gt;macosx_11_0_arm64.whl&lt;/code&gt; variant.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://tools.simonwillison.net/zip-wheel-explorer?url=https%3A%2F%2Ffiles.pythonhosted.org%2Fpackages%2F88%2Fb1%2F17a716635d2733fec53ba0a8267f85bd6b6cf882c6b29301bc711fba212c%2Fsqlite_scanner-0.1.1-py3-none-macosx_11_0_arm64.whl#sqlite_scanner/__init__.py"&gt;what's in the wheel&lt;/a&gt;, which is a zip file with a &lt;code&gt;.whl&lt;/code&gt; extension.&lt;/p&gt;
&lt;p&gt;In addition to the &lt;code&gt;bin/sqlite-scanner&lt;/code&gt; the most important file is &lt;code&gt;sqlite_scanner/__init__.py&lt;/code&gt; which includes the following:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;get_binary_path&lt;/span&gt;():
    &lt;span class="pl-s"&gt;"""Return the path to the bundled binary."""&lt;/span&gt;
    &lt;span class="pl-s1"&gt;binary&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;os&lt;/span&gt;.&lt;span class="pl-c1"&gt;path&lt;/span&gt;.&lt;span class="pl-c1"&gt;join&lt;/span&gt;(&lt;span class="pl-s1"&gt;os&lt;/span&gt;.&lt;span class="pl-c1"&gt;path&lt;/span&gt;.&lt;span class="pl-c1"&gt;dirname&lt;/span&gt;(&lt;span class="pl-s1"&gt;__file__&lt;/span&gt;), &lt;span class="pl-s"&gt;"bin"&lt;/span&gt;, &lt;span class="pl-s"&gt;"sqlite-scanner"&lt;/span&gt;)
 
    &lt;span class="pl-c"&gt;# Ensure binary is executable on Unix&lt;/span&gt;
    &lt;span class="pl-k"&gt;if&lt;/span&gt; &lt;span class="pl-s1"&gt;sys&lt;/span&gt;.&lt;span class="pl-c1"&gt;platform&lt;/span&gt; &lt;span class="pl-c1"&gt;!=&lt;/span&gt; &lt;span class="pl-s"&gt;"win32"&lt;/span&gt;:
        &lt;span class="pl-s1"&gt;current_mode&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;os&lt;/span&gt;.&lt;span class="pl-c1"&gt;stat&lt;/span&gt;(&lt;span class="pl-s1"&gt;binary&lt;/span&gt;).&lt;span class="pl-c1"&gt;st_mode&lt;/span&gt;
        &lt;span class="pl-k"&gt;if&lt;/span&gt; &lt;span class="pl-c1"&gt;not&lt;/span&gt; (&lt;span class="pl-s1"&gt;current_mode&lt;/span&gt; &lt;span class="pl-c1"&gt;&amp;amp;&lt;/span&gt; &lt;span class="pl-s1"&gt;stat&lt;/span&gt;.&lt;span class="pl-c1"&gt;S_IXUSR&lt;/span&gt;):
            &lt;span class="pl-s1"&gt;os&lt;/span&gt;.&lt;span class="pl-c1"&gt;chmod&lt;/span&gt;(&lt;span class="pl-s1"&gt;binary&lt;/span&gt;, &lt;span class="pl-s1"&gt;current_mode&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s1"&gt;stat&lt;/span&gt;.&lt;span class="pl-c1"&gt;S_IXUSR&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s1"&gt;stat&lt;/span&gt;.&lt;span class="pl-c1"&gt;S_IXGRP&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s1"&gt;stat&lt;/span&gt;.&lt;span class="pl-c1"&gt;S_IXOTH&lt;/span&gt;)
 
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;binary&lt;/span&gt;
 
 
&lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;main&lt;/span&gt;():
    &lt;span class="pl-s"&gt;"""Execute the bundled binary."""&lt;/span&gt;
    &lt;span class="pl-s1"&gt;binary&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;get_binary_path&lt;/span&gt;()
 
    &lt;span class="pl-k"&gt;if&lt;/span&gt; &lt;span class="pl-s1"&gt;sys&lt;/span&gt;.&lt;span class="pl-c1"&gt;platform&lt;/span&gt; &lt;span class="pl-c1"&gt;==&lt;/span&gt; &lt;span class="pl-s"&gt;"win32"&lt;/span&gt;:
        &lt;span class="pl-c"&gt;# On Windows, use subprocess to properly handle signals&lt;/span&gt;
        &lt;span class="pl-s1"&gt;sys&lt;/span&gt;.&lt;span class="pl-c1"&gt;exit&lt;/span&gt;(&lt;span class="pl-s1"&gt;subprocess&lt;/span&gt;.&lt;span class="pl-c1"&gt;call&lt;/span&gt;([&lt;span class="pl-s1"&gt;binary&lt;/span&gt;] &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;sys&lt;/span&gt;.&lt;span class="pl-c1"&gt;argv&lt;/span&gt;[&lt;span class="pl-c1"&gt;1&lt;/span&gt;:]))
    &lt;span class="pl-k"&gt;else&lt;/span&gt;:
        &lt;span class="pl-c"&gt;# On Unix, exec replaces the process&lt;/span&gt;
        &lt;span class="pl-s1"&gt;os&lt;/span&gt;.&lt;span class="pl-c1"&gt;execvp&lt;/span&gt;(&lt;span class="pl-s1"&gt;binary&lt;/span&gt;, [&lt;span class="pl-s1"&gt;binary&lt;/span&gt;] &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;sys&lt;/span&gt;.&lt;span class="pl-c1"&gt;argv&lt;/span&gt;[&lt;span class="pl-c1"&gt;1&lt;/span&gt;:])&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;main()&lt;/code&gt; method - also called from &lt;code&gt;sqlite_scanner/__main__.py&lt;/code&gt; - locates the binary and executes it when the Python package itself is executed, using the &lt;code&gt;sqlite-scanner = sqlite_scanner:main&lt;/code&gt; entry point defined in the wheel.&lt;/p&gt;
&lt;h4 id="which-means-we-can-use-it-as-a-dependency"&gt;Which means we can use it as a dependency&lt;/h4&gt;
&lt;p&gt;Using PyPI as a distribution platform for Go binaries feels a tiny bit abusive, albeit &lt;a href="https://simonwillison.net/2022/May/23/bundling-binary-tools-in-python-wheels/"&gt;there is plenty of precedent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ll justify it by pointing out that this means &lt;strong&gt;we can use Go binaries as dependencies&lt;/strong&gt; for other Python packages now.&lt;/p&gt;
&lt;p&gt;That's genuinely useful! It means that any functionality which is available in a cross-platform Go binary can now be subsumed into a Python package. Python is really good at running subprocesses so this opens up a whole world of useful tricks that we can bake into our Python tools.&lt;/p&gt;
&lt;p&gt;To demonstrate this, I built &lt;a href="https://github.com/simonw/datasette-scan"&gt;datasette-scan&lt;/a&gt; - a new Datasette plugin which depends on &lt;code&gt;sqlite-scanner&lt;/code&gt; and then uses that Go binary to scan a folder for SQLite databases and attach them to a Datasette instance.&lt;/p&gt;
&lt;p&gt;Here's how to use that (without even installing anything first, thanks &lt;code&gt;uv&lt;/code&gt;) to explore any SQLite databases in your Downloads folder:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uv run --with datasette-scan datasette scan &lt;span class="pl-k"&gt;~&lt;/span&gt;/Downloads&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you peek at the code you'll see it &lt;a href="https://github.com/simonw/datasette-scan/blob/1a2b6d1e6b04c8cd05f5676ff7daa877efd99f08/pyproject.toml#L14"&gt;depends on sqlite-scanner&lt;/a&gt; in &lt;code&gt;pyproject.toml&lt;/code&gt; and calls it using &lt;code&gt;subprocess.run()&lt;/code&gt; against &lt;code&gt;sqlite_scanner.get_binary_path()&lt;/code&gt; in its own &lt;a href="https://github.com/simonw/datasette-scan/blob/1a2b6d1e6b04c8cd05f5676ff7daa877efd99f08/datasette_scan/__init__.py#L38-L58"&gt;scan_directories() function&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've been exploring this pattern for other, non-Go binaries recently - here's &lt;a href="https://github.com/simonw/tools/blob/main/python/livestream-gif.py"&gt;a recent script&lt;/a&gt; that depends on &lt;a href="https://pypi.org/project/static-ffmpeg/"&gt;static-ffmpeg&lt;/a&gt; to ensure that &lt;code&gt;ffmpeg&lt;/code&gt; is available for the script to use.&lt;/p&gt;
&lt;h4 id="building-python-wheels-from-go-packages-with-go-to-wheel"&gt;Building Python wheels from Go packages with go-to-wheel&lt;/h4&gt;
&lt;p&gt;After trying this pattern myself a couple of times I realized it would be useful to have a tool to automate the process.&lt;/p&gt;
&lt;p&gt;I first &lt;a href="https://claude.ai/share/2d9ced56-b3e8-4651-83cc-860b9b419187"&gt;brainstormed with Claude&lt;/a&gt; to check that there was no existing tool to do this. It pointed me to &lt;a href="https://www.maturin.rs/bindings.html#bin"&gt;maturin bin&lt;/a&gt; which helps distribute Rust projects using Python wheels, and &lt;a href="https://github.com/Bing-su/pip-binary-factory"&gt;pip-binary-factory&lt;/a&gt; which bundles all sorts of other projects, but did not identify anything that addressed the exact problem I was looking to solve.&lt;/p&gt;
&lt;p&gt;So I &lt;a href="https://gisthost.github.io/?41f04e4eb823b1ceb888d9a28c2280dd/index.html"&gt;had Claude Code for web build the first version&lt;/a&gt;, then refined the code locally on my laptop with the help of more Claude Code and a little bit of OpenAI Codex too, just to mix things up.&lt;/p&gt;
&lt;p&gt;The full documentation is in the &lt;a href="https://github.com/simonw/go-to-wheel"&gt;simonw/go-to-wheel&lt;/a&gt; repository. I've published that tool to PyPI so now you can run it using:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uvx go-to-wheel --help&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;sqlite-scanner&lt;/code&gt; package you can &lt;a href="https://pypi.org/project/sqlite-scanner/"&gt;see on PyPI&lt;/a&gt; was built using &lt;code&gt;go-to-wheel&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uvx go-to-wheel &lt;span class="pl-k"&gt;~&lt;/span&gt;/dev/sqlite-scanner \
  --set-version-var main.version \
  --version 0.1.1 \
  --readme README.md \
  --author &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Simon Willison&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; \
  --url https://github.com/simonw/sqlite-scanner \
  --description &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Scan directories for SQLite databases&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This created a set of wheels in the &lt;code&gt;dist/&lt;/code&gt; folder. I tested one of them like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uv run --with dist/sqlite_scanner-0.1.1-py3-none-macosx_11_0_arm64.whl \
  sqlite-scanner --version&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When that spat out the correct version number I was confident everything had worked as planned, so I pushed the whole set of wheels to PyPI using &lt;code&gt;twine upload&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uvx twine upload dist/&lt;span class="pl-k"&gt;*&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I had to paste in a PyPI API token I had saved previously.&lt;/p&gt;
&lt;h4 id="i-expect-to-use-this-pattern-a-lot"&gt;I expect to use this pattern a lot&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;sqlite-scanner&lt;/code&gt; is very clearly meant as a proof-of-concept for this wider pattern - Python is very much capable of recursively crawling a directory structure looking for files that start with a specific byte prefix on its own!&lt;/p&gt;
&lt;p&gt;That said, I think there's a &lt;em&gt;lot&lt;/em&gt; to be said for this pattern. Go is a great complement to Python - it's fast, compiles to small self-contained binaries, has excellent concurrency support and a rich ecosystem of libraries.&lt;/p&gt;
&lt;p&gt;Go is similar to Python in that it has a strong standard library. Go is particularly good for HTTP tooling - I've built several HTTP proxies in the past using Go's excellent &lt;code&gt;net/http/httputil.ReverseProxy&lt;/code&gt; handler.&lt;/p&gt;
&lt;p&gt;I've also been experimenting with &lt;a href="https://github.com/wazero/wazero"&gt;wazero&lt;/a&gt;, Go's robust and mature zero dependency WebAssembly runtime as part of my ongoing quest for the ideal sandbox for running untrusted code. &lt;a href="https://github.com/simonw/research/tree/main/wasm-repl-cli"&gt;Here's my latest experiment&lt;/a&gt; with that library.&lt;/p&gt;
&lt;p&gt;Being able to seamlessly integrate Go binaries into Python projects without the end user having to think about Go at all - they &lt;code&gt;pip install&lt;/code&gt; and everything Just Works - feels like a valuable addition to my toolbox.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/go"&gt;go&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="go"/><category term="packaging"/><category term="projects"/><category term="pypi"/><category term="python"/><category term="sqlite"/><category term="datasette"/><category term="ai-assisted-programming"/><category term="uv"/></entry><entry><title>Poe the Poet</title><link href="https://simonwillison.net/2025/Dec/16/poe-the-poet/#atom-tag" rel="alternate"/><published>2025-12-16T22:57:02+00:00</published><updated>2025-12-16T22:57:02+00:00</updated><id>https://simonwillison.net/2025/Dec/16/poe-the-poet/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://poethepoet.natn.io/"&gt;Poe the Poet&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I was looking for a way to specify additional commands in my &lt;code&gt;pyproject.toml&lt;/code&gt; file to execute using &lt;code&gt;uv&lt;/code&gt;. There's an &lt;a href="https://github.com/astral-sh/uv/issues/5903"&gt;enormous issue thread&lt;/a&gt; on this in the &lt;code&gt;uv&lt;/code&gt; issue tracker (300+ comments dating back to August 2024) and from there I learned of several options including this one, Poe the Poet.&lt;/p&gt;
&lt;p&gt;It's neat. I added it to my &lt;a href="https://github.com/simonw/s3-credentials"&gt;s3-credentials&lt;/a&gt; project just now and the following now works for running the live preview server for the documentation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv run poe livehtml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's the snippet of TOML I added to my &lt;code&gt;pyproject.toml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;[&lt;span class="pl-en"&gt;dependency-groups&lt;/span&gt;]
&lt;span class="pl-smi"&gt;test&lt;/span&gt; = [
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;pytest&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;pytest-mock&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;cogapp&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;moto&amp;gt;=5.0.4&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
]
&lt;span class="pl-smi"&gt;docs&lt;/span&gt; = [
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;furo&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;sphinx-autobuild&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;myst-parser&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;cogapp&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
]
&lt;span class="pl-smi"&gt;dev&lt;/span&gt; = [
    {&lt;span class="pl-smi"&gt;include-group&lt;/span&gt; = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;test&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;},
    {&lt;span class="pl-smi"&gt;include-group&lt;/span&gt; = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;docs&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;},
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;poethepoet&amp;gt;=0.38.0&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
]

[&lt;span class="pl-en"&gt;tool&lt;/span&gt;.&lt;span class="pl-en"&gt;poe&lt;/span&gt;.&lt;span class="pl-en"&gt;tasks&lt;/span&gt;]
&lt;span class="pl-smi"&gt;docs&lt;/span&gt; = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;sphinx-build -M html docs docs/_build&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-smi"&gt;livehtml&lt;/span&gt; = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;sphinx-autobuild -b html docs docs/_build&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-smi"&gt;cog&lt;/span&gt; = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;cog -r docs/*.md&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Since &lt;code&gt;poethepoet&lt;/code&gt; is in the &lt;code&gt;dev=&lt;/code&gt; dependency group any time I run &lt;code&gt;uv run ...&lt;/code&gt; it will be available in the environment.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/s3-credentials"&gt;s3-credentials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="s3-credentials"/><category term="uv"/></entry><entry><title>TIL: Dependency groups and uv run</title><link href="https://simonwillison.net/2025/Dec/3/til-dependency-groups-and-uv-run/#atom-tag" rel="alternate"/><published>2025-12-03T05:55:23+00:00</published><updated>2025-12-03T05:55:23+00:00</updated><id>https://simonwillison.net/2025/Dec/3/til-dependency-groups-and-uv-run/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://til.simonwillison.net/uv/dependency-groups"&gt;TIL: Dependency groups and uv run&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I wrote up the new pattern I'm using for my various Python project repos to make them as easy to hack on with &lt;code&gt;uv&lt;/code&gt; as possible. The trick is to use a &lt;a href="https://peps.python.org/pep-0735/"&gt;PEP 735 dependency group&lt;/a&gt; called &lt;code&gt;dev&lt;/code&gt;, declared in &lt;code&gt;pyproject.toml&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[dependency-groups]
dev = ["pytest"]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in place, running &lt;code&gt;uv run pytest&lt;/code&gt; will automatically install that development dependency into a new virtual environment and use it to run your tests.&lt;/p&gt;
&lt;p&gt;This means you can get started hacking on one of my projects (here &lt;a href="https://github.com/datasette/datasette-extract"&gt;datasette-extract&lt;/a&gt;) with just these steps:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/datasette/datasette-extract
cd datasette-extract
uv run pytest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also split my &lt;a href="https://til.simonwillison.net/uv"&gt;uv TILs out&lt;/a&gt; into a separate folder. This meant I had to setup redirects for the old paths, so I had &lt;a href="https://gistpreview.github.io/?f460e64d1768b418b594614f9f57eb89"&gt;Claude Code help build me&lt;/a&gt; a new plugin called &lt;a href="https://github.com/datasette/datasette-redirects"&gt;datasette-redirects&lt;/a&gt; and then &lt;a href="https://github.com/simonw/til/commit/5191fb1f98f19e6788b8e7249da6f366e2f47343"&gt;apply it to my TIL site&lt;/a&gt;, including &lt;a href="https://gistpreview.github.io/?d78470bc652dc257b06474edf3dea61c"&gt;updating the build script&lt;/a&gt; to correctly track the creation date of files that had since been renamed.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/til"&gt;til&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="ai"/><category term="til"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="uv"/><category term="coding-agents"/><category term="claude-code"/></entry><entry><title>We should all be using dependency cooldowns</title><link href="https://simonwillison.net/2025/Nov/21/dependency-cooldowns/#atom-tag" rel="alternate"/><published>2025-11-21T17:27:33+00:00</published><updated>2025-11-21T17:27:33+00:00</updated><id>https://simonwillison.net/2025/Nov/21/dependency-cooldowns/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns"&gt;We should all be using dependency cooldowns&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
William Woodruff gives a name to a sensible strategy for managing dependencies while reducing the chances of a surprise supply chain attack: &lt;strong&gt;dependency cooldowns&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Supply chain attacks happen when an attacker compromises a widely used open source package and publishes a new version with an exploit. These are usually spotted &lt;em&gt;very&lt;/em&gt; quickly, so an attack often only has a few hours of effective window before the problem is identified and the compromised package is pulled.&lt;/p&gt;
&lt;p&gt;You are most at risk if you're automatically applying upgrades the same day they are released.&lt;/p&gt;
&lt;p&gt;William says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I &lt;strong&gt;love&lt;/strong&gt; cooldowns for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They're empirically effective, per above. They won't stop &lt;em&gt;all&lt;/em&gt; attackers, but they &lt;em&gt;do&lt;/em&gt; stymie the majority of high-visibiity, mass-impact supply chain attacks that have become more common.&lt;/li&gt;
&lt;li&gt;They're &lt;em&gt;incredibly&lt;/em&gt; easy to implement. Moreover, they're &lt;strong&gt;literally free&lt;/strong&gt; to implement in most cases: most people can use &lt;a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference#cooldown-"&gt;Dependabot's functionality&lt;/a&gt;, &lt;a href="https://docs.renovatebot.com/key-concepts/minimum-release-age/"&gt;Renovate's functionality&lt;/a&gt;, or the functionality build directly into their package manager&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The one counter-argument to this is that sometimes an upgrade fixes a security vulnerability, and in those cases every hour of delay in upgrading as an hour when an attacker could exploit the new issue against your software.&lt;/p&gt;
&lt;p&gt;I see that as an argument for carefully monitoring the release notes of your dependencies, and paying special attention to security advisories. I'm a big fan of the &lt;a href="https://github.com/advisories"&gt;GitHub Advisory Database&lt;/a&gt; for that kind of information.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=46005111"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/definitions"&gt;definitions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;&lt;/p&gt;



</summary><category term="definitions"/><category term="github"/><category term="open-source"/><category term="packaging"/><category term="supply-chain"/></entry><entry><title>pyx: a Python-native package registry, now in Beta</title><link href="https://simonwillison.net/2025/Aug/13/pyx/#atom-tag" rel="alternate"/><published>2025-08-13T18:36:51+00:00</published><updated>2025-08-13T18:36:51+00:00</updated><id>https://simonwillison.net/2025/Aug/13/pyx/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://astral.sh/blog/introducing-pyx"&gt;pyx: a Python-native package registry, now in Beta&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Since its first release, the single biggest question around the &lt;a href="https://github.com/astral-sh/uv"&gt;uv&lt;/a&gt; Python environment management tool has been around Astral's business model: Astral are a VC-backed company and at some point they need to start making real revenue.&lt;/p&gt;
&lt;p&gt;Back in September Astral founder Charlie Marsh &lt;a href="https://simonwillison.net/2024/Sep/8/uv-under-discussion-on-mastodon/"&gt;said the following&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I don't want to charge people money to use our tools, and I don't want to create an incentive structure whereby our open source offerings are competing with any commercial offerings (which is what you see with a lost of hosted-open-source-SaaS business models).&lt;/p&gt;
&lt;p&gt;What I want to do is build software that vertically integrates with our open source tools, and sell that software to companies that are already using Ruff, uv, etc. Alternatives to things that companies already pay for today.&lt;/p&gt;
&lt;p&gt;An example of what this might look like (we may not do this, but it's helpful to have a concrete example of the strategy) would be something like an enterprise-focused private package registry. [...]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It looks like those plans have become concrete now! From today's announcement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;a href="https://astral.sh/pyx"&gt;pyx&lt;/a&gt; is a Python-native package registry --- and the first piece of the Astral platform, our next-generation infrastructure for the Python ecosystem.&lt;/p&gt;
&lt;p&gt;We think of &lt;a href="https://astral.sh/pyx"&gt;pyx&lt;/a&gt; as an optimized backend for &lt;a href="https://github.com/astral-sh/uv"&gt;uv&lt;/a&gt;: it's a package registry, but it also solves problems that go beyond the scope of a traditional "package registry", making your Python experience faster, more secure, and even GPU-aware, both for private packages and public sources (like PyPI and the PyTorch index).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://astral.sh/pyx"&gt;pyx&lt;/a&gt; is live with our early partners, including &lt;a href="https://ramp.com/"&gt;Ramp&lt;/a&gt;, &lt;a href="https://www.intercom.com/"&gt;Intercom&lt;/a&gt;, and &lt;a href="https://fal.ai/"&gt;fal&lt;/a&gt; [...]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This looks like a sensible direction to me, and one that stays true to Charlie's promises to carefully design the incentive structure to avoid corrupting the core open source project that the Python community is coming to depend on.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://x.com/charliermarsh/status/1955695947716985241"&gt;@charliermarsh&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/astral"&gt;astral&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/charlie-marsh"&gt;charlie-marsh&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="packaging"/><category term="python"/><category term="uv"/><category term="astral"/><category term="charlie-marsh"/></entry><entry><title>Introducing OSS Rebuild: Open Source, Rebuilt to Last</title><link href="https://simonwillison.net/2025/Jul/23/oss-rebuild/#atom-tag" rel="alternate"/><published>2025-07-23T17:16:32+00:00</published><updated>2025-07-23T17:16:32+00:00</updated><id>https://simonwillison.net/2025/Jul/23/oss-rebuild/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://security.googleblog.com/2025/07/introducing-oss-rebuild-open-source.html"&gt;Introducing OSS Rebuild: Open Source, Rebuilt to Last&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Major news on the &lt;a href="https://reproducible-builds.org/"&gt;Reproducible Builds&lt;/a&gt; front: the Google Security team have announced &lt;a href="https://github.com/google/oss-rebuild"&gt;OSS Rebuild&lt;/a&gt;, their project to provide build attestations for open source packages released through the NPM, PyPI and Crates ecosystom (and more to come).&lt;/p&gt;
&lt;p&gt;They currently run builds against the "most popular" packages from those ecosystems:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Through automation and heuristics, we determine a prospective build definition for a target package and rebuild it. We semantically compare the result with the existing upstream artifact, normalizing each one to remove instabilities that cause bit-for-bit comparisons to fail (e.g. archive compression). Once we reproduce the package, we publish the build definition and outcome via &lt;a href="https://slsa.dev/spec/v0.1/provenance"&gt;SLSA Provenance&lt;/a&gt;. This attestation allows consumers to reliably verify a package's origin within the source history, understand and repeat its build process, and customize the build from a known-functional baseline&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The only way to interact with the Rebuild data right now is through their &lt;a href="https://github.com/google/oss-rebuild"&gt;Go CLI tool&lt;/a&gt;. I reverse-engineered it &lt;a href="https://gist.github.com/simonw/a5416718587aadfb0ce5f046b66b54fb"&gt;using Gemini 2.5 Pro&lt;/a&gt; and derived this command to get a list of all of their built packages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gsutil ls -r 'gs://google-rebuild-attestations/**'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are 9,513 total lines, &lt;a href="https://gist.github.com/simonw/9287de5900d5b76969e331d9b4ad9eba"&gt;here's a Gist&lt;/a&gt;. I &lt;a href="https://gist.github.com/simonw/7b1d0a01f74c2e8d8cedea7a9dc7f8d7"&gt;used Claude Code&lt;/a&gt; to count them across the different ecosystems (discounting duplicates for different versions of the same package):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pypi: 5,028 packages&lt;/li&gt;
&lt;li&gt;cratesio: 2,437 packages&lt;/li&gt;
&lt;li&gt;npm: 2,048 packages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I got a bit ambitious... since the files themselves are hosted in a Google Cloud Bucket, could I run my own web app somewhere on &lt;code&gt;storage.googleapis.com&lt;/code&gt; that could use &lt;code&gt;fetch()&lt;/code&gt; to retrieve that data, working around the lack of open CORS headers?&lt;/p&gt;
&lt;p&gt;I &lt;a href="https://gist.github.com/simonw/178a1cb57597a7b8aaa4910beae89cd3"&gt;got Claude Code to try that for me&lt;/a&gt; (I didn't want to have to figure out how to create a bucket and configure it for web access just for this one experiment) and it built and then deployed &lt;a href="https://storage.googleapis.com/rebuild-ui/index.html"&gt;https://storage.googleapis.com/rebuild-ui/index.html&lt;/a&gt;, which did indeed work!&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Google Rebuild Explorer interface showing a search box with placeholder text &amp;quot;Type to search packages (e.g., 'adler', 'python-slugify')...&amp;quot; under &amp;quot;Search rebuild attestations:&amp;quot;, a loading file path &amp;quot;pypi/accelerate/0.21.0/accelerate-0.21.0-py3-none-any.whl/rebuild.intoto.jsonl&amp;quot;, and Object 1 containing JSON with &amp;quot;payloadType&amp;quot;: &amp;quot;in-toto.io Statement v1 URL&amp;quot;, &amp;quot;payload&amp;quot;: &amp;quot;...&amp;quot;, &amp;quot;signatures&amp;quot;: [{&amp;quot;keyid&amp;quot;: &amp;quot;Google Cloud KMS signing key URL&amp;quot;, &amp;quot;sig&amp;quot;: &amp;quot;...&amp;quot;}]" src="https://static.simonwillison.net/static/2025/rebuild-ui.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;It lets you search against that list of packages from the Gist and then select one to view the pretty-printed newline-delimited JSON that was stored for that package.&lt;/p&gt;
&lt;p&gt;The output isn't as interesting as I was expecting, but it was fun demonstrating that it's possible to build and deploy web apps to Google Cloud that can then make &lt;code&gt;fetch()&lt;/code&gt; requests to other public buckets.&lt;/p&gt;
&lt;p&gt;Hopefully the OSS Rebuild team will &lt;a href="https://news.ycombinator.com/item?id=44646925#44652098"&gt;add a web UI&lt;/a&gt; to their project at some point in the future.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=44646925"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/npm"&gt;npm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;&lt;/p&gt;



</summary><category term="google"/><category term="packaging"/><category term="pypi"/><category term="security"/><category term="npm"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="supply-chain"/><category term="vibe-coding"/><category term="claude-code"/></entry><entry><title>crates.io: Trusted Publishing</title><link href="https://simonwillison.net/2025/Jul/12/cratesio-trusted-publishing/#atom-tag" rel="alternate"/><published>2025-07-12T16:12:18+00:00</published><updated>2025-07-12T16:12:18+00:00</updated><id>https://simonwillison.net/2025/Jul/12/cratesio-trusted-publishing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.rust-lang.org/2025/07/11/crates-io-development-update-2025-07/"&gt;crates.io: Trusted Publishing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
crates.io is the Rust ecosystem's equivalent of PyPI. Inspired by PyPI's GitHub integration (see &lt;a href="https://til.simonwillison.net/pypi/pypi-releases-from-github"&gt;my TIL&lt;/a&gt;, I use this for dozens of my packages now) they've added a similar feature:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Trusted Publishing eliminates the need for GitHub Actions secrets when publishing crates from your CI/CD pipeline. Instead of managing API tokens, you can now configure which GitHub repository you trust directly on crates.io.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They're missing one feature that PyPI has: on PyPI you can create a "pending publisher" for your first release. crates.io currently requires the first release to be manual:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To get started with Trusted Publishing, you'll need to publish your first release manually. After that, you can set up trusted publishing for future releases.&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://x.com/charliermarsh/status/1943853345967423506"&gt;@charliermarsh&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rust"&gt;rust&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="packaging"/><category term="pypi"/><category term="rust"/></entry><entry><title>2025 Python Packaging Ecosystem Survey</title><link href="https://simonwillison.net/2025/May/18/2025-python-packaging-ecosystem-survey/#atom-tag" rel="alternate"/><published>2025-05-18T11:50:06+00:00</published><updated>2025-05-18T11:50:06+00:00</updated><id>https://simonwillison.net/2025/May/18/2025-python-packaging-ecosystem-survey/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://anaconda.surveymonkey.com/r/py-package-2025"&gt;2025 Python Packaging Ecosystem Survey&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
If you make use of Python packaging tools (pip, Anaconda, uv, dozens of others) and have opinions please spend a few minutes with this year's packaging survey. This one was "Co-authored by 30+ of your favorite Python Ecosystem projects, organizations and companies."


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pip"&gt;pip&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/surveys"&gt;surveys&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="pip"/><category term="python"/><category term="surveys"/><category term="psf"/></entry><entry><title>Quoting Andrew Nesbitt</title><link href="https://simonwillison.net/2025/Apr/12/andrew-nesbitt/#atom-tag" rel="alternate"/><published>2025-04-12T16:30:07+00:00</published><updated>2025-04-12T16:30:07+00:00</updated><id>https://simonwillison.net/2025/Apr/12/andrew-nesbitt/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://mastodon.social/@andrewnez/114302875075999244"&gt;&lt;p&gt;&lt;strong&gt;Slopsquatting&lt;/strong&gt; -- when an LLM hallucinates a non-existent package name, and a bad actor registers it maliciously. The AI brother of typosquatting.&lt;/p&gt;
&lt;p&gt;Credit to &lt;a href="https://fosstodon.org/@sethmlarson" title="@sethmlarson"&gt;@sethmlarson&lt;/a&gt; for the name&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://mastodon.social/@andrewnez/114302875075999244"&gt;Andrew Nesbitt&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/definitions"&gt;definitions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/slop"&gt;slop&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-ethics"&gt;ai-ethics&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/seth-michael-larson"&gt;seth-michael-larson&lt;/a&gt;&lt;/p&gt;



</summary><category term="definitions"/><category term="packaging"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="supply-chain"/><category term="slop"/><category term="ai-ethics"/><category term="seth-michael-larson"/></entry><entry><title>Quoting Brett Cannon</title><link href="https://simonwillison.net/2024/Nov/22/brett-cannon/#atom-tag" rel="alternate"/><published>2024-11-22T08:02:53+00:00</published><updated>2024-11-22T08:02:53+00:00</updated><id>https://simonwillison.net/2024/Nov/22/brett-cannon/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://bsky.app/profile/snarky.ca/post/3lbid7l27722p"&gt;&lt;p&gt;It's okay to complain and vent, I just ask you be able to back it up. Saying, "Python packaging sucks", but then admit you actually haven't used it in so long you don't remember why it sucked isn't fair. Things do improve, so it's better to say "it did suck" and acknowledge you might be out-of-date.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://bsky.app/profile/snarky.ca/post/3lbid7l27722p"&gt;Brett Cannon&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/brett-cannon"&gt;brett-cannon&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="brett-cannon"/></entry><entry><title>Using uv with PyTorch</title><link href="https://simonwillison.net/2024/Nov/19/using-uv-with-pytorch/#atom-tag" rel="alternate"/><published>2024-11-19T23:20:18+00:00</published><updated>2024-11-19T23:20:18+00:00</updated><id>https://simonwillison.net/2024/Nov/19/using-uv-with-pytorch/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.astral.sh/uv/guides/integration/pytorch/"&gt;Using uv with PyTorch&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
PyTorch is a notoriously tricky piece of Python software to install, due to the need to provide separate wheels for different combinations of Python version and GPU accelerator (e.g. different CUDA versions).&lt;/p&gt;
&lt;p&gt;uv now has dedicated documentation for PyTorch which I'm finding really useful - it clearly explains the challenge and then shows exactly how to configure a &lt;code&gt;pyproject.toml&lt;/code&gt; such that &lt;code&gt;uv&lt;/code&gt; knows which version of each package it should install from where.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/charliermarsh/status/1858966355518878163"&gt;@charliermarsh&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pip"&gt;pip&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pytorch"&gt;pytorch&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="pip"/><category term="python"/><category term="pytorch"/><category term="uv"/></entry><entry><title>PyPI now supports digital attestations</title><link href="https://simonwillison.net/2024/Nov/14/pypi-digital-attestations/#atom-tag" rel="alternate"/><published>2024-11-14T19:56:49+00:00</published><updated>2024-11-14T19:56:49+00:00</updated><id>https://simonwillison.net/2024/Nov/14/pypi-digital-attestations/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.pypi.org/posts/2024-11-14-pypi-now-supports-digital-attestations/"&gt;PyPI now supports digital attestations&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Dustin Ingram:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PyPI package maintainers can now publish signed digital attestations when publishing, in order to further increase trust in the supply-chain security of their projects. Additionally, a new API is available for consumers and installers to verify published attestations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This has been in the works for a while, and is another component of PyPI's approach to supply chain security for Python packaging - see &lt;a href="https://peps.python.org/pep-0740/"&gt;PEP 740 – Index support for digital attestations&lt;/a&gt; for all of the underlying details.&lt;/p&gt;
&lt;p&gt;A key problem this solves is cryptographically linking packages published on PyPI to the exact source code that was used to build those packages. In the absence of this feature there are no guarantees that the &lt;code&gt;.tar.gz&lt;/code&gt; or &lt;code&gt;.whl&lt;/code&gt; file you download from PyPI hasn't been tampered with (to add malware, for example) in a way that's not visible in the published source code.&lt;/p&gt;
&lt;p&gt;These new attestations provide a mechanism for proving that a known, trustworthy build system was used to generate and publish the package, starting with its source code on GitHub.&lt;/p&gt;
&lt;p&gt;The good news is that if you're using the PyPI Trusted Publishers mechanism in GitHub Actions to publish packages, you're already using this new system. I wrote about that system in January: &lt;a href="https://simonwillison.net/2024/Jan/16/python-lib-pypi/"&gt;Publish Python packages to PyPI with a python-lib cookiecutter template and GitHub Actions&lt;/a&gt; - and hundreds of my own PyPI packages are already using that system, thanks to my various cookiecutter templates.&lt;/p&gt;
&lt;p&gt;Trail of Bits helped build this feature, and provide extra background about it on their own blog in &lt;a href="https://blog.trailofbits.com/2024/11/14/attestations-a-new-generation-of-signatures-on-pypi/"&gt;Attestations: A new generation of signatures on PyPI&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/pypa/gh-action-pypi-publish/releases/tag/v1.11.0"&gt;As of October 29&lt;/a&gt;, attestations are the default for anyone using Trusted Publishing via the &lt;a href="https://github.com/marketplace/actions/pypi-publish"&gt;PyPA publishing action for GitHub&lt;/a&gt;. That means roughly 20,000 packages can now attest to their provenance &lt;em&gt;by default&lt;/em&gt;, with no changes needed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They also built &lt;a href="https://trailofbits.github.io/are-we-pep740-yet/"&gt;Are we PEP 740 yet?&lt;/a&gt; (&lt;a href="https://github.com/trailofbits/are-we-pep740-yet/blob/a87a8895dd238d14af50aaa2675c81060aa52846/utils.py#L31-L72"&gt;key implementation here&lt;/a&gt;) to track the rollout of attestations across the 360 most downloaded packages from PyPI. It works by hitting URLs such as &lt;a href="https://pypi.org/simple/pydantic/"&gt;https://pypi.org/simple/pydantic/&lt;/a&gt; with a &lt;code&gt;Accept: application/vnd.pypi.simple.v1+json&lt;/code&gt; header - &lt;a href="https://gist.github.com/simonw/8cf8a850739e2865cf3b9a74e6461b28"&gt;here's the JSON that returns&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I published an alpha package using Trusted Publishers last night and the &lt;a href="https://pypi.org/project/llm/0.18a0/#llm-0.18a0-py3-none-any.whl"&gt;files for that release&lt;/a&gt; are showing the new provenance information already:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Provenance. The following attestation bundles were made for llm-0.18a0-py3-none-any.whl: Publisher: publish.yml on simonw/llm Attestations: Statement type: https://in-toto.io/Statement/v1 Predicate type: https://docs.pypi.org/attestations/publish/v1 Subject name: llm-0.18a0-py3-none-any.whl Subject digest: dde9899583172e6434971d8cddeb106bb535ae4ee3589cb4e2d525a4526976da Sigstore transparency entry: 148798240 Sigstore integration time: about 18 hours ago" src="https://static.simonwillison.net/static/2024/provenance.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Which links to &lt;a href="https://search.sigstore.dev/?logIndex=148798240"&gt;this Sigstore log entry&lt;/a&gt; with more details, including &lt;a href="https://github.com/simonw/llm/tree/041730d8b2bc12f62cfe41c44b62a03ef4790117"&gt;the Git hash&lt;/a&gt; that was used to build the package:&lt;/p&gt;
&lt;p&gt;&lt;img alt="X509v3 extensions:   Key Usage (critical):   - Digital Signature   Extended Key Usage:   - Code Signing   Subject Key Identifier:   - 4E:D8:B4:DB:C1:28:D5:20:1A:A0:14:41:2F:21:07:B4:4E:EF:0B:F1   Authority Key Identifier:     keyid: DF:D3:E9:CF:56:24:11:96:F9:A8:D8:E9:28:55:A2:C6:2E:18:64:3F   Subject Alternative Name (critical):     url:     - https://github.com/simonw/llm/.github/workflows/publish.yml@refs/tags/0.18a0   OIDC Issuer: https://token.actions.githubusercontent.com   GitHub Workflow Trigger: release   GitHub Workflow SHA: 041730d8b2bc12f62cfe41c44b62a03ef4790117   GitHub Workflow Name: Publish Python Package   GitHub Workflow Repository: simonw/llm   GitHub Workflow Ref: refs/tags/0.18a0   OIDC Issuer (v2): https://token.actions.githubusercontent.com   Build Signer URI: https://github.com/simonw/llm/.github/workflows/publish.yml@refs/tags/0.18a0   Build Signer Digest: 041730d8b2bc12f62cfe41c44b62a03ef4790117" src="https://static.simonwillison.net/static/2024/sigstore.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.sigstore.dev/"&gt;Sigstore&lt;/a&gt; is a transparency log maintained by &lt;a href="https://en.wikipedia.org/wiki/Open_Source_Security_Foundation"&gt;Open Source Security Foundation (OpenSSF)&lt;/a&gt;, a sub-project of the Linux Foundation.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=42136375"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github-actions"&gt;github-actions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dustin-ingram"&gt;dustin-ingram&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="packaging"/><category term="pypi"/><category term="python"/><category term="github-actions"/><category term="psf"/><category term="supply-chain"/><category term="dustin-ingram"/></entry><entry><title>uv 0.5.0</title><link href="https://simonwillison.net/2024/Nov/8/uv/#atom-tag" rel="alternate"/><published>2024-11-08T23:54:42+00:00</published><updated>2024-11-08T23:54:42+00:00</updated><id>https://simonwillison.net/2024/Nov/8/uv/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/astral-sh/uv/releases/tag/0.5.0"&gt;uv 0.5.0&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The first backwards-incompatible (in minor ways) release after 30 releases &lt;a href="https://twitter.com/charliermarsh/status/1855015218071355663"&gt;without a breaking change&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I found out about this release this morning when I &lt;a href="https://github.com/astral-sh/uv/issues/8940"&gt;filed an issue&lt;/a&gt; about a fiddly usability problem I had encountered with the combo of &lt;code&gt;uv&lt;/code&gt; and &lt;code&gt;conda&lt;/code&gt;... and learned that the &lt;em&gt;exact&lt;/em&gt; problem had already been fixed in the brand new version!


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="uv"/></entry><entry><title>TIL: Using uv to develop Python command-line applications</title><link href="https://simonwillison.net/2024/Oct/24/uv-cli/#atom-tag" rel="alternate"/><published>2024-10-24T05:56:21+00:00</published><updated>2024-10-24T05:56:21+00:00</updated><id>https://simonwillison.net/2024/Oct/24/uv-cli/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://til.simonwillison.net/python/uv-cli-apps"&gt;TIL: Using uv to develop Python command-line applications&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I've been increasingly using &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt; to try out new software (via &lt;code&gt;uvx&lt;/code&gt;) and experiment with new ideas, but I hadn't quite figured out the right way to use it for developing my own projects.&lt;/p&gt;
&lt;p&gt;It turns out I was missing a few things - in particular the fact that there's no need to use &lt;code&gt;uv pip&lt;/code&gt; at all when working with a local development environment, you can get by entirely on &lt;code&gt;uv run&lt;/code&gt; (and maybe &lt;code&gt;uv sync --extra test&lt;/code&gt; to install test dependencies) with no direct invocations of &lt;code&gt;uv pip&lt;/code&gt; at all.&lt;/p&gt;
&lt;p&gt;I bounced &lt;a href="https://gist.github.com/simonw/975dfa41e9b03bca2513a986d9aa3dcf"&gt;a few questions&lt;/a&gt; off Charlie Marsh and filled in the missing gaps - this TIL shows my new uv-powered process for hacking on Python CLI apps built using Click and my &lt;a href="https://github.com/simonw/click-app"&gt;simonw/click-app&lt;/a&gt; cookecutter template.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cli"&gt;cli&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pip"&gt;pip&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/til"&gt;til&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookiecutter"&gt;cookiecutter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/astral"&gt;astral&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/charlie-marsh"&gt;charlie-marsh&lt;/a&gt;&lt;/p&gt;



</summary><category term="cli"/><category term="packaging"/><category term="pip"/><category term="python"/><category term="til"/><category term="cookiecutter"/><category term="uv"/><category term="astral"/><category term="charlie-marsh"/></entry><entry><title>sudoku-in-python-packaging</title><link href="https://simonwillison.net/2024/Oct/21/sudoku-in-python-packaging/#atom-tag" rel="alternate"/><published>2024-10-21T18:59:57+00:00</published><updated>2024-10-21T18:59:57+00:00</updated><id>https://simonwillison.net/2024/Oct/21/sudoku-in-python-packaging/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/konstin/sudoku-in-python-packaging"&gt;sudoku-in-python-packaging&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Absurdly clever hack by &lt;a href="https://github.com/konstin"&gt;konsti&lt;/a&gt;: solve a Sudoku puzzle entirely using the Python package resolver!&lt;/p&gt;
&lt;p&gt;First convert the puzzle into a &lt;code&gt;requirements.in&lt;/code&gt; file representing the current state of the board:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/konstin/sudoku-in-python-packaging
cd sudoku-in-python-packaging
echo '5,3,_,_,7,_,_,_,_                                        
6,_,_,1,9,5,_,_,_
_,9,8,_,_,_,_,6,_
8,_,_,_,6,_,_,_,3
4,_,_,8,_,3,_,_,1
7,_,_,_,2,_,_,_,6
_,6,_,_,_,_,2,8,_
_,_,_,4,1,9,_,_,5
_,_,_,_,8,_,_,7,9' &amp;gt; sudoku.csv
python csv_to_requirements.py sudoku.csv requirements.in
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;requirements.in&lt;/code&gt; file now contains lines like this for each of the filled-in cells:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudoku_0_0 == 5
sudoku_1_0 == 3
sudoku_4_0 == 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run &lt;code&gt;uv pip compile&lt;/code&gt; to convert that into a fully fleshed out &lt;code&gt;requirements.txt&lt;/code&gt; file that includes all of the resolved dependencies, based on the wheel files in the &lt;a href="https://github.com/konstin/sudoku-in-python-packaging/tree/main/packages"&gt;packages/&lt;/a&gt; folder:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv pip compile \
  --find-links packages/ \
  --no-annotate \
  --no-header \
  requirements.in &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The contents of &lt;code&gt;requirements.txt&lt;/code&gt; is now the fully solved board:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudoku-0-0==5
sudoku-0-1==6
sudoku-0-2==1
sudoku-0-3==8
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The trick is the 729 wheel files in &lt;code&gt;packages/&lt;/code&gt; - each with a name like &lt;code&gt;sudoku_3_4-8-py3-none-any.whl&lt;/code&gt;. I decompressed that wheel and it included a &lt;code&gt;sudoku_3_4-8.dist-info/METADATA&lt;/code&gt; file which started like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Name: sudoku_3_4
Version: 8
Metadata-Version: 2.2
Requires-Dist: sudoku_3_0 != 8
Requires-Dist: sudoku_3_1 != 8
Requires-Dist: sudoku_3_2 != 8
Requires-Dist: sudoku_3_3 != 8
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a &lt;code&gt;!=8&lt;/code&gt; line for every other cell on the board that cannot contain the number 8 due to the rules of Sudoku (if 8 is in the 3, 4 spot). Visualized:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Sudoku grid partially filled. Number 8 in center. X's fill entire row and column containing 8, as well as the 3x3 box containing 8. Additional X's in center column above and below 8's box." src="https://static.simonwillison.net/static/2024/coords.jpg" style="width: 300px; display: block; margin: 0 auto"&gt;&lt;/p&gt;
&lt;p&gt;So the trick here is that the Python dependency resolver (now lightning fast thanks to &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt;) reads those dependencies and rules out every package version that represents a number in an invalid position. The resulting version numbers represent the cell numbers for the solution.&lt;/p&gt;
&lt;p&gt;How much faster? I tried the same thing with the &lt;a href="https://github.com/jazzband/pip-tools"&gt;pip-tools&lt;/a&gt; &lt;code&gt;pip-compile&lt;/code&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;time pip-compile \   
  --find-links packages/ \
  --no-annotate \
  --no-header \
  requirements.in &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That took 17.72s. On the same machine the &lt;code&gt;time pip uv compile...&lt;/code&gt; command took 0.24s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Here's &lt;a href="https://www.splitgraph.com/blog/poetry-dependency-resolver-sudoku"&gt;an earlier implementation&lt;/a&gt; of the same idea by Artjoms Iškovs in 2022.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://mastodon.social/@konstin/113341705101217633"&gt;@konstin&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="uv"/></entry><entry><title>UV — I am (somewhat) sold</title><link href="https://simonwillison.net/2024/Sep/15/uv-i-am-somewhat-sold/#atom-tag" rel="alternate"/><published>2024-09-15T14:54:04+00:00</published><updated>2024-09-15T14:54:04+00:00</updated><id>https://simonwillison.net/2024/Sep/15/uv-i-am-somewhat-sold/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://andrich.me/2024/09/uv-i-am-somewhat-sold/"&gt;UV — I am (somewhat) sold&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Oliver Andrich's detailed notes on adopting &lt;code&gt;uv&lt;/code&gt;. Oliver has some pretty specific requirements:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I need to have various Python versions installed locally to test my work and my personal projects. Ranging from Python 3.8 to 3.13. [...] I also require decent dependency management in my projects that goes beyond manually editing a &lt;code&gt;pyproject.toml&lt;/code&gt; file. Likewise, I am way too accustomed to &lt;code&gt;poetry add ...&lt;/code&gt;. And I run a number of Python-based tools --- &lt;a href="https://pypi.org/project/djhtml/"&gt;djhtml&lt;/a&gt;, &lt;a href="https://pypi.org/project/poetry/"&gt;poetry&lt;/a&gt;, &lt;a href="https://pypi.org/project/ipython/"&gt;ipython&lt;/a&gt;, &lt;a href="https://pypi.org/project/llm/"&gt;llm&lt;/a&gt;, &lt;a href="https://pypi.org/project/mkdocs/"&gt;mkdocs&lt;/a&gt;, &lt;a href="https://pypi.org/project/pre-commit/"&gt;pre-commit&lt;/a&gt;, &lt;a href="https://pypi.org/project/tox/"&gt;tox&lt;/a&gt;, ...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He's braver than I am!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I started by removing all Python installations, pyenv, pipx and Homebrew from my machine. Rendering me unable to do my work. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here's a neat trick: first install a specific Python version with &lt;code&gt;uv&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv python install 3.11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create an alias to run it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alias python3.11 'uv run --python=3.11 python3'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And install standalone tools with optional extra dependencies like this (a replacement for &lt;code&gt;pipx&lt;/code&gt; and &lt;code&gt;pipx inject&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv tool install --python=3.12 --with mkdocs-material mkdocs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oliver also links to Anže Pečar's handy guide on using &lt;a href="https://blog.pecar.me/uv-with-django"&gt;UV with Django&lt;/a&gt;.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://mastodon.social/@webology/113142102296895914"&gt;Jeff Triplett&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/astral"&gt;astral&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="packaging"/><category term="python"/><category term="uv"/><category term="astral"/></entry><entry><title>uv under discussion on Mastodon</title><link href="https://simonwillison.net/2024/Sep/8/uv-under-discussion-on-mastodon/#atom-tag" rel="alternate"/><published>2024-09-08T16:23:31+00:00</published><updated>2024-09-08T16:23:31+00:00</updated><id>https://simonwillison.net/2024/Sep/8/uv-under-discussion-on-mastodon/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://social.jacobian.org/@jacob/113091418140504394"&gt;uv under discussion on Mastodon&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Jacob Kaplan-Moss kicked off this fascinating conversation about &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt; on Mastodon recently. It's worth reading the whole thing, which includes input from a whole range of influential Python community members such as Jeff Triplett, Glyph Lefkowitz, Russell Keith-Magee, Seth Michael Larson, Hynek Schlawack, James Bennett and others. (Mastodon is a pretty great place for keeping up with the Python community these days.)&lt;/p&gt;
&lt;p&gt;The key theme of the conversation is that, while &lt;code&gt;uv&lt;/code&gt; represents a huge set of potential improvements to the Python ecosystem, it comes with additional risks due its attachment to a VC-backed company - and its reliance on Rust rather than Python.&lt;/p&gt;
&lt;p&gt;Here are a few comments that stood out to me.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cloudisland.nz/@freakboy3742/113093889194737339"&gt;Russell&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As enthusiastic as I am about the direction uv is going, I &lt;em&gt;haven't&lt;/em&gt; adopted them anywhere - because I want very much to understand Astral’s intended business model before I hook my wagon to their tools. It's definitely not clear to me how they're going to stay liquid once the VC money runs out. They could get me onboard in a hot second if they published a "This is what we're planning to charge for" blog post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://mastodon.social/@hynek/113094437303343866"&gt;Hynek&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As much as I hate VC, [...] FOSS projects flame out all the time too. If Frost loses interest, there’s no PDM anymore. Same for Ofek and Hatch(ling).&lt;/p&gt;
&lt;p&gt;I fully expect Astral to flame out and us having to fork/take over—it’s the circle of FOSS. To me uv looks like a genius sting to trick VCs into paying to fix packaging. We’ll be better off either way.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://mastodon.social/@glyph/113094489295782200"&gt;Glyph&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Even in the best case, Rust is more expensive and difficult to maintain, not to mention "non-native" to the average customer here. [...] And the difficulty with VC money here is that it can burn out &lt;em&gt;all&lt;/em&gt; the other projects in the ecosystem simultaneously, creating a risk of monoculture, where previously, I think we can say that "monoculture" was the &lt;em&gt;least&lt;/em&gt; of Python's packaging concerns.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://mastodon.social/@hynek/113094547139925962"&gt;Hynek on Rust&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I don’t think y’all quite grok what uv makes so special due to your seniority. The speed is really cool, but the reason Rust is elemental is that it’s one compiled blob that can be used to bootstrap and maintain a Python development. A blob that will never break because someone upgraded Homebrew, ran pip install or any other creative way people found to fuck up their installations. Python has shown to be a terrible tech to maintain Python.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://social.coop/@chrisjrn/113094511860843571"&gt;Christopher Neugebauer&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just dropping in here to say that corporate capture of the Python ecosystem is the #1 keeps-me-up-at-night subject in my community work, so I watch Astral with interest, even if I'm not yet too worried.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I'm reminded of &lt;a href="https://lucumr.pocoo.org/2024/8/21/harvest-season/"&gt;this note from Armin Ronacher&lt;/a&gt;, who created Rye and later donated it to uv maintainers Astral:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;However having seen the code and what uv is doing, even in the worst possible future this is a very forkable and maintainable thing. I believe that even in case Astral shuts down or were to do something incredibly dodgy licensing wise, the community would be better off than before uv existed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I'm currently inclined to agree with Armin and Hynek: while the risk of corporate capture for a crucial aspect of the Python packaging and onboarding ecosystem is a legitimate concern, the amount of progress that has been made here in a relatively short time combined with the open license and quality of the underlying code keeps me optimistic that &lt;code&gt;uv&lt;/code&gt; will be a net positive for Python overall.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: &lt;code&gt;uv&lt;/code&gt; creator Charlie Marsh &lt;a href="https://hachyderm.io/@charliermarsh/113103564055291456"&gt;joined the conversation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I don't want to charge people money to use our tools, and I don't want to create an incentive structure whereby our open source offerings are competing with any commercial offerings (which is what you see with a lost of hosted-open-source-SaaS business models).&lt;/p&gt;
&lt;p&gt;What I want to do is build software that vertically integrates with our open source tools, and sell that software to companies that are already using Ruff, uv, etc. Alternatives to things that companies already pay for today.&lt;/p&gt;
&lt;p&gt;An example of what this might look like (we may not do this, but it's helpful to have a concrete example of the strategy) would be something like an enterprise-focused private package registry. A lot of big companies use uv. We spend time talking to them. They all spend money on private package registries, and have issues with them. We could build a private registry that integrates well with uv, and sell it to those companies. [...]&lt;/p&gt;
&lt;p&gt;But the core of what I want to do is this: build great tools, hopefully people like them, hopefully they grow, hopefully companies adopt them; then sell software to those companies that represents the natural next thing they need when building with Python. Hopefully we can build something better than the alternatives by playing well with our OSS, and hopefully we are the natural choice if they're already using our OSS.&lt;/p&gt;
&lt;/blockquote&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/armin-ronacher"&gt;armin-ronacher&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jacob-kaplan-moss"&gt;jacob-kaplan-moss&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/russell-keith-magee"&gt;russell-keith-magee&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rust"&gt;rust&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/hynek-schlawack"&gt;hynek-schlawack&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mastodon"&gt;mastodon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/glyph"&gt;glyph&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/astral"&gt;astral&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/charlie-marsh"&gt;charlie-marsh&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/seth-michael-larson"&gt;seth-michael-larson&lt;/a&gt;&lt;/p&gt;



</summary><category term="armin-ronacher"/><category term="jacob-kaplan-moss"/><category term="open-source"/><category term="packaging"/><category term="python"/><category term="russell-keith-magee"/><category term="rust"/><category term="hynek-schlawack"/><category term="mastodon"/><category term="glyph"/><category term="uv"/><category term="astral"/><category term="charlie-marsh"/><category term="seth-michael-larson"/></entry><entry><title>Why I Still Use Python Virtual Environments in Docker</title><link href="https://simonwillison.net/2024/Sep/2/why-i-still-use-python-virtual-environments-in-docker/#atom-tag" rel="alternate"/><published>2024-09-02T23:57:55+00:00</published><updated>2024-09-02T23:57:55+00:00</updated><id>https://simonwillison.net/2024/Sep/2/why-i-still-use-python-virtual-environments-in-docker/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://hynek.me/articles/docker-virtualenv/"&gt;Why I Still Use Python Virtual Environments in Docker&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Hynek Schlawack argues for using virtual environments even when running Python applications in a Docker container. This argument was most convincing to me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I'm responsible for dozens of services, so I appreciate the &lt;em&gt;consistency&lt;/em&gt; of knowing that everything I'm deploying is in &lt;code&gt;/app&lt;/code&gt;, and if it's a Python application, I know it's a virtual environment, and if I run &lt;code&gt;/app/bin/python&lt;/code&gt;, I get the virtual environment's Python with my application ready to be imported and run.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s good to use the same tools and primitives in development and in production.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also worth a look: Hynek's guide to &lt;a href="https://hynek.me/articles/docker-uv/"&gt;Production-ready Docker Containers with uv&lt;/a&gt;, an actively maintained guide that aims to reflect ongoing changes made to &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt; itself.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://mastodon.social/@hynek/113067230489781151"&gt;@hynek&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/virtualenv"&gt;virtualenv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/docker"&gt;docker&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/hynek-schlawack"&gt;hynek-schlawack&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="virtualenv"/><category term="docker"/><category term="hynek-schlawack"/><category term="uv"/></entry><entry><title>light-the-torch</title><link href="https://simonwillison.net/2024/Aug/22/light-the-torch/#atom-tag" rel="alternate"/><published>2024-08-22T04:11:32+00:00</published><updated>2024-08-22T04:11:32+00:00</updated><id>https://simonwillison.net/2024/Aug/22/light-the-torch/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://pypi.org/project/light-the-torch/"&gt;light-the-torch&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;light-the-torch&lt;/code&gt; is a small utility that wraps &lt;code&gt;pip&lt;/code&gt; to ease the installation process for PyTorch distributions like &lt;code&gt;torch&lt;/code&gt;, &lt;code&gt;torchvision&lt;/code&gt;, &lt;code&gt;torchaudio&lt;/code&gt;, and so on as well as third-party packages that depend on them. It auto-detects compatible CUDA versions from the local setup and installs the correct PyTorch binaries without user interference.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use it like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;pip install light-the-torch
ltt install torch&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It works by wrapping and &lt;a href="https://github.com/pmeier/light-the-torch/blob/main/light_the_torch/_patch.py"&gt;patching pip&lt;/a&gt;.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/thezachmueller/status/1826384400684384476"&gt;@ thezachmueller&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pip"&gt;pip&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pytorch"&gt;pytorch&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="pip"/><category term="python"/><category term="pytorch"/></entry><entry><title>#!/usr/bin/env -S uv run</title><link href="https://simonwillison.net/2024/Aug/21/usrbinenv-uv-run/#atom-tag" rel="alternate"/><published>2024-08-21T01:29:54+00:00</published><updated>2024-08-21T01:29:54+00:00</updated><id>https://simonwillison.net/2024/Aug/21/usrbinenv-uv-run/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/alsuren/sixdofone/blob/43a73c4b9d60904fceb4ed0418178ca0bd1a663d/app.py"&gt;#!/usr/bin/env -S uv run&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is a really neat pattern. Start your Python script like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env -S uv run
# /// script
# requires-python = "&amp;gt;=3.12"
# dependencies = [
#     "flask==3.*",
# ]
# ///
import flask
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now if you &lt;code&gt;chmod 755&lt;/code&gt; it you can run it on &lt;em&gt;any machine&lt;/em&gt; with the &lt;code&gt;uv&lt;/code&gt; binary installed like this: &lt;code&gt;./app.py&lt;/code&gt; - and it will automatically create its own isolated environment and run itself with the correct installed dependencies and even the correctly installed Python version.&lt;/p&gt;
&lt;p&gt;All of that from putting &lt;code&gt;uv run&lt;/code&gt; in the shebang line!&lt;/p&gt;
&lt;p&gt;Code from &lt;a href="https://github.com/alsuren/sixdofone/pull/8"&gt;this PR&lt;/a&gt; by David Laban.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/charliermarsh/status/1826008669131067757"&gt;@charliermarsh&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="uv"/></entry><entry><title>uv: Unified Python packaging</title><link href="https://simonwillison.net/2024/Aug/20/uv-unified-python-packaging/#atom-tag" rel="alternate"/><published>2024-08-20T22:45:16+00:00</published><updated>2024-08-20T22:45:16+00:00</updated><id>https://simonwillison.net/2024/Aug/20/uv-unified-python-packaging/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://astral.sh/blog/uv-unified-python-packaging"&gt;uv: Unified Python packaging&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Huge new release from the Astral team today. &lt;a href="https://github.com/astral-sh/uv/releases/tag/0.3.0"&gt;uv 0.3.0&lt;/a&gt; adds a bewildering array of new features, as part of their attempt to build "Cargo, for Python".&lt;/p&gt;
&lt;p&gt;It's going to take a while to fully absorb all of this. Some of the key new features are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;uv tool run cowsay&lt;/code&gt;, aliased to &lt;code&gt;uvx cowsay&lt;/code&gt; - a &lt;a href="https://github.com/pypa/pipx"&gt;pipx&lt;/a&gt; alternative that runs a tool in its own dedicated virtual environment (tucked away in &lt;code&gt;~/Library/Caches/uv&lt;/code&gt;), installing it if it's not present. It has a neat &lt;code&gt;--with&lt;/code&gt; option for installing extras - I tried that just now with &lt;code&gt;uvx --with datasette-cluster-map datasette&lt;/code&gt; and it ran Datasette with the &lt;code&gt;datasette-cluster-map&lt;/code&gt; plugin installed.&lt;/li&gt;
&lt;li&gt;Project management, as an alternative to tools like &lt;a href="https://python-poetry.org/"&gt;Poetry&lt;/a&gt; and &lt;a href="https://pdm-project.org/en/latest/"&gt;PDM&lt;/a&gt;. &lt;code&gt;uv init&lt;/code&gt; creates a &lt;code&gt;pyproject.toml&lt;/code&gt; file in the current directory, &lt;code&gt;uv add sqlite-utils&lt;/code&gt; then creates and activates a &lt;code&gt;.venv&lt;/code&gt; virtual environment, adds the package to that &lt;code&gt;pyproject.toml&lt;/code&gt; and adds all of its dependencies to a new &lt;code&gt;uv.lock&lt;/code&gt; file (&lt;a href="https://gist.github.com/simonw/e309647b7d5380c7c7e5864d567f697b"&gt;like this one&lt;/a&gt;). That &lt;code&gt;uv.lock&lt;/code&gt; is described as &lt;a href="https://docs.astral.sh/uv/concepts/projects/#lockfile"&gt;a universal or cross-platform lockfile&lt;/a&gt; that can support locking dependencies for multiple platforms.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astral.sh/uv/guides/scripts/"&gt;Single-file script execution&lt;/a&gt; using &lt;code&gt;uv run myscript.py&lt;/code&gt;, where those scripts can define their own dependencies using &lt;a href="https://peps.python.org/pep-0723/"&gt;PEP 723 inline metadata&lt;/a&gt;. These dependencies are listed in a specially formatted comment and will be installed into a virtual environment before the script is executed.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astral.sh/uv/concepts/python-versions/"&gt;Python version management&lt;/a&gt; similar to &lt;a href="https://docs.astral.sh/uv/concepts/python-versions/"&gt;pyenv&lt;/a&gt;. The new &lt;code&gt;uv python list&lt;/code&gt; command lists all Python versions available on your system (including detecting various system and Homebrew installations), and &lt;code&gt;uv python install 3.13&lt;/code&gt; can then install a uv-managed Python using  Gregory Szorc's invaluable &lt;a href="https://github.com/indygreg/python-build-standalone"&gt;python-build-standalone&lt;/a&gt; releases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's all accompanied by &lt;a href="https://docs.astral.sh/uv/"&gt;new and very thorough documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The paint isn't even dry on this stuff - it's only been out for a few hours - but this feels &lt;em&gt;very&lt;/em&gt; promising to me. The idea that you can install &lt;code&gt;uv&lt;/code&gt; (a single Rust binary) and then start running all of these commands to manage Python installations and their dependencies is very appealing.&lt;/p&gt;
&lt;p&gt;If you’re wondering about the relationship between this and Rye - another project that Astral adopted solving a subset of these problems - &lt;a href="https://github.com/astral-sh/rye/discussions/1342"&gt;this forum thread&lt;/a&gt; clarifies that they intend to continue maintaining Rye but are eager for &lt;code&gt;uv&lt;/code&gt; to work as a full replacement.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/charliermarsh/status/1825958674239803515"&gt;@charliermarsh&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rust"&gt;rust&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rye"&gt;rye&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/astral"&gt;astral&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="rust"/><category term="rye"/><category term="uv"/><category term="astral"/></entry><entry><title>Writing your pyproject.toml</title><link href="https://simonwillison.net/2024/Aug/20/writing-your-pyproject-toml/#atom-tag" rel="alternate"/><published>2024-08-20T00:12:21+00:00</published><updated>2024-08-20T00:12:21+00:00</updated><id>https://simonwillison.net/2024/Aug/20/writing-your-pyproject-toml/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/"&gt;Writing your pyproject.toml&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
When I started &lt;a href="https://til.simonwillison.net/python/pyproject"&gt;exploring pyproject.toml a year ago&lt;/a&gt; I had trouble finding comprehensive documentation about what should go in that file.&lt;/p&gt;
&lt;p&gt;Since then the &lt;a href="https://packaging.python.org/"&gt;Python Packaging Guide&lt;/a&gt; split out &lt;a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/"&gt;this page&lt;/a&gt;, which is exactly what I was looking for back then.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://github.com/simonw/click-app/pull/10"&gt;PR against click-app from @lonnen&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/></entry><entry><title>cibuildwheel 2.20.0 now builds Python 3.13 wheels by default</title><link href="https://simonwillison.net/2024/Aug/6/cibuildwheel/#atom-tag" rel="alternate"/><published>2024-08-06T22:54:44+00:00</published><updated>2024-08-06T22:54:44+00:00</updated><id>https://simonwillison.net/2024/Aug/6/cibuildwheel/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/pypa/cibuildwheel/releases/tag/v2.20.0"&gt;cibuildwheel 2.20.0 now builds Python 3.13 wheels by default&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CPython 3.13 wheels are now built by default […] This release includes CPython 3.13.0rc1, which is guaranteed to be ABI compatible with the final release.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://cibuildwheel.pypa.io/"&gt;cibuildwheel&lt;/a&gt; is an underrated but crucial piece of the overall Python ecosystem.&lt;/p&gt;
&lt;p&gt;Python wheel packages that include binary compiled components - packages with C extensions for example - need to be built multiple times, once for each combination of Python version, operating system and architecture.&lt;/p&gt;
&lt;p&gt;A package like Adam Johnson’s &lt;a href="https://github.com/adamchainz/time-machine"&gt;time-machine&lt;/a&gt; - which bundles a &lt;a href="https://github.com/adamchainz/time-machine/blob/main/src/_time_machine.c"&gt;500 line C extension&lt;/a&gt; - can end up with &lt;a href="https://pypi.org/project/time-machine/#files"&gt;55 different wheel files&lt;/a&gt; with names like &lt;code&gt;time_machine-2.15.0-cp313-cp313-win_arm64.whl&lt;/code&gt; and &lt;code&gt;time_machine-2.15.0-cp38-cp38-musllinux_1_2_x86_64.whl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Without these wheels, anyone who runs &lt;code&gt;pip install time-machine&lt;/code&gt; will need to have a working C compiler toolchain on their machine for the command to work.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cibuildwheel&lt;/code&gt; solves the problem of building all of those wheels for all of those different platforms on the CI provider of your choice. Adam is using it in GitHub Actions for &lt;code&gt;time-machine&lt;/code&gt;, and his &lt;a href="https://github.com/adamchainz/time-machine/blob/2.15.0/.github/workflows/build.yml"&gt;.github/workflows/build.yml&lt;/a&gt; file neatly demonstrates how concise the configuration can be once you figure out how to use it.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.python.org/downloads/release/python-3130rc1/"&gt;first release candidate of Python 3.13&lt;/a&gt; hit its target release date of August 1st, and the final version looks on schedule for release on the 1st of October. Since this rc should be binary compatible with the final build now is the time to start shipping those wheels to PyPI.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/adamchainz/status/1820821199850476004"&gt;@adamchainz&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/continuous-integration"&gt;continuous-integration&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/adam-johnson"&gt;adam-johnson&lt;/a&gt;&lt;/p&gt;



</summary><category term="continuous-integration"/><category term="packaging"/><category term="pypi"/><category term="python"/><category term="adam-johnson"/></entry><entry><title>npm install everything, and the complete and utter chaos that follows</title><link href="https://simonwillison.net/2024/Mar/16/npm-install-everything/#atom-tag" rel="alternate"/><published>2024-03-16T05:18:51+00:00</published><updated>2024-03-16T05:18:51+00:00</updated><id>https://simonwillison.net/2024/Mar/16/npm-install-everything/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://boehs.org/node/npm-everything"&gt;npm install everything, and the complete and utter chaos that follows&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Here’s an experiment which went really badly wrong: a team of mostly-students decided to see if it was possible to install every package from npm (all 2.5 million of them) on the same machine. As part of that experiment they created and published their own npm package that depended on every other package in the registry.&lt;/p&gt;

&lt;p&gt;Unfortunately, in response to the leftpad incident a few years ago npm had introduced a policy that a package cannot be removed from the registry if there exists at least one other package that lists it as a dependency. The new “everything” package inadvertently prevented all 2.5m packages—including many that had no other dependencies—from ever being removed!

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/46dgy1/npm_install_everything_complete_utter"&gt;lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/npm"&gt;npm&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="security"/><category term="npm"/></entry><entry><title>Python packaging must be getting better - a datapoint</title><link href="https://simonwillison.net/2024/Jan/22/python-packaging-must-be-getting-better/#atom-tag" rel="alternate"/><published>2024-01-22T18:06:49+00:00</published><updated>2024-01-22T18:06:49+00:00</updated><id>https://simonwillison.net/2024/Jan/22/python-packaging-must-be-getting-better/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lukeplant.me.uk/blog/posts/python-packaging-must-be-getting-better-a-datapoint/"&gt;Python packaging must be getting better - a datapoint&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Luke Plant reports on a recent project he developed on Linux using a requirements.txt file and some complex binary dependencies—Qt5 and VTK—and when he tried to run it on Windows... it worked! No modifications required.&lt;/p&gt;

&lt;p&gt;I think Python’s packaging system has never been more effective... provided you know how to use it. The learning curve is still too high, which I think accounts for the bulk of complaints about it today.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/slkn4g/python_packaging_must_be_getting_better"&gt;lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/luke-plant"&gt;luke-plant&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/windows"&gt;windows&lt;/a&gt;&lt;/p&gt;



</summary><category term="luke-plant"/><category term="packaging"/><category term="python"/><category term="windows"/></entry><entry><title>My User Experience Porting Off setup.py</title><link href="https://simonwillison.net/2023/Oct/31/my-user-experience-porting-off-setuppy/#atom-tag" rel="alternate"/><published>2023-10-31T19:57:19+00:00</published><updated>2023-10-31T19:57:19+00:00</updated><id>https://simonwillison.net/2023/Oct/31/my-user-experience-porting-off-setuppy/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://gregoryszorc.com/blog/2023/10/30/my-user-experience-porting-off-setup.py/"&gt;My User Experience Porting Off setup.py&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
PyOxidizer maintainer Gregory Szorc provides a detailed account of his experience trying to figure out how to switch from setup.py to pyproject.toml for his zstandard Python package.&lt;/p&gt;

&lt;p&gt;This kind of detailed usability feedback is incredibly valuable for project maintainers, especially when the user encountered this many different frustrations along the way. It’s like the written version of a detailed usability testing session.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://discuss.python.org/t/user-experience-with-porting-off-setup-py/37502"&gt;discuss.python.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/packaging"&gt;packaging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/usability"&gt;usability&lt;/a&gt;&lt;/p&gt;



</summary><category term="packaging"/><category term="python"/><category term="usability"/></entry></feed>