<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: psf</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/psf.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2026-01-13T23:58:17+00:00</updated><author><name>Simon Willison</name></author><entry><title>Anthropic invests $1.5 million in the Python Software Foundation and open source security</title><link href="https://simonwillison.net/2026/Jan/13/anthropic-invests-15-million-in-the-python-software-foundation-a/#atom-tag" rel="alternate"/><published>2026-01-13T23:58:17+00:00</published><updated>2026-01-13T23:58:17+00:00</updated><id>https://simonwillison.net/2026/Jan/13/anthropic-invests-15-million-in-the-python-software-foundation-a/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://pyfound.blogspot.com/2025/12/anthropic-invests-in-python.html?m=1"&gt;Anthropic invests $1.5 million in the Python Software Foundation and open source security&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is outstanding news, especially given our decision to withdraw from that NSF grant application &lt;a href="https://simonwillison.net/2025/Oct/27/psf-withdrawn-proposal/"&gt;back in October&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We are thrilled to announce that Anthropic has entered into a two-year partnership with the Python Software Foundation (PSF) to contribute a landmark total of $1.5 million to support the foundation’s work, with an emphasis on Python ecosystem security. This investment will enable the PSF to make crucial security advances to CPython and the Python Package Index (PyPI) benefiting all users, and it will also sustain the foundation’s core work supporting the Python language, ecosystem, and global community.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that while security is a focus these funds will also support other aspects of the PSF's work:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Anthropic’s support will also go towards the PSF’s core work, including the Developer in Residence program driving contributions to CPython, community support through grants and other programs, running core infrastructure such as PyPI, and more.&lt;/p&gt;
&lt;/blockquote&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/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/psf"&gt;psf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/anthropic"&gt;anthropic&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="python"/><category term="ai"/><category term="psf"/><category term="anthropic"/></entry><entry><title>PyCon US 2026 call for proposals is now open</title><link href="https://simonwillison.net/2025/Nov/2/pycon-us-2026/#atom-tag" rel="alternate"/><published>2025-11-02T19:22:46+00:00</published><updated>2025-11-02T19:22:46+00:00</updated><id>https://simonwillison.net/2025/Nov/2/pycon-us-2026/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://pycon.blogspot.com/2025/10/pycon-us-2026-call-for-proposals-now.html"&gt;PyCon US 2026 call for proposals is now open&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
PyCon US is coming to the US west coast! 2026 and 2027 will both be held in Long Beach, California - the 2026 conference is set for May 13th-19th next year.&lt;/p&gt;
&lt;p&gt;The call for proposals just opened. Since we'll be in LA County I'd love to see talks about Python in the entertainment industry - if you know someone who could present on that topic please make sure they know about the CFP!&lt;/p&gt;
&lt;p&gt;The deadline for submissions is December 19th 2025. There are two new tracks this year:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PyCon US is introducing two dedicated Talk tracks to the schedule this year, "The Future of AI with Python" and "Trailblazing Python Security". For more information and how to submit your proposal, &lt;a href="https://us.pycon.org/2026/speaking/guidelines/"&gt;visit this page&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now is also a great time to consider sponsoring PyCon - here's &lt;a href="https://s3.dualstack.us-east-2.amazonaws.com/pythondotorg-assets/media/files/psf_sponsor_prospectus_25-26_final_compressed.pdf"&gt;the sponsorship prospectus&lt;/a&gt;.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://bsky.app/profile/pycon.us/post/3m4j34eloes25"&gt;@pycon.us&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/call-for-proposals"&gt;call-for-proposals&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/conferences"&gt;conferences&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pycon"&gt;pycon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;



</summary><category term="call-for-proposals"/><category term="conferences"/><category term="pycon"/><category term="python"/><category term="psf"/></entry><entry><title>The PSF has withdrawn a $1.5 million proposal to US government grant program</title><link href="https://simonwillison.net/2025/Oct/27/psf-withdrawn-proposal/#atom-tag" rel="alternate"/><published>2025-10-27T20:32:07+00:00</published><updated>2025-10-27T20:32:07+00:00</updated><id>https://simonwillison.net/2025/Oct/27/psf-withdrawn-proposal/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://pyfound.blogspot.com/2025/10/NSF-funding-statement.html"&gt;The PSF has withdrawn a $1.5 million proposal to US government grant program&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The Python Software Foundation was recently "recommended for funding" (NSF terminology) for a $1.5m grant from the US government National Science Foundation to help improve the security of the Python software ecosystem, after an grant application process lead by Seth Larson and Loren Crary.&lt;/p&gt;
&lt;p&gt;The PSF's annual budget is less than $6m so this is a meaningful amount of money for the organization!&lt;/p&gt;
&lt;p&gt;We were forced to withdraw our application and turn down the funding, thanks to new language that was added to the agreement requiring us to affirm that we "do not, and will not during the term of this financial assistance award, operate any programs that advance or promote DEI, or discriminatory equity ideology in violation of Federal anti-discrimination laws."&lt;/p&gt;
&lt;p&gt;Our legal advisors confirmed that this would not just apply to security work covered by the grant - this would apply to all of the PSF's activities.&lt;/p&gt;
&lt;p&gt;This was not an option for us. Here's the &lt;a href="https://www.python.org/psf/mission/"&gt;mission&lt;/a&gt; of the PSF:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The mission of the Python Software Foundation is to promote, protect, and advance the Python programming language, and to support and facilitate the growth of a diverse and international community of Python programmers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we accepted and spent the money despite this term, there was a very real risk that the money could be clawed back later. That represents an existential risk for the foundation since we would have already spent the money!&lt;/p&gt;
&lt;p&gt;I was one of the board members who voted to reject this funding - a unanimous but tough decision. I’m proud to serve on a board that can make difficult decisions like this.&lt;/p&gt;
&lt;p&gt;If you'd like to sponsor the PSF you can find out more &lt;a href="https://www.python.org/sponsors/application/"&gt;on our site&lt;/a&gt;. I'd love to see a few more of the large AI labs show up &lt;a href="https://www.python.org/psf/sponsors/"&gt;on our top-tier visionary sponsors list&lt;/a&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/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="python"/><category term="psf"/></entry><entry><title>Python 3.14</title><link href="https://simonwillison.net/2025/Oct/8/python-314/#atom-tag" rel="alternate"/><published>2025-10-08T04:10:06+00:00</published><updated>2025-10-08T04:10:06+00:00</updated><id>https://simonwillison.net/2025/Oct/8/python-314/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.python.org/downloads/release/python-3140/"&gt;Python 3.14&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This year's major Python version, Python 3.14, just made its first stable release!&lt;/p&gt;
&lt;p&gt;As usual the &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html"&gt;what's new in Python 3.14&lt;/a&gt; document is the best place to get familiar with the new release:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The biggest changes include &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-template-string-literals"&gt;template string literals&lt;/a&gt;, &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-deferred-annotations"&gt;deferred evaluation of annotations&lt;/a&gt;, and support for &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-multiple-interpreters"&gt;subinterpreters&lt;/a&gt; in the standard library.&lt;/p&gt;
&lt;p&gt;The library changes include significantly improved capabilities for &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-asyncio-introspection"&gt;introspection in asyncio&lt;/a&gt;, &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-zstandard"&gt;support for Zstandard&lt;/a&gt; via a new &lt;a href="https://docs.python.org/3.14/library/compression.zstd.html#module-compression.zstd"&gt;compression.zstd&lt;/a&gt; module, syntax highlighting in the REPL, as well as the usual deprecations and removals, and improvements in user-friendliness and correctness.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Subinterpreters look particularly interesting as a way to use multiple CPU cores to run Python code despite the continued existence of the GIL. If you're feeling brave and &lt;a href="https://hugovk.github.io/free-threaded-wheels/"&gt;your dependencies cooperate&lt;/a&gt; you can also use the free-threaded build of Python 3.14 - &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-free-threaded-now-supported"&gt;now officially supported&lt;/a&gt; - to skip the GIL entirely.&lt;/p&gt;
&lt;p&gt;A new major Python release means an older release hits the &lt;a href="https://devguide.python.org/versions/"&gt;end of its support lifecycle&lt;/a&gt; - in this case that's Python 3.9. If you maintain open source libraries that target every supported Python versions (as I do) this means features introduced in Python 3.10 can now be depended on! &lt;a href="https://docs.python.org/3.14/whatsnew/3.10.html"&gt;What's new in Python 3.10&lt;/a&gt; lists those - I'm most excited by &lt;a href="https://docs.python.org/3.14/whatsnew/3.10.html#pep-634-structural-pattern-matching"&gt;structured pattern matching&lt;/a&gt; (the &lt;code&gt;match/case&lt;/code&gt; statement) and the &lt;a href="https://docs.python.org/3.14/whatsnew/3.10.html#pep-604-new-type-union-operator"&gt;union type operator&lt;/a&gt;, allowing &lt;code&gt;int | float | None&lt;/code&gt; as a type annotation in place of &lt;code&gt;Optional[Union[int, float]]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you use &lt;code&gt;uv&lt;/code&gt; you can grab a copy of 3.14 using:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv self update
uv python upgrade 3.14
uvx python@3.14
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or for free-threaded Python 3.1;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uvx python@3.14t
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;uv&lt;/code&gt; team wrote &lt;a href="https://astral.sh/blog/python-3.14"&gt;about their Python 3.14 highlights&lt;/a&gt; in their announcement of Python 3.14's availability via &lt;code&gt;uv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The GitHub Actions &lt;a href="https://github.com/actions/setup-python"&gt;setup-python action&lt;/a&gt; includes Python 3.14 now too, so the following YAML snippet in will run tests on all currently supported versions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;strategy:
  matrix:
    python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/setup-python@v6
  with:
    python-version: ${{ matrix.python-version }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/datasette-pretty-traces/blob/3edddecab850d6ac47ed128a400b6a0ff8b0c012/.github/workflows/test.yml"&gt;Full example here&lt;/a&gt; for one of my many Datasette plugin repos.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/gil"&gt;gil&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/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/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="gil"/><category term="open-source"/><category term="python"/><category term="github-actions"/><category term="psf"/><category term="uv"/></entry><entry><title>Announcing the 2025 PSF Board Election Results!</title><link href="https://simonwillison.net/2025/Sep/16/the-2025-psf-board-election-results/#atom-tag" rel="alternate"/><published>2025-09-16T20:39:41+00:00</published><updated>2025-09-16T20:39:41+00:00</updated><id>https://simonwillison.net/2025/Sep/16/the-2025-psf-board-election-results/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://pyfound.blogspot.com/2025/09/announcing-2025-psf-board-election.html"&gt;Announcing the 2025 PSF Board Election Results!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I'm happy to share that I've been re-elected for  second term on the board of directors of the Python Software Foundation.&lt;/p&gt;
&lt;p&gt;Jannis Leidel was also re-elected and Abigail Dogbe and Sheena O’Connell will be joining the board for the first time.


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



</summary><category term="python"/><category term="psf"/></entry><entry><title>The 2025 PSF Board Election is Open!</title><link href="https://simonwillison.net/2025/Sep/9/psf-board-election/#atom-tag" rel="alternate"/><published>2025-09-09T10:13:33+00:00</published><updated>2025-09-09T10:13:33+00:00</updated><id>https://simonwillison.net/2025/Sep/9/psf-board-election/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://pyfound.blogspot.com/2025/09/the-2025-psf-board-election-is-open.html"&gt;The 2025 PSF Board Election is Open!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The Python Software Foundation's annual board member election is taking place right now, with votes (from previously affirmed voting members) accepted from September 2nd, 2:00 pm UTC through Tuesday, September 16th, 2:00 pm UTC.&lt;/p&gt;
&lt;p&gt;I've served on the board since 2022 and I'm running for a second term. Here's the opening section of my &lt;a href="https://www.python.org/nominations/elections/2025-python-software-foundation-board/nominees/"&gt;nomination statement&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi, I'm Simon Willison. I've been a board member of the Python Software Foundation since 2022 and I'm running for re-election in 2025.&lt;/p&gt;
&lt;p&gt;Last year I wrote a detailed article about &lt;a href="https://simonwillison.net/2024/Sep/18/board-of-the-python-software-foundation/"&gt;Things I’ve learned serving on the board of the Python Software Foundation&lt;/a&gt;. I hope to continue learning and sharing what I've learned for a second three-year term.&lt;/p&gt;
&lt;p&gt;One of my goals for a second term is to help deepen the relationship between the AI research world and the Python Software Foundation. There is an enormous amount of value being created in the AI space using Python and I would like to see more of that value flow back into the rest of the Python ecosystem.&lt;/p&gt;
&lt;p&gt;I see the Python Package Index (PyPI) as one of the most impactful projects of the Python Software Foundation and plan to continue to advocate for further investment in the PyPI team and infrastructure.&lt;/p&gt;
&lt;p&gt;As a California resident I'm excited to see PyCon return to the West Coast, and I'm looking forward to getting involved in helping make PyCon 2026 and 2027 in Long Beach, California as successful as possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I'm delighted to have been endorsed this year by &lt;a href="https://inventwithpython.com/blog/psf-candidate-endorsements-2025.html"&gt;Al Sweigart&lt;/a&gt;, &lt;a href="https://fosstodon.org/@lorenipsum/115170249309856873"&gt;Loren Crary&lt;/a&gt; and &lt;a href="https://social.coop/@chrisjrn/115135449245231588"&gt;Christopher Neugebauer&lt;/a&gt;. If you are a voting member I hope I have earned your vote this year.&lt;/p&gt;
&lt;p&gt;You can watch video introductions from several of the other nominees &lt;a href="https://www.youtube.com/watch?v=MM9lLXH-GjA"&gt;in this six minute YouTube video&lt;/a&gt; and &lt;a href="https://www.youtube.com/playlist?list=PLFIcqSiijithlBSVBvZzrlGwhGfuT8uzp"&gt;this playlist&lt;/a&gt;.


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



</summary><category term="python"/><category term="psf"/></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>PyPI now supports project archival</title><link href="https://simonwillison.net/2025/Jan/30/pypi-now-supports-project-archival/#atom-tag" rel="alternate"/><published>2025-01-30T16:46:34+00:00</published><updated>2025-01-30T16:46:34+00:00</updated><id>https://simonwillison.net/2025/Jan/30/pypi-now-supports-project-archival/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.pypi.org/posts/2025-01-30-archival/"&gt;PyPI now supports project archival&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Neat new PyPI feature, similar to GitHub's &lt;a href="https://docs.github.com/en/repositories/archiving-a-github-repository/archiving-repositories"&gt;archiving repositories&lt;/a&gt; feature. You can now mark a PyPI project as "archived", making it clear that no new releases are planned (though you can switch back out of that mode later if you need to).&lt;/p&gt;
&lt;p&gt;I like the sound of these future plans around this topic:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Project archival is the first step in a larger project, aimed at improving the &lt;em&gt;lifecycle&lt;/em&gt; of projects on PyPI. That project includes evaluating additional project statuses (things like "deprecated" and "unmaintained"), as well as changes to &lt;a href="https://docs.pypi.org/api/"&gt;PyPI's public APIs&lt;/a&gt; that will enable clients to retrieve and act on project status information. You can track our progress on these fronts by following along with &lt;a href="https://github.com/pypi/warehouse/issues/16844"&gt;warehouse#16844&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;


    &lt;p&gt;Tags: &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/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;



</summary><category term="pypi"/><category term="python"/><category term="psf"/></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>Perks of Being a Python Core Developer</title><link href="https://simonwillison.net/2024/Oct/12/perks-of-being-a-python-core-developer/#atom-tag" rel="alternate"/><published>2024-10-12T16:34:16+00:00</published><updated>2024-10-12T16:34:16+00:00</updated><id>https://simonwillison.net/2024/Oct/12/perks-of-being-a-python-core-developer/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://mariatta.ca/posts/perks-of-python-core/"&gt;Perks of Being a Python Core Developer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Mariatta Wijaya provides a detailed breakdown of the exact capabilities and privileges that are granted to Python core developers - including commit access to the Python &lt;code&gt;main&lt;/code&gt;, the ability to write or sponsor PEPs, the ability to vote on new core developers and for the steering council election and financial support from the PSF for travel expenses related to PyCon and core development sprints.&lt;/p&gt;
&lt;p&gt;Not to be under-estimated is that you also gain respect:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Everyone’s always looking for ways to stand out in resumes, right? So do I. I’ve been an engineer for longer than I’ve been a core developer, and I do notice that having the extra title like open source maintainer and public speaker really make a difference. As a woman, as someone with foreign last name that nobody knows how to pronounce, as someone who looks foreign, and speaks in a foreign accent, having these extra “credentials” helped me be seen as more or less equal compared to other people.&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/muormf/perks_being_python_core_developer"&gt;lobste.rs&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/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="python"/><category term="psf"/></entry><entry><title>Weeknotes: Three podcasts, two trips and a new plugin system</title><link href="https://simonwillison.net/2024/Sep/30/weeknotes/#atom-tag" rel="alternate"/><published>2024-09-30T17:43:22+00:00</published><updated>2024-09-30T17:43:22+00:00</updated><id>https://simonwillison.net/2024/Sep/30/weeknotes/#atom-tag</id><summary type="html">
    &lt;p&gt;I fell behind a bit on my weeknotes. Here's most of what I've been doing in September.&lt;/p&gt;
&lt;h4 id="lisbon-portugal-and-durham-north-carolina"&gt;Lisbon, Portugal and Durham, North Carolina&lt;/h4&gt;
&lt;p&gt;I had two trips this month. The first was a short visit to Lisbon, Portugal for the Python Software Foundation's annual board retreat. This inspired me to write about &lt;a href="https://simonwillison.net/2024/Sep/18/board-of-the-python-software-foundation/"&gt;Things I've learned serving on the board of the Python Software Foundation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The second was to Durham, North Carolina for DjangoCon US 2024. I wrote about that one in &lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2024/"&gt;Themes from DjangoCon US 2024&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My talk at DjangoCon was about plugin systems, and in a classic example of conference-driven development I ended up writing and releasing a new plugin system for Django in preparation for that talk. I introduced that in &lt;a href="https://simonwillison.net/2024/Sep/25/djp-a-plugin-system-for-django/"&gt;DJP: A plugin system for Django&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="podcasts"&gt;Podcasts&lt;/h4&gt;
&lt;p&gt;I haven't been a podcast guest &lt;a href="https://simonwillison.net/search/?year=2024&amp;amp;month=1&amp;amp;tag=podcasts"&gt;since January&lt;/a&gt;, and then three came along at once! All three appearences involved LLMs in some way but I don't think there was a huge amount of overlap in terms of what I actually said.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I went on &lt;a href="https://simonwillison.net/2024/Sep/10/software-misadventures/"&gt;The Software Misadventures Podcast&lt;/a&gt; to talk about my career to-date.&lt;/li&gt;
&lt;li&gt;My appearance &lt;a href="https://simonwillison.net/2024/Sep/20/using-llms-for-code/"&gt;on TWIML&lt;/a&gt; dug into ways in which I use Claude and ChatGPT to help me write code.&lt;/li&gt;
&lt;li&gt;I was the guest for the inaugral episode of Gergely Orosz's &lt;a href="https://newsletter.pragmaticengineer.com/p/ai-tools-for-software-engineers-simon-willison"&gt;Pragmatic Engineer Podcast&lt;/a&gt;, which ended up touching on a whole array of different topics relevant to modern software engineering, from the importance of open source to the impact AI tools are likely to have on our industry.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Gergely has been sharing neat edited snippets from our conversation on Twitter. Here's &lt;a href="https://twitter.com/GergelyOrosz/status/1839682428471779596"&gt;one on RAG&lt;/a&gt; and another about &lt;a href="https://twitter.com/GergelyOrosz/status/1840779737297260646"&gt;how open source has been the the biggest productivity boost&lt;/a&gt; of my career.&lt;/p&gt;
&lt;h4 id="on-the-blog"&gt;On the blog&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/29/notebooklm-audio-overview/"&gt;NotebookLM's automatically generated podcasts are surprisingly effective&lt;/a&gt; - Sept. 29, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2024/"&gt;Themes from DjangoCon US 2024&lt;/a&gt; - Sept. 27, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/25/djp-a-plugin-system-for-django/"&gt;DJP: A plugin system for Django&lt;/a&gt; - Sept. 25, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/20/using-llms-for-code/"&gt;Notes on using LLMs for code&lt;/a&gt; - Sept. 20, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/18/board-of-the-python-software-foundation/"&gt;Things I've learned serving on the board of the Python Software Foundation&lt;/a&gt; - Sept. 18, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/12/openai-o1/"&gt;Notes on OpenAI's new o1 chain-of-thought models&lt;/a&gt; - Sept. 12, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/10/software-misadventures/"&gt;Notes from my appearance on the Software Misadventures Podcast&lt;/a&gt; - Sept. 10, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/8/teresa-t-whale-pillar-point/"&gt;Teresa T is name of the whale in Pillar Point Harbor near Half Moon Bay&lt;/a&gt; - Sept. 8, 2024&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="museums"&gt;Museums&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.niche-museums.com/112"&gt;The Vincent and Ethel Simonetti Historic Tuba Collection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="releases"&gt;Releases&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/shot-scraper/releases/tag/1.5"&gt;shot-scraper 1.5&lt;/a&gt;&lt;/strong&gt; - 2024-09-27&lt;br /&gt;A command-line utility for taking automated screenshots of websites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-plugin-datasette/releases/tag/0.2"&gt;django-plugin-datasette 0.2&lt;/a&gt;&lt;/strong&gt; - 2024-09-26&lt;br /&gt;Django plugin to run Datasette inside of Django&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/djp/releases/tag/0.3.1"&gt;djp 0.3.1&lt;/a&gt;&lt;/strong&gt; - 2024-09-26&lt;br /&gt;A plugin system for Django&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm-gemini/releases/tag/0.1a5"&gt;llm-gemini 0.1a5&lt;/a&gt;&lt;/strong&gt; - 2024-09-24&lt;br /&gt;LLM plugin to access Google's Gemini family of models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-plugin-blog/releases/tag/0.1.1"&gt;django-plugin-blog 0.1.1&lt;/a&gt;&lt;/strong&gt; - 2024-09-24&lt;br /&gt;A blog for Django as a DJP plugin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-plugin-database-url/releases/tag/0.1"&gt;django-plugin-database-url 0.1&lt;/a&gt;&lt;/strong&gt; - 2024-09-24&lt;br /&gt;Django plugin for reading the DATABASE_URL environment variable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-plugin-django-header/releases/tag/0.1.1"&gt;django-plugin-django-header 0.1.1&lt;/a&gt;&lt;/strong&gt; - 2024-09-23&lt;br /&gt;Add a Django-Compositions HTTP header to a Django app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm-jina-api/releases/tag/0.1a0"&gt;llm-jina-api 0.1a0&lt;/a&gt;&lt;/strong&gt; - 2024-09-20&lt;br /&gt;Access Jina AI embeddings via their API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm/releases/tag/0.16"&gt;llm 0.16&lt;/a&gt;&lt;/strong&gt; - 2024-09-12&lt;br /&gt;Access large language models from the command-line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/datasette/datasette-acl/releases/tag/0.4a4"&gt;datasette-acl 0.4a4&lt;/a&gt;&lt;/strong&gt; - 2024-09-10&lt;br /&gt;Advanced permission management for Datasette&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm-cmd/releases/tag/0.2a0"&gt;llm-cmd 0.2a0&lt;/a&gt;&lt;/strong&gt; - 2024-09-09&lt;br /&gt;Use LLM to generate and execute commands in your shell&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/files-to-prompt/releases/tag/0.3"&gt;files-to-prompt 0.3&lt;/a&gt;&lt;/strong&gt; - 2024-09-09&lt;br /&gt;Concatenate a directory full of files into a single prompt for use with LLMs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/json-flatten/releases/tag/0.3.1"&gt;json-flatten 0.3.1&lt;/a&gt;&lt;/strong&gt; - 2024-09-07&lt;br /&gt;Python functions for flattening a JSON object to a single dictionary of pairs, and unflattening that dictionary back to a JSON object&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/csv-diff/releases/tag/1.2"&gt;csv-diff 1.2&lt;/a&gt;&lt;/strong&gt; - 2024-09-06&lt;br /&gt;Python CLI tool and library for diffing CSV and JSON files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette/releases/tag/1.0a16"&gt;datasette 1.0a16&lt;/a&gt;&lt;/strong&gt; - 2024-09-06&lt;br /&gt;An open source multi-tool for exploring and publishing data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-search-all/releases/tag/1.1.4"&gt;datasette-search-all 1.1.4&lt;/a&gt;&lt;/strong&gt; - 2024-09-06&lt;br /&gt;Datasette plugin for searching all searchable tables at once&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="tils"&gt;TILs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/llms/streaming-llm-apis"&gt;How streaming LLM APIs work&lt;/a&gt; - 2024-09-21&lt;/li&gt;
&lt;/ul&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/podcasts"&gt;podcasts&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djp"&gt;djp&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="django"/><category term="podcasts"/><category term="weeknotes"/><category term="psf"/><category term="llms"/><category term="djp"/></entry><entry><title>Things I've Learned Serving on the Board of The Perl Foundation</title><link href="https://simonwillison.net/2024/Sep/24/the-board-of-the-perl-foundation/#atom-tag" rel="alternate"/><published>2024-09-24T01:42:14+00:00</published><updated>2024-09-24T01:42:14+00:00</updated><id>https://simonwillison.net/2024/Sep/24/the-board-of-the-perl-foundation/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blogs.perl.org/users/makoto_nozaki/2024/09/things-ive-learned-serving-on-the-board-of-the-perl-foundation.html"&gt;Things I&amp;#x27;ve Learned Serving on the Board of The Perl Foundation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
My &lt;a href="https://simonwillison.net/2024/Sep/18/board-of-the-python-software-foundation/"&gt;post about the PSF board&lt;/a&gt; inspired Perl Foundation secretary Makoto Nozaki to publish similar notes about how TPF (also known since 2019 as TPRF, for The Perl and Raku Foundation) operates.&lt;/p&gt;
&lt;p&gt;Seeing this level of explanation about other open source foundations is fascinating. I’d love to see more of these.&lt;/p&gt;
&lt;p&gt;Along those lines, I found the &lt;a href="https://ziglang.org/news/2024-financials/"&gt;2024 Financial Report&lt;/a&gt; from the Zig foundation really interesting too.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/heismakoto/status/1838389539204641143"&gt;@ heismakoto&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/perl"&gt;perl&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/zig"&gt;zig&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="perl"/><category term="zig"/><category term="psf"/></entry><entry><title>Things I've learned serving on the board of the Python Software Foundation</title><link href="https://simonwillison.net/2024/Sep/18/board-of-the-python-software-foundation/#atom-tag" rel="alternate"/><published>2024-09-18T14:15:37+00:00</published><updated>2024-09-18T14:15:37+00:00</updated><id>https://simonwillison.net/2024/Sep/18/board-of-the-python-software-foundation/#atom-tag</id><summary type="html">
    &lt;p&gt;Two years ago &lt;a href="https://simonwillison.net/2022/Jul/30/psf-board/"&gt;I was elected&lt;/a&gt; to the board of directors for the &lt;a href="https://www.python.org/psf-landing/"&gt;Python Software Foundation&lt;/a&gt; - the PSF. I recently returned from the annual PSF board retreat (this one was in Lisbon, Portugal) and this feels like a good opportunity to write up some of the things I've learned along the way.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="#what-is-the-psf"&gt;What is the PSF?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#the-psf-employs-staff"&gt;The PSF employs staff&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#a-lot-of-this-is-about-money"&gt;A lot of this is about money&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#the-psf-does-not-directly-develop-python-itself"&gt;The PSF does not directly develop Python itself&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#pypi-the-python-package-index"&gt;PyPI - the Python Package Index&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#pycon-is-a-key-commitment"&gt;PyCon is a key commitment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#other-psf-activities"&gt;Other PSF activities&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#work-groups"&gt;Work Groups&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#acting-as-a-fiscal-sponsor"&gt;Acting as a fiscal sponsor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#life-as-a-board-member"&gt;Life as a board member&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#the-kinds-of-things-the-board-talks-about"&gt;The kinds of things the board talks about&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#want-to-know-more-"&gt;Want to know more?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="what-is-the-psf"&gt;What is the PSF?&lt;/h4&gt;
&lt;p&gt;The PSF is a &lt;a href="https://en.wikipedia.org/wiki/501(c)(3)_organization"&gt;US 501(c)(3)&lt;/a&gt; non-profit organization with the following &lt;a href="https://www.python.org/psf/mission/"&gt;mission&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The mission of the Python Software Foundation is to promote, protect, and advance the Python programming language, and to support and facilitate the growth of a diverse and international community of Python programmers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That mission definition is &lt;em&gt;really important&lt;/em&gt;. Board members and paid staff come and go, but the mission remains constant - it's the single most critical resource to help make decisions about whether the PSF should be investing time, money and effort into an activity or not.&lt;/p&gt;
&lt;p&gt;The board's 501(c)(3) status is predicated on following the &lt;a href="https://www.python.org/psf/mission/"&gt;full expanded mission statement&lt;/a&gt;. When our finances get audited (we conduct an annual "friendly audit", which is considered best practice for organizations at our size), the auditors need to be able to confirm that we've been supporting that mission through our management of the tax-exempt funds that have been entrusted to us.&lt;/p&gt;
&lt;p&gt;This auditability is an interesting aspect of how 501(c)(3) organizations work, because it means you can donate funds to them and know that the IRS will ostensibly be ensuring that the money is spent in a way that supports their stated mission.&lt;/p&gt;
&lt;p&gt;Board members have fiduciary responsibility for the PSF. A good explanation of this can be found &lt;a href="https://boardsource.org/resources/fiduciary-responsibilities/"&gt;here on BoardSource&lt;/a&gt;, which also has other useful resources for understanding &lt;a href="https://boardsource.org/fundamental-topics-of-nonprofit-board-service/roles-responsibilities/"&gt;the roles and responsibilities&lt;/a&gt; of non-profit board members.&lt;/p&gt;
&lt;p&gt;(Developing at least a loose intuition for US tax law around non-profits is one of the many surprising things that are necessary to be an effective board member.)&lt;/p&gt;
&lt;h4 id="the-psf-employs-staff"&gt;The PSF employs staff&lt;/h4&gt;
&lt;p&gt;The PSF currently employs &lt;a href="https://www.python.org/psf/records/staff/"&gt;12 full-time staff members&lt;/a&gt;. Members of the board do not directly manage the activities of the staff - in fact board members telling staff what to do is highly inappropriate.&lt;/p&gt;
&lt;p&gt;Instead, the board is responsible for hiring an Executive Director - currently Deb Nicholson - who is then responsible for hiring and managing (directly or indirectly) those other staff members. The board is responsible for evaluating the Executive Director's performance.&lt;/p&gt;
&lt;p&gt;I joined the board shortly after Deb was hired, so I have not personally been part of a board hiring committee for a new Executive Director.&lt;/p&gt;
&lt;p&gt;While paid staff support and enact many of the activities of the PSF, the foundation is fundamentally a volunteer-based organization. Many PSF activities are carried out by &lt;a href="https://www.python.org/psf/volunteer/"&gt;these volunteers&lt;/a&gt;, in particular via &lt;a href="#work-groups"&gt;Work Groups&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="a-lot-of-this-is-about-money"&gt;A lot of this is about money&lt;/h4&gt;
&lt;p&gt;A grossly simplified way to think about the PSF is that it's a bucket of money that is raised from &lt;a href="https://www.python.org/psf/sponsors/"&gt;sponsors&lt;/a&gt; and the Python community (via donations and membership fees), and then spent to support the community and the language in different ways.&lt;/p&gt;
&lt;p&gt;The PSF spends money on staff, on grants to Python-related causes and on infrastructure and activities that support Python development and the Python community itself.&lt;/p&gt;
&lt;p&gt;You can see how that money has been spent in the &lt;a href="https://www.python.org/psf/annual-report/2023/"&gt;2023 Annual Impact Report&lt;/a&gt;. The PSF had $4,356,000 revenue for that year and spent $4,508,000 - running a small loss, but not a concerning one given our assets from previous years.&lt;/p&gt;
&lt;p&gt;The most significant categories of expenditure in 2023 were PyCon US ($1,800,000), our Grants program ($677,000), Infrastructure (including PyPI) ($286,000) and our Fiscal Sponsorees ($204,000) - I'll describe these in more detail below.&lt;/p&gt;
&lt;h4 id="the-psf-does-not-directly-develop-python-itself"&gt;The PSF does not directly develop Python itself&lt;/h4&gt;
&lt;p&gt;This is an important detail to understand. The PSF is responsible for protecting and supporting the Python language and community, but development of &lt;a href="https://github.com/python/cpython"&gt;CPython&lt;/a&gt; itself is not directly managed by the PSF.&lt;/p&gt;
&lt;p&gt;Python development is handled by the &lt;a href="https://devguide.python.org/core-developers/developer-log/"&gt;Python core team&lt;/a&gt;, who are governed by the 5-person &lt;a href="https://github.com/python/steering-council/blob/main/README.md"&gt;Python Steering Council&lt;/a&gt;. The Steering Council is elected by the core team. The process for becoming a core developer &lt;a href="https://devguide.python.org/core-developers/become-core-developer/"&gt;is described here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;How this all works is defined by &lt;a href="https://peps.python.org/pep-0013/"&gt;PEP 13: Python Language Governance&lt;/a&gt; (and several subsequent PEPs). This structure was created - with much discussion - after Guido van Rossum stepped down from his role as Python BDFL in 2018.&lt;/p&gt;
&lt;p&gt;The PSF's executive director maintains close ties with the steering council, meeting with them 2-3 times a month. The PSF provides financial support for some Python core activities, such as infrastructure used for Python development and sponsoring travel to and logistics for core Python sprints.&lt;/p&gt;
&lt;p&gt;More recently, the PSF has started employing Developers in Residence to directly support the work of both the core Python team and initiatives such as the Python Package Index.&lt;/p&gt;
&lt;h4 id="pypi-the-python-package-index"&gt;PyPI - the Python Package Index&lt;/h4&gt;
&lt;p&gt;One of the most consequential projects directly managed by the PSF is &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt;, the Python Package Index. This is the system that enables &lt;code&gt;pip install name-of-package&lt;/code&gt; to do its thing.&lt;/p&gt;
&lt;p&gt;Having PyPI managed by a non-profit that answers directly to the community it serves is a very good thing.&lt;/p&gt;
&lt;p&gt;PyPI's numbers are staggering. Today there are 570,000 projects consisting of 12,035,133 files, serving 1.9 billion downloads a day (that number from &lt;a href="https://pypistats.org/packages/__all__"&gt;PyPI Stats&lt;/a&gt;). Bandwidth for these downloads is donated by &lt;a href="https://www.fastly.com/"&gt;Fastly&lt;/a&gt;, a PSF Visionary Sponsor who recently signed &lt;a href="https://fosstodon.org/@ThePSF/112456715341751673"&gt;a five year agreement&lt;/a&gt; to continue this service.&lt;/p&gt;
&lt;p&gt;(This was a big deal - prior to that agreement there was concern over what would happen if Fastly ever decided to end that sponsorship.)&lt;/p&gt;
&lt;h4 id="pycon-is-a-key-commitment"&gt;PyCon is a key commitment&lt;/h4&gt;
&lt;p&gt;The annual US Python Conference - &lt;a href="https://us.pycon.org/"&gt;PyCon US&lt;/a&gt; - is a big part of the PSF's annual activities and operations. With over 3,000 attendees each year (and a $1.8m budget for 2023) running that conference represents a full-time job for several PSF staff members.&lt;/p&gt;
&lt;p&gt;In the past PyCon US has also been responsible for the majority of the PSF's operating income. This is no longer true today - in fact it ran at a slight loss this year. This is not a big problem: the PSF's funding has diversified, and the importance of PyCon US to the Python community is such that the PSF is happy to lose money running the event if necessary.&lt;/p&gt;
&lt;h4 id="other-psf-activities"&gt;Other PSF activities&lt;/h4&gt;
&lt;p&gt;Many of these are detailed in &lt;a href="https://www.python.org/psf/mission/"&gt;the full mission statement&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Operating &lt;a href="https://www.python.org/"&gt;python.org&lt;/a&gt; and making Python available to download. It's interesting to note that Python is distributed through many alternative routes that are not managed by the PSF - through Linux packaging systems like Ubuntu, Debian and Red Hat, via tools like Docker or Homebrew, by companies such as &lt;a href="https://www.anaconda.com/download"&gt;Anaconda&lt;/a&gt; or through newer channels such as &lt;a href="https://docs.astral.sh/uv/guides/install-python/"&gt;uv&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Owning and protecting the Python trademarks and the Python intellectual property rights under the (&lt;a href="https://opensource.org/license/python-2-0"&gt;OSI compliant&lt;/a&gt;) Python license. This is one of the fundamental reasons for the organization to exist, but thankfully is one of the smaller commitments in terms of cost and staff time.&lt;/li&gt;
&lt;li&gt;Running the annual PyCon US conference.&lt;/li&gt;
&lt;li&gt;Operating the Python Packaging Index. Fastly provide the CDN, but the PSF still takes on the task of developing and operating the core PyPI web application and the large amounts of moderation and user support that entails.&lt;/li&gt;
&lt;li&gt;Supporting infrastructure used for core Python development, and logistics for core Python sprints.&lt;/li&gt;
&lt;li&gt;Issuing grants to Python community efforts.&lt;/li&gt;
&lt;li&gt;Caring for fiscal sponsorees.&lt;/li&gt;
&lt;li&gt;Supporting the work of PSF Work Groups.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="work-groups"&gt;Work Groups&lt;/h4&gt;
&lt;p&gt;A number of PSF initiatives take place in the form of Work Groups, &lt;a href="https://www.python.org/psf/workgroups/"&gt;listed here&lt;/a&gt;. Work Groups are teams of volunteers from the community who collaborate on projects relevant to the PSF's mission.&lt;/p&gt;
&lt;p&gt;Each Work Group sets its own cadence and ways of working. Some groups have decisions delegated to them by the board - for example the Grants Work Group for reviewing grant proposals and the Code of Conduct Work Group for enforcing Code of Conduct activity. Others coordinate technical projects such as the &lt;a href="https://wiki.python.org/psf/InfrastructureWG"&gt;Infrastructure Working Group&lt;/a&gt;, who manage and make decisions on various pieces of technical infrastructure relevant to Python and the PSF.&lt;/p&gt;
&lt;p&gt;Work Groups are formed by a board vote, with a designated charter. Most recently the board approved &lt;a href="https://github.com/psf/user-success-wg/blob/main/WG-charter.md"&gt;a charter&lt;/a&gt; for a new User Success Work Group, focusing on things like improving the new Python user onboarding experience.&lt;/p&gt;
&lt;h4 id="acting-as-a-fiscal-sponsor"&gt;Acting as a fiscal sponsor&lt;/h4&gt;
&lt;p&gt;This is another term I was unfamiliar with before joining the board: the idea of a &lt;strong&gt;fiscal sponsor&lt;/strong&gt;, which is a key role played by the PSF.&lt;/p&gt;
&lt;p&gt;Running a non-profit organization is decidedly non-trivial: you need a legal structure, a bank account, accounting, governance, the ability to handle audits - there's a whole lot of complexity behind the scenes.&lt;/p&gt;
&lt;p&gt;Looking to run an annual community conference? You'll need a bank account, and an entity that can sign agreements with venues and vendors.&lt;/p&gt;
&lt;p&gt;Want to accept donations to support work you are doing? Again, you need an entity, and a bank account, and some form of legal structure that ensures your donors can confidently trust you with their money.&lt;/p&gt;
&lt;p&gt;Instead of forming a whole new non-profit for this, you can instead find an existing non-profit that is willing to be your "fiscal sponsor". They'll handle the accounting and various other legal aspects, which allows you to invest your efforts in the distinctive work that you are trying to do.&lt;/p&gt;
&lt;p&gt;The PSF acts as a fiscal sponsor for a number of different organizations - 20 as-of the 2023 report - including PyLadies, Twisted, Pallets, Jazzband,  PyCascades and North Bay Python. The PSF's accounting team invest a great deal of effort in making all of this work.&lt;/p&gt;
&lt;p&gt;The PSF generally takes a 10% cut of donations to its fiscal sponsorees. This doesn't actually cover the full staffing cost of servicing these organizations, but this all still makes financial sense in terms of the PSF's mission to support the global Python community.&lt;/p&gt;
&lt;h4 id="life-as-a-board-member"&gt;Life as a board member&lt;/h4&gt;
&lt;p&gt;There are 12 board members. Elections are held every year after PyCon US, voted on by the PSF membership - by both paid members and members who have earned voting rights through being acknowledged as PSF fellows.&lt;/p&gt;
&lt;p&gt;Board members are elected for three year terms. Since 1-3 new board members are likely to join annually, these terms ensure there is overlap which helps maintain institutional knowledge about how the board operates.&lt;/p&gt;
&lt;p&gt;The board's activities are governed by &lt;a href="https://www.python.org/psf/bylaws/"&gt;the PSF Bylaws&lt;/a&gt;, and there is a documented process for modifying them (see ARTICLE XI).&lt;/p&gt;
&lt;p&gt;We have board members from all over the world. This is extremely important, because the PSF is responsible for the health and growth of the global Python community. A perennial concern is how to ensure that board candidates are nominated from around the world, in order to maintain that critical global focus.&lt;/p&gt;
&lt;p&gt;The board meets once a month over Zoom, has ongoing conversations via Slack and meets in-person twice a year: once at PyCon US and once at a "retreat" in a different global city, selected to try and minimize the total amount of travel needed to get all of our global board members together in the same place.&lt;/p&gt;
&lt;p&gt;Our most recent retreat was in Lisbon, Portugal. The retreat before that was in Malmö in Sweden.&lt;/p&gt;
&lt;p&gt;I considered using an analogy that describes each board member as 1/12th of the "brain" of the PSF, but that doesn't hold up: the paid, full-time staff of the PSF make an enormous number of decisions that impact how the PSF works.&lt;/p&gt;
&lt;p&gt;Instead, the board acts to set strategy, represent the global community and help ensure that the PSF's activities are staying true to that mission. Like I said earlier, the mission definition really is &lt;em&gt;critical&lt;/em&gt;. I admit that in the past I've been a bit cynical about the importance of mission statements: being a board member of a 501(c)(3) non-profit has entirely cured me of that skepticism!&lt;/p&gt;
&lt;p&gt;Board members can also sit on board committees, of which there are currently four: the Executive Committee, Finance Committee, PyCon US Committee and Membership Committee. These mainly exist so that relevant decisions can be delegated to them, helping reduce the topics that must be considered by the full board in our monthly meetings.&lt;/p&gt;
&lt;h4 id="the-kinds-of-things-the-board-talks-about"&gt;The kinds of things the board talks about&lt;/h4&gt;
&lt;p&gt;Our Lisbon retreat involved two full 9-hour days of discussion, plus social breakfasts, lunches and dinners. It was an &lt;em&gt;intense&lt;/em&gt; workload.&lt;/p&gt;
&lt;p&gt;I won't even attempt to do it justice here, but I'll use a couple of topics to illustrate the kind of things we think about on the board.&lt;/p&gt;
&lt;p&gt;The first is our &lt;strong&gt;grants strategy&lt;/strong&gt;. The PSF financially sponsors Python community events around the world. In the past this grants program has suffered from low visibility and, to simplify, we've felt that we weren't giving away enough money.&lt;/p&gt;
&lt;p&gt;Over the past year we've fixed that: board outreach around the grants program and initiatives such as grants office hours have put our grants program in a much healthier position... but potentially &lt;em&gt;too&lt;/em&gt; healthy.&lt;/p&gt;
&lt;p&gt;We took steps to improve that visibily and streamline that process, and they worked! This gives us a new problem: we now have no shortage of applicants, so we need to figure out how to stick within a budget that won't harm the financial sustainability of the PSF itself.&lt;/p&gt;
&lt;p&gt;Does this mean we say no to more events? Should we instead reduce the size of our grants? Can we take other initiatives, like more actively helping events find alternative forms of sponsorship?&lt;/p&gt;
&lt;p&gt;Grants shouldn't just be about events - but if we're making grants to other initiatives that support the Python community how can we fairly select those, manage the budget allocated to supporting them and maximize the value the Python community gets from the money managed by the PSF?&lt;/p&gt;
&lt;p&gt;A much larger topic for the retreat was &lt;strong&gt;strategic planning&lt;/strong&gt;. What should our goals be for the PSF that can't be achieved over a short period of time? Projects and initiatives that might require a one-year, three-year or five-year margin of planning.&lt;/p&gt;
&lt;p&gt;Director terms only last three years (though board members can and frequently do stand for re-election), so having these long-term goals planned and documented in detail is crucial.&lt;/p&gt;
&lt;p&gt;A five-year plan is not something that can be put together over two days of work, but the in-person meeting is a fantastic opportunity to kick things off and ensure each board member gets to participate in shaping that process.&lt;/p&gt;
&lt;h4 id="want-to-know-more-"&gt;Want to know more?&lt;/h4&gt;
&lt;p&gt;The above is by no means a comprehensive manual to the PSF, but it's a good representation of the things I would have found most valuable to understand when I first got involved with the organization.&lt;/p&gt;
&lt;p&gt;For a broader set of perspectives on how the board works and what it does, I recommend the &lt;a href="https://www.youtube.com/watch?v=kD6cPBfR4A4"&gt;FAQs about the PSF Board&lt;/a&gt; video on YouTube.&lt;/p&gt;
&lt;p&gt;If you're interested in helping the PSF achieve its mission, we would love to have you involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Encourage your company to sponsor the PSF directly, or to sponsor Python events worldwide&lt;/li&gt;
&lt;li&gt;Volunteer at PyCon US or help with other suitable PSF initiatives&lt;/li&gt;
&lt;li&gt;Join a Work Group that's addressing problems you want to help solve&lt;/li&gt;
&lt;li&gt;Run your own event and &lt;a href="https://www.python.org/psf/grants/"&gt;apply for a grant&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join the PSF as a voting member and vote in our elections&lt;/li&gt;
&lt;li&gt;Run for the board elections yourself!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We're always interested in hearing from our community. We host public office hours on the PSF Discord every month, at different times of day to to cater for people in different timezones - here's &lt;a href="https://pyfound.blogspot.com/2024/08/ask-questions-or-tell-us-what-you-think.html"&gt;the full calendar of upcoming office hours&lt;/a&gt;.&lt;/p&gt;
    
        &lt;p&gt;Tags: &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/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="pypi"/><category term="python"/><category term="psf"/></entry><entry><title>Python Developers Survey 2023 Results</title><link href="https://simonwillison.net/2024/Sep/3/python-developers-survey-2023/#atom-tag" rel="alternate"/><published>2024-09-03T02:47:45+00:00</published><updated>2024-09-03T02:47:45+00:00</updated><id>https://simonwillison.net/2024/Sep/3/python-developers-survey-2023/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lp.jetbrains.com/python-developers-survey-2023/"&gt;Python Developers Survey 2023 Results&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The seventh annual Python survey is out. Here are the things that caught my eye or that I found surprising:&lt;/p&gt;
&lt;p&gt;25% of survey respondents had been programming in Python for less than a year, and 33% had less than a year of professional experience.&lt;/p&gt;
&lt;p&gt;37% of Python developers reported contributing to open-source projects last year - a new question for the survey. This is delightfully high!&lt;/p&gt;
&lt;p&gt;6% of users are still using Python 2. The survey notes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Almost half of Python 2 holdouts are under 21 years old and a third are students. Perhaps courses are still using Python 2?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In web frameworks, Flask and Django neck and neck at 33% each, but &lt;a href="https://fastapi.tiangolo.com/"&gt;FastAPI&lt;/a&gt; is a close third at 29%! &lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt; is at 6%, but that's an under-count because it's the basis for FastAPI.&lt;/p&gt;
&lt;p&gt;The most popular library in "other framework and libraries" was BeautifulSoup with 31%, then Pillow 28%, then &lt;a href="https://github.com/opencv/opencv-python"&gt;OpenCV-Python&lt;/a&gt; at 22% (wow!) and Pydantic at 22%. Tkinter had 17%. These numbers are all a surprise to me.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.pytest.org/en/stable/"&gt;pytest&lt;/a&gt; scores 52% for unit testing, &lt;code&gt;unittest&lt;/code&gt; from the standard library just 25%. I'm glad to see &lt;code&gt;pytest&lt;/code&gt; so widely used, it's my favourite testing tool across any programming language.&lt;/p&gt;
&lt;p&gt;The top cloud providers are AWS, then Google Cloud Platform, then Azure... but &lt;a href="https://www.pythonanywhere.com/"&gt;PythonAnywhere&lt;/a&gt; (11%) took fourth place just ahead of DigitalOcean (10%). And &lt;a href="https://www.alibabacloud.com/"&gt;Alibaba Cloud&lt;/a&gt; is a new entrant in sixth place (after Heroku) with 4%. Heroku's ending of its free plan dropped them from 14% in 2021 to 7% now.&lt;/p&gt;
&lt;p&gt;Linux and Windows equal at 55%, macOS is at 29%. This was one of many multiple-choice questions that could add up to more than 100%.&lt;/p&gt;
&lt;p&gt;In databases, SQLite usage was trending down - 38% in 2021 to 34% for 2023, but still in second place behind PostgreSQL, stable at 43%.&lt;/p&gt;
&lt;p&gt;The survey incorporates quotes from different Python experts responding to the numbers, it's worth &lt;a href="https://lp.jetbrains.com/python-developers-survey-2023/"&gt;reading through the whole thing&lt;/a&gt;.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://pyfound.blogspot.com/2024/08/python-developers-survey-2023-results.html"&gt;PSF news&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/postgresql"&gt;postgresql&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/surveys"&gt;surveys&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pytest"&gt;pytest&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pydantic"&gt;pydantic&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="postgresql"/><category term="python"/><category term="sqlite"/><category term="surveys"/><category term="pytest"/><category term="psf"/><category term="pydantic"/></entry><entry><title>PSF announces a new five year commitment from Fastly</title><link href="https://simonwillison.net/2024/May/17/pypi-fastly/#atom-tag" rel="alternate"/><published>2024-05-17T13:52:16+00:00</published><updated>2024-05-17T13:52:16+00:00</updated><id>https://simonwillison.net/2024/May/17/pypi-fastly/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://fosstodon.org/@ThePSF/112456715341751673"&gt;PSF announces a new five year commitment from Fastly&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Fastly have been donating CDN resources to Python—most notably to the PyPI package index—for ten years now.&lt;/p&gt;

&lt;p&gt;The PSF just announced at PyCon US that Fastly have agreed to a new five year commitment. This is a really big deal, because it addresses the strategic risk of having a key sponsor like this who might change their support policy based on unexpected future conditions.&lt;/p&gt;

&lt;p&gt;Thanks, Fastly. Very much appreciated!


    &lt;p&gt;Tags: &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/psf"&gt;psf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fastly"&gt;fastly&lt;/a&gt;&lt;/p&gt;



</summary><category term="pypi"/><category term="python"/><category term="psf"/><category term="fastly"/></entry><entry><title>Django Chat: Datasette, LLMs, and Django</title><link href="https://simonwillison.net/2024/Jan/24/django-chat/#atom-tag" rel="alternate"/><published>2024-01-24T20:41:21+00:00</published><updated>2024-01-24T20:41:21+00:00</updated><id>https://simonwillison.net/2024/Jan/24/django-chat/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://djangochat.com/episodes/datasette-llms-and-django-simon-willison"&gt;Django Chat: Datasette, LLMs, and Django&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I’m the guest on the latest episode of the Django Chat podcast. We talked about Datasette, LLMs, the New York Times OpenAI lawsuit, the Python Software Foundation and all sorts of other topics.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/podcasts"&gt;podcasts&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&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/podcast-appearances"&gt;podcast-appearances&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="podcasts"/><category term="ai"/><category term="psf"/><category term="generative-ai"/><category term="llms"/><category term="podcast-appearances"/></entry><entry><title>Weeknotes: datasette-test, datasette-build, PSF board retreat</title><link href="https://simonwillison.net/2024/Jan/21/weeknotes/#atom-tag" rel="alternate"/><published>2024-01-21T11:34:43+00:00</published><updated>2024-01-21T11:34:43+00:00</updated><id>https://simonwillison.net/2024/Jan/21/weeknotes/#atom-tag</id><summary type="html">
    &lt;p&gt;I wrote about &lt;a href="https://simonwillison.net/2024/Jan/7/page-caching-and-custom-templates-for-datasette-cloud/"&gt;Page caching and custom templates&lt;/a&gt; in my last weeknotes. This week I wrapped up that work, modifying &lt;a href="https://github.com/simonw/datasette-edit-templates/releases"&gt;datasette-edit-templates&lt;/a&gt; to be compatible with the &lt;a href="https://docs.datasette.io/en/latest/plugin_hooks.html#jinja2-environment-from-request-datasette-request-env"&gt;jinja2_environment_from_request()&lt;/a&gt; plugin hook. This means you can edit templates directly in Datasette itself and have those served either for the full instance or just for the instance when served from a specific domain (the Datasette Cloud case).&lt;/p&gt;
&lt;h4 id="testing-plugins-with-playwright"&gt;Testing plugins with Playwright&lt;/h4&gt;
&lt;p&gt;As Datasette 1.0 draws closer, I've started thinking about plugin compatibility. This is heavily inspired by my work on Datasette Cloud, which has been running the latest Datasette alphas for several months.&lt;/p&gt;
&lt;p&gt;I spotted that &lt;code&gt;datasette-cluster-map&lt;/code&gt; wasn't working correctly on &lt;a href="https://www.datasette.cloud/"&gt;Datasette Cloud&lt;/a&gt;, as it hadn't been upgraded to account for JSON API changes in Datasette 1.0.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/datasette-cluster-map/releases/tag/0.18"&gt;datasette-cluster-map 0.18&lt;/a&gt; fixed that, while continuing to work with previous versions of Datasette. More importantly, it introduced &lt;a href="https://playwright.dev/python/"&gt;Playwright&lt;/a&gt; tests to exercise the plugin in a real Chromium browser running in GitHub Actions.&lt;/p&gt;
&lt;p&gt;I've been wanting to establish a good pattern for this for a while, since a lot of Datasette plugins include JavaScript behaviour that warrants browser automation testing.&lt;/p&gt;
&lt;p&gt;Alex Garcia figured this out for &lt;a href="https://github.com/datasette/datasette-comments/blob/main/tests/test_ui.py"&gt;datasette-comments&lt;/a&gt; - inspired by his code I wrote up a TIL on &lt;a href="https://til.simonwillison.net/datasette/playwright-tests-datasette-plugin"&gt;Writing Playwright tests for a Datasette Plugin&lt;/a&gt; which I've now also used in &lt;a href="https://github.com/simonw/datasette-search-all/blob/770f95018f106d3b754a526b84d2f877d4725cf9/tests/test_playwright.py"&gt;datasette-search-all&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="datasette-test"&gt;datasette-test&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/datasette/datasette-test"&gt;datasette-test&lt;/a&gt; is a new library that provides testing utilities for Datasette plugins. So far it offers two:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;datasette_test&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-v"&gt;Datasette&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;pytest&lt;/span&gt;

&lt;span class="pl-en"&gt;@&lt;span class="pl-s1"&gt;pytest&lt;/span&gt;.&lt;span class="pl-s1"&gt;mark&lt;/span&gt;.&lt;span class="pl-s1"&gt;asyncio&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;async&lt;/span&gt; &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;test_datasette&lt;/span&gt;():
    &lt;span class="pl-s1"&gt;ds&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Datasette&lt;/span&gt;(&lt;span class="pl-s1"&gt;plugin_config&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;{&lt;span class="pl-s"&gt;"my-plugin"&lt;/span&gt;: {&lt;span class="pl-s"&gt;"config"&lt;/span&gt;: &lt;span class="pl-s"&gt;"goes here"&lt;/span&gt;})&lt;/pre&gt;
&lt;p&gt;This &lt;code&gt;datasette_test.Datasette&lt;/code&gt; class is a subclass of &lt;code&gt;Datasette&lt;/code&gt; which helps write tests that work against both Datasette &amp;lt;1.0 and Datasette &amp;gt;=1.0a8 (releasing shortly). The way plugin configuration works is changing, and this &lt;code&gt;plugin_config=&lt;/code&gt; parameter papers over that difference for plugin tests.&lt;/p&gt;
&lt;p&gt;The other utility is a &lt;code&gt;wait_until_responds("http://localhost:8001")&lt;/code&gt; function. Thes can be used to wait until a server has started, useful for testing with Playwright. I extracted this from Alex's &lt;code&gt;datasette-comments&lt;/code&gt; tests.&lt;/p&gt;
&lt;h4 id="datasette-build"&gt;datasette-build&lt;/h4&gt;
&lt;p&gt;So far this is just the skeleton of a new tool. I plan for &lt;a href="https://github.com/datasette/datasette-build"&gt;datasette-build&lt;/a&gt; to offer comprehensive support for converting a directory full of static data files - JSON, TSV, CSV and more - into a SQLite database, and eventually to other database backends as well.&lt;/p&gt;
&lt;p&gt;So far it's pretty minimal, but my goal is to use plugins to provide optional support for further formats, such as GeoJSON or Parquet or even &lt;code&gt;.xlsx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I really like using GitHub to keep smaller (less than 1GB) datasets under version control. My plan is for &lt;code&gt;datasette-build&lt;/code&gt; to support that pattern, making it easy to load version-controlled data files into a SQLite database you can then query directly.&lt;/p&gt;
&lt;h4 id="psf-in-person"&gt;PSF board in-person meeting&lt;/h4&gt;
&lt;p&gt;I spent the last two days of this week at the annual &lt;a href="https://www.python.org/psf-landing/"&gt;Python Software Foundation&lt;/a&gt; in-person board meeting. It's been fantastic catching up with the other board members over more than just a Zoom connection, and we had a very thorough two days figuring out strategy for the next year and beyond.&lt;/p&gt;
&lt;h4 id="blog-entries-2024-01-21"&gt;Blog entries&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2024/Jan/17/oxide-and-friends/"&gt;Talking about Open Source LLMs on Oxide and Friends&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&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;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2024/Jan/9/what-i-should-have-said-about-ai/"&gt;What I should have said about the term Artificial Intelligence&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="releases-2024-01-21"&gt;Releases&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-edit-templates/releases/tag/0.4.3"&gt;datasette-edit-templates 0.4.3&lt;/a&gt;&lt;/strong&gt; - 2024-01-17&lt;br /&gt;Plugin allowing Datasette templates to be edited within Datasette&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/datasette/datasette-test/releases/tag/0.2"&gt;datasette-test 0.2&lt;/a&gt;&lt;/strong&gt; - 2024-01-16&lt;br /&gt;Utilities to help write tests for Datasette plugins and applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-cluster-map/releases/tag/0.18.1"&gt;datasette-cluster-map 0.18.1&lt;/a&gt;&lt;/strong&gt; - 2024-01-16&lt;br /&gt;Datasette plugin that shows a map for any data with latitude/longitude columns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/datasette/datasette-build/releases/tag/0.1a0"&gt;datasette-build 0.1a0&lt;/a&gt;&lt;/strong&gt; - 2024-01-15&lt;br /&gt;Build a directory full of files into a SQLite database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-auth-tokens/releases/tag/0.4a7"&gt;datasette-auth-tokens 0.4a7&lt;/a&gt;&lt;/strong&gt; - 2024-01-13&lt;br /&gt;Datasette plugin for authenticating access using API tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-search-all/releases/tag/1.1.2"&gt;datasette-search-all 1.1.2&lt;/a&gt;&lt;/strong&gt; - 2024-01-08&lt;br /&gt;Datasette plugin for searching all searchable tables at once&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="tils-2024-01-21"&gt;TILs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/pypi/pypi-releases-from-github"&gt;Publish releases to PyPI from GitHub Actions without a password or token&lt;/a&gt; - 2024-01-15&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/python/pprint-no-sort-dicts"&gt;Using pprint() to print dictionaries while preserving their key order&lt;/a&gt; - 2024-01-15&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/playwright/expect-selector-count"&gt;Using expect() to wait for a selector to match multiple items&lt;/a&gt; - 2024-01-13&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/sphinx/literalinclude-with-markers"&gt;literalinclude with markers for showing code in documentation&lt;/a&gt; - 2024-01-10&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/datasette/playwright-tests-datasette-plugin"&gt;Writing Playwright tests for a Datasette Plugin&lt;/a&gt; - 2024-01-09&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/cloudflare/cloudflare-cache-html"&gt;How to get Cloudflare to cache HTML&lt;/a&gt; - 2024-01-09&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/fly/varnish-on-fly"&gt;Running Varnish on Fly&lt;/a&gt; - 2024-01-08&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-cloud"&gt;datasette-cloud&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/playwright"&gt;playwright&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="datasette"/><category term="weeknotes"/><category term="datasette-cloud"/><category term="playwright"/><category term="psf"/></entry><entry><title>Introducing PyPI Organizations</title><link href="https://simonwillison.net/2023/Apr/23/introducing-pypi-organizations/#atom-tag" rel="alternate"/><published>2023-04-23T20:29:39+00:00</published><updated>2023-04-23T20:29:39+00:00</updated><id>https://simonwillison.net/2023/Apr/23/introducing-pypi-organizations/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.pypi.org/posts/2023-04-23-introducing-pypi-organizations/"&gt;Introducing PyPI Organizations&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Launched at PyCon US today: Organizations allow packages on the Python Package Index to be owned by a group, not an individual user account. “We’re making organizations available to community projects for free, forever, and to corporate projects for a small fee.”—this is the first revenue generating PyPI feature.


    &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/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/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="packaging"/><category term="pypi"/><category term="python"/><category term="psf"/></entry><entry><title>Weeknotes: Joining the board of the Python Software Foundation</title><link href="https://simonwillison.net/2022/Jul/30/psf-board/#atom-tag" rel="alternate"/><published>2022-07-30T19:02:04+00:00</published><updated>2022-07-30T19:02:04+00:00</updated><id>https://simonwillison.net/2022/Jul/30/psf-board/#atom-tag</id><summary type="html">
    &lt;p&gt;A few weeks ago I was &lt;a href="https://pyfound.blogspot.com/2022/07/board-election-results-for-2022.html"&gt;elected&lt;/a&gt; to the board of directors for the &lt;a href="https://www.python.org/psf/"&gt;Python Software Foundation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I put myself up for election partly because I've found myself saying "I wish the PSF would help with ..." a few times over the years, and realized that joining the board could be a more useful way to actively participate, rather than shouting from the sidelines.&lt;/p&gt;
&lt;p&gt;I was quite surprised to win. I wrote up a short  manifesto - you can &lt;a href="https://www.python.org/nominations/elections/2022-python-software-foundation-board/nominees/simon-willison/"&gt;see that here&lt;/a&gt; - but the voting system lets voters select as many candidates as they like, so it's possible I got in more on broad name recognition among the voters than based on what I wrote. I don't think there's a way to tell one way or the other.&lt;/p&gt;
&lt;p&gt;I had my first board meeting on Wednesday, where I formally joined the board and got to vote on my first resolutions. This is my first time as a board member for a non-profit and I have learned a bunch already, with a lot more to go!&lt;/p&gt;
&lt;p&gt;Board terms last three years. I expect it will take me at least a few months to get fully up to speed on how everything works.&lt;/p&gt;
&lt;p&gt;As a board member, my &lt;a href="https://discuss.python.org/t/interested-in-running-for-the-psf-board-read-about-board-responsibilities-here/8564"&gt;primary responsibilities&lt;/a&gt; are to show up to the meetings, vote on resolutions, act as an ambassador for the PSF to the Python community and beyond and help both set the direction for the PSF and ensure that the PSF meets its goals and holds true to its values.&lt;/p&gt;
&lt;p&gt;I'm embarassed to admit that I wrote my election manifesto without a deep understanding of how the PSF operates and how much is possible for it to get done. Here's the section I wrote about my goals should I be elected:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I believe there are problems facing the Python community that require dedicated resources beyond volunteer labour. I'd like the PSF to invest funding in the following areas in particular:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improve Python onboarding.&lt;/strong&gt; In coaching new developers I've found that the initial steps to getting started with a Python development environment can be a difficult hurdle to cross. I'd like to help direct PSF resources to tackling this problem, with a goal of making the experience of starting to learn Python as smooth as possible, no matter what platform the learner is using.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make Python a great platform for distributing software.&lt;/strong&gt; In building my own application, Datasette, in Python I've seen how difficult it can be to package up a Python application so that it can be installed by end-users, who aren't ready to install Python and learn pip in order to try out a new piece of software. I've researched solutions for this for my own software using Homebrew, Docker, an Electron app and WASM/Pyodide. I'd like the PSF to invest in initiatives and documentation to make this as easy as possible, so that one of the reasons to build with Python is that distributing an application to end-users is already a solved problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I still think these are good ideas, and I hope to make progress on them during my term as a director - but I'm not going to start arguing for new initiatives until I've learned the ropes and fully understood the PSF's abilities, current challenges and existing goals.&lt;/p&gt;
&lt;p&gt;In figuring out how the board works, one of the most useful pages I stumbled across was &lt;a href="https://www.python.org/psf/records/board/resolutions/"&gt;this list of resolutions&lt;/a&gt; voted on by the board, dating back to 2001. There are over 1,600 of them! Browsing through them gave me a much better idea of the kind of things the board has the authority to do.&lt;/p&gt;
&lt;h4 id="scraping-datasette-lite"&gt;Scraping data into Datasette Lite&lt;/h4&gt;
&lt;p&gt;Because everything looks like a nail when you have a good hammer, I explored the board resolutions by loading them into &lt;a href="https://datasette.io/"&gt;Datasette&lt;/a&gt;. I tried a new trick this time: I scraped data from that page into a CSV file, then loaded up that CSV file in &lt;a href="https://simonwillison.net/2022/May/4/datasette-lite/"&gt;Datasette Lite&lt;/a&gt; via a GitHub Gist.&lt;/p&gt;
&lt;p&gt;My scraper isn't perfect - it misses about 150 resolutions because they don't exactly fit the format it expects, but it was good enough for a proof of concept. I wrote that in a Jupyter Notebook which you can &lt;a href="https://gist.github.com/simonw/027408c5df8a579053957ceaaef3a144"&gt;see here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://gist.github.com/simonw/71b03ca3688c8f37fe1b35859ebc458b"&gt;the CSV in a Gist&lt;/a&gt;. The great thing about Gists is that GitHub serve those files with the &lt;code&gt;access-control-allow-origin: *&lt;/code&gt; HTTP header, which means you can load them cross-domain.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://lite.datasette.io/?csv=https%3A%2F%2Fgist.githubusercontent.com%2Fsimonw%2F71b03ca3688c8f37fe1b35859ebc458b%2Fraw%2Fd3ca708e5d6242848201d1d4c1c2ddbba15c4d28%2Fpsf-resolutions.csv#/data/psf-resolutions"&gt;Here's what you get&lt;/a&gt; if you paste the URL to that CSV into Datasette Lite (using &lt;a href="https://simonwillison.net/2022/Jun/20/datasette-lite-csvs/"&gt;this new feature&lt;/a&gt; I added last month):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/psf-resolutions.jpg" alt="A screenshot of the psf-resolutions table in Datasette, showing 1,654 rows" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;And here's &lt;a href="https://lite.datasette.io/?csv=https%3A%2F%2Fgist.githubusercontent.com%2Fsimonw%2F71b03ca3688c8f37fe1b35859ebc458b%2Fraw%2Fd3ca708e5d6242848201d1d4c1c2ddbba15c4d28%2Fpsf-resolutions.csv#/data?sql=with+filtered+as+%28%0A++select+*+from%0A++++%5Bpsf-resolutions%5D%0A++where%0A++++%22dollars%22+is+not+null%0A++++and+%22text%22+like+%27%25%27+%7C%7C+%3Asearch+%7C%7C+%27%25%27%0A%29%0Aselect%0A++%27Total%3A+%24%27+%7C%7C+printf%28%27%25%2Cd%27%2C+sum%28dollars%29%29+as+text%2C%0A++null+as+date%0Afrom+filtered%0Aunion+all%0Aselect%0A++text%2C+date%0Afrom+filtered%3B&amp;amp;search=Nigeria"&gt;a SQL query&lt;/a&gt; that shows the sum total dollar amount from every resolution that mentions "Nigeria":&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;with filtered &lt;span class="pl-k"&gt;as&lt;/span&gt; (
  &lt;span class="pl-k"&gt;select&lt;/span&gt; &lt;span class="pl-k"&gt;*&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt;
    [psf&lt;span class="pl-k"&gt;-&lt;/span&gt;resolutions]
  &lt;span class="pl-k"&gt;where&lt;/span&gt;
    &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;dollars&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;is not null&lt;/span&gt;
    &lt;span class="pl-k"&gt;and&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;text&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;like&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;%&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;||&lt;/span&gt; :search &lt;span class="pl-k"&gt;||&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;%&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
)
&lt;span class="pl-k"&gt;select&lt;/span&gt;
  &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Total: $&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;||&lt;/span&gt; printf(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;%,d&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-c1"&gt;sum&lt;/span&gt;(dollars)) &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-k"&gt;text&lt;/span&gt;,
  &lt;span class="pl-k"&gt;null&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-k"&gt;date&lt;/span&gt;
&lt;span class="pl-k"&gt;from&lt;/span&gt; filtered
&lt;span class="pl-k"&gt;union all&lt;/span&gt;
&lt;span class="pl-k"&gt;select&lt;/span&gt;
  &lt;span class="pl-k"&gt;text&lt;/span&gt;, &lt;span class="pl-k"&gt;date&lt;/span&gt;
&lt;span class="pl-k"&gt;from&lt;/span&gt; filtered;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/psf-resolutions-nigeria.jpg" alt="A screenshot of the results of that query, returning 132 rows the top of which says Total: $163,849" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;I'm using a new-to-me trick here: I use a CTE to filter down to just the rows I am interested in, then I create a first row that sums the dollar amounts as the &lt;code&gt;text&lt;/code&gt; column and leaves the &lt;code&gt;date&lt;/code&gt; column &lt;code&gt;null&lt;/code&gt;, then unions that against the rows from the query.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; These numbers aren't actually particularly useful. Just because the PSF board voted on a resolution does not mean that the money made it to the grantee - there are apparently situations where the approved grant may not be properly claimed and transferred. Also, my scraper logic isn't perfect. Plus the PSF spends a whole lot of money in ways that don't show up in these resolutions.&lt;/p&gt;
&lt;p&gt;So this is a fun hack, and a neat way to start getting a general idea of how the PSF works, but any numbers it produces should not be taken as the absolute truth.&lt;/p&gt;
&lt;p&gt;As a general pattern though, I really like this workflow of generating CSV files, saving them to a Gist and then opening them directly in Datasette Lite. It provides a way to use Datasette to share and explore data without needing to involve any server-side systems (other than GitHub Gists) at all!&lt;/p&gt;
&lt;h4&gt;Big-endian bugs in sqlite-fts4&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/sqlite-fts4"&gt;sqlite-fts4&lt;/a&gt; is a small Python library I wrote that adds SQLite functions for calculating relevance scoring for full-text search using the FTS4 module that comes bundled with SQLite. I described that project in detail in &lt;a href="https://simonwillison.net/2019/Jan/7/exploring-search-relevance-algorithms-sqlite/"&gt;Exploring search relevance algorithms with SQLite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It's a dependency of &lt;a href="https://sqlite-utils.datasette.io/"&gt;sqlite-utils&lt;/a&gt; so it has a pretty big install base, despite being relatively obscure.&lt;/p&gt;
&lt;p&gt;This week I had a fascinating bug report from Sarah Julia Kriesch: &lt;a href="https://github.com/simonw/sqlite-fts4/issues/6"&gt;Test test_underlying_decode_matchinfo fails on PPC64 and s390x on openSUSE&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;s390x&lt;/code&gt; is an &lt;a href="https://en.wikipedia.org/wiki/Linux_on_IBM_Z"&gt;IBM mainframe architecture&lt;/a&gt; and it uses a big-endian byte order, unlike all of the machines I use which are little-endian.&lt;/p&gt;
&lt;p&gt;This is the first time I've encountered a big-endian v.s. little-endian bug in my entire career! I was excited to dig in.&lt;/p&gt;
&lt;p&gt;Here's the relevant code:&lt;/p&gt;
&lt;pre&gt; &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;decode_matchinfo&lt;/span&gt;(&lt;span class="pl-s1"&gt;buf&lt;/span&gt;): 
     &lt;span class="pl-c"&gt;# buf is a bytestring of unsigned integers, each 4 bytes long &lt;/span&gt;
     &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;struct&lt;/span&gt;.&lt;span class="pl-en"&gt;unpack&lt;/span&gt;(&lt;span class="pl-s"&gt;"I"&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; (&lt;span class="pl-en"&gt;len&lt;/span&gt;(&lt;span class="pl-s1"&gt;buf&lt;/span&gt;) &lt;span class="pl-c1"&gt;//&lt;/span&gt; &lt;span class="pl-c1"&gt;4&lt;/span&gt;), &lt;span class="pl-s1"&gt;buf&lt;/span&gt;) &lt;/pre&gt;
&lt;p&gt;SQLite FTS4 provides a &lt;code&gt;matchinfo&lt;/code&gt; binary string which you need to decode in order to calculate the relevance score. This code uses the &lt;a href="https://docs.python.org/3/library/struct.html"&gt;struct&lt;/a&gt; standard library module to unpack that binary string into a list of integers.&lt;/p&gt;
&lt;p&gt;My initial attempt at fixing this turned out to be entirely incorrect.&lt;/p&gt;
&lt;p&gt;I didn't have a big-endian machine available for testing, and I assumed that the problem was caused by Python interpreting the bytes as the current architecture's byte order. So I applied this fix:&lt;/p&gt;
&lt;pre&gt;    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;struct&lt;/span&gt;.&lt;span class="pl-en"&gt;unpack&lt;/span&gt;(&lt;span class="pl-s"&gt;"&amp;gt;"&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; (&lt;span class="pl-s"&gt;"I"&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; (&lt;span class="pl-en"&gt;len&lt;/span&gt;(&lt;span class="pl-s1"&gt;buf&lt;/span&gt;) &lt;span class="pl-c1"&gt;//&lt;/span&gt; &lt;span class="pl-c1"&gt;4&lt;/span&gt;)), &lt;span class="pl-s1"&gt;buf&lt;/span&gt;)&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;&amp;gt;&lt;/code&gt; prefix there ensures that &lt;code&gt;struct&lt;/code&gt; will always interpret the bytes as little-endian. I wrote up &lt;a href="https://til.simonwillison.net/python/struct-endianness"&gt;a TIL&lt;/a&gt; and &lt;a href="https://github.com/simonw/sqlite-fts4/releases/1.0.2"&gt;shipped 1.0.2&lt;/a&gt; with the fix.&lt;/p&gt;
&lt;p&gt;Sarah promptly got back to me and reported some &lt;a href="https://github.com/simonw/sqlite-fts4/issues/6#issuecomment-1198566258"&gt;new failing tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It turns out my fix was entirely incorrect - in fact, I'd broken something that previously was working just fine.&lt;/p&gt;
&lt;p&gt;The clue is in the SQLite &lt;a href="https://www.sqlite.org/fts3.html#matchinfo"&gt;documentation for matchinfo&lt;/a&gt; (which I really should have checked):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;matchinfo&lt;/code&gt; function returns a blob value. If it is used within a query that does not use the full-text index (a "query by rowid" or "linear scan"), then the blob is zero bytes in size. Otherwise, the blob consists of zero or more 32-bit unsigned integers &lt;strong&gt;in machine byte-order&lt;/strong&gt; (emphasis mine).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Looking more closely at the original bug report, the test that failed was this one:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-en"&gt;@&lt;span class="pl-s1"&gt;pytest&lt;/span&gt;.&lt;span class="pl-s1"&gt;mark&lt;/span&gt;.&lt;span class="pl-en"&gt;parametrize&lt;/span&gt;(&lt;/span&gt;
&lt;span class="pl-en"&gt;    &lt;span class="pl-s"&gt;"buf,expected"&lt;/span&gt;,&lt;/span&gt;
&lt;span class="pl-en"&gt;    [&lt;/span&gt;
&lt;span class="pl-en"&gt;        (&lt;/span&gt;
&lt;span class="pl-en"&gt;            &lt;span class="pl-s"&gt;b"&lt;span class="pl-cce"&gt;\x01&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;"&lt;/span&gt;,&lt;/span&gt;
&lt;span class="pl-en"&gt;            (&lt;span class="pl-c1"&gt;1&lt;/span&gt;, &lt;span class="pl-c1"&gt;2&lt;/span&gt;, &lt;span class="pl-c1"&gt;2&lt;/span&gt;, &lt;span class="pl-c1"&gt;2&lt;/span&gt;),&lt;/span&gt;
&lt;span class="pl-en"&gt;        )&lt;/span&gt;
&lt;span class="pl-en"&gt;    ],&lt;/span&gt;
&lt;span class="pl-en"&gt;)&lt;/span&gt;
&lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;test_underlying_decode_matchinfo&lt;/span&gt;(&lt;span class="pl-s1"&gt;buf&lt;/span&gt;, &lt;span class="pl-s1"&gt;expected&lt;/span&gt;):
    &lt;span class="pl-k"&gt;assert&lt;/span&gt; &lt;span class="pl-s1"&gt;expected&lt;/span&gt; &lt;span class="pl-c1"&gt;==&lt;/span&gt; &lt;span class="pl-en"&gt;decode_matchinfo&lt;/span&gt;(&lt;span class="pl-s1"&gt;buf&lt;/span&gt;)&lt;/pre&gt;
&lt;p&gt;That test hard-codes a little-endian binary string and checks the output of my &lt;code&gt;decode_matchinfo&lt;/code&gt; function. This is obviously going to fail on a big-endian system.&lt;/p&gt;
&lt;p&gt;So my original behaviour was actually correct: I was parsing the string using the byte order of the architecture, and SQLite was providing the string in the byte order of the architecture. The only bug was in my test.&lt;/p&gt;
&lt;p&gt;I reverted my previous fix and fixed the test instead:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-en"&gt;@&lt;span class="pl-s1"&gt;pytest&lt;/span&gt;.&lt;span class="pl-s1"&gt;mark&lt;/span&gt;.&lt;span class="pl-en"&gt;parametrize&lt;/span&gt;(&lt;/span&gt;
&lt;span class="pl-en"&gt;    &lt;span class="pl-s"&gt;"buf,expected"&lt;/span&gt;,&lt;/span&gt;
&lt;span class="pl-en"&gt;    [&lt;/span&gt;
&lt;span class="pl-en"&gt;        (&lt;/span&gt;
&lt;span class="pl-en"&gt;            &lt;span class="pl-s"&gt;b"&lt;span class="pl-cce"&gt;\x01&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-en"&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-s1"&gt;byteorder&lt;/span&gt; &lt;span class="pl-c1"&gt;==&lt;/span&gt; &lt;span class="pl-s"&gt;"little"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-en"&gt;            &lt;span class="pl-k"&gt;else&lt;/span&gt; &lt;span class="pl-s"&gt;b"&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x01&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x00&lt;/span&gt;&lt;span class="pl-cce"&gt;\x02&lt;/span&gt;"&lt;/span&gt;,&lt;/span&gt;
&lt;span class="pl-en"&gt;            (&lt;span class="pl-c1"&gt;1&lt;/span&gt;, &lt;span class="pl-c1"&gt;2&lt;/span&gt;, &lt;span class="pl-c1"&gt;2&lt;/span&gt;, &lt;span class="pl-c1"&gt;2&lt;/span&gt;),&lt;/span&gt;
&lt;span class="pl-en"&gt;        )&lt;/span&gt;
&lt;span class="pl-en"&gt;    ],&lt;/span&gt;
&lt;span class="pl-en"&gt;)&lt;/span&gt;
&lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;test_underlying_decode_matchinfo&lt;/span&gt;(&lt;span class="pl-s1"&gt;buf&lt;/span&gt;, &lt;span class="pl-s1"&gt;expected&lt;/span&gt;):
    &lt;span class="pl-k"&gt;assert&lt;/span&gt; &lt;span class="pl-s1"&gt;expected&lt;/span&gt; &lt;span class="pl-c1"&gt;==&lt;/span&gt; &lt;span class="pl-en"&gt;decode_matchinfo&lt;/span&gt;(&lt;span class="pl-s1"&gt;buf&lt;/span&gt;)&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;sys.byteorder&lt;/code&gt; reports the byte order of the host system, so this test now passes on both little-endian and big-endian systems.&lt;/p&gt;
&lt;p&gt;There was one remaining challenge: how to test this? I wasn't going to make the same mistake of shipping a fix that hadn't actually been exercised on the target architecture a second time.&lt;/p&gt;
&lt;p&gt;After quite a bit of research (mainly throwing the terms &lt;code&gt;docker&lt;/code&gt; and &lt;code&gt;s390x&lt;/code&gt; into the GitHub code search engine and seeing what I could find) I figured out a fix. It turns out you can use Docker and QEMU to run an emulated &lt;code&gt;s390x&lt;/code&gt; system - both on a Mac laptop and in GitHub Actions.&lt;/p&gt;
&lt;p&gt;Short version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run --rm --privileged multiarch/qemu-user-static:register --reset
docker run -it multiarch/ubuntu-core:s390x-focal /bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the longer version, check my TIL: &lt;a href="https://til.simonwillison.net/docker/emulate-s390x-with-qemu"&gt;Emulating a big-endian s390x with QEMU&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/sqlite-fts4"&gt;sqlite-fts4&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/sqlite-fts4/releases/tag/1.0.3"&gt;1.0.3&lt;/a&gt; - (&lt;a href="https://github.com/simonw/sqlite-fts4/releases"&gt;5 releases total&lt;/a&gt;) - 2022-07-30
&lt;br /&gt;Custom Python functions for working with SQLite FTS4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/shot-scraper"&gt;shot-scraper&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/shot-scraper/releases/tag/0.14.2"&gt;0.14.2&lt;/a&gt; - (&lt;a href="https://github.com/simonw/shot-scraper/releases"&gt;17 releases total&lt;/a&gt;) - 2022-07-28
&lt;br /&gt;A comand-line utility for taking automated screenshots of websites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-sqlite-fts4"&gt;datasette-sqlite-fts4&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-sqlite-fts4/releases/tag/0.3.1"&gt;0.3.1&lt;/a&gt; - 2022-07-28
&lt;br /&gt;Datasette plugin that adds custom SQL functions for working with SQLite FTS4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-publish-vercel"&gt;datasette-publish-vercel&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-publish-vercel/releases/tag/0.14.1"&gt;0.14.1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-publish-vercel/releases"&gt;22 releases total&lt;/a&gt;) - 2022-07-23
&lt;br /&gt;Datasette plugin for publishing data using Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-insert"&gt;datasette-insert&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-insert/releases/tag/0.8"&gt;0.8&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-insert/releases"&gt;8 releases total&lt;/a&gt;) - 2022-07-22&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/pytest/playwright-pytest"&gt;Using pytest and Playwright to test a JavaScript web application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/fly/redbean-on-fly"&gt;Deploying a redbean app to Fly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/docker/test-fedora-in-docker"&gt;Testing things in Fedora using Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/python/struct-endianness"&gt;struct endianness in Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/github/migrate-github-wiki"&gt;Migrating a GitHub wiki from one repository to another&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/docker/emulate-s390x-with-qemu"&gt;Emulating a big-endian s390x with QEMU&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &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/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-lite"&gt;datasette-lite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/psf"&gt;psf&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="python"/><category term="sqlite"/><category term="datasette"/><category term="weeknotes"/><category term="sqlite-utils"/><category term="datasette-lite"/><category term="psf"/></entry><entry><title>Donate to the PSF!</title><link href="https://simonwillison.net/2003/Dec/20/psf/#atom-tag" rel="alternate"/><published>2003-12-20T22:02:07+00:00</published><updated>2003-12-20T22:02:07+00:00</updated><id>https://simonwillison.net/2003/Dec/20/psf/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.python.org/psf/donations/"&gt;Donate to the PSF!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Support Python as well.


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



</summary><category term="python"/><category term="psf"/></entry></feed>