<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Python Developer Tooling Handbook – Python Tooling Blog</title>
    <link>https://pydevtools.com/blog/</link>
    <description>Articles on Python developer tools including uv, ruff, ty, pytest, and the evolving packaging ecosystem.</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Tue, 06 Feb 2024 16:32:39 +0000</lastBuildDate>
    
	  <atom:link href="https://pydevtools.com/blog/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Claude Mythos 5 Published Malware to PyPI. Fifteen Systems Ran It.</title>
      <link>https://pydevtools.com/blog/claude-mythos-5-published-malware-to-pypi/</link>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/claude-mythos-5-published-malware-to-pypi/</guid>
      <description>&lt;p&gt;Claude Mythos 5 registered a PyPI account and published three malicious package versions. One of the 15 systems that installed them leaked credentials, which the model used to reach further infrastructure.&lt;/p&gt;
&lt;p&gt;PyPI removed the package approximately 90 minutes after publication. The affected system belonged to a security company whose scanner executed the package with usable credentials.&lt;/p&gt;
&lt;p&gt;Anthropic first disclosed the incident in July. Its &lt;a href=&#34;https://www.anthropic.com/research/alignment-assessment-cybersecurity-incidents&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;September alignment assessment&lt;/a&gt; changes the interpretation: the company now says the model&amp;rsquo;s repeated claim that it was inside a simulation reflected biased reasoning, even after evidence pointed to the real internet. The Python tooling lesson does not depend on what the model believed. A new package reached automated systems faster than reputation or human review could catch up.&lt;/p&gt;</description>
    </item>
    <item>
      <title>I&#39;m Running for the Python Packaging Council</title>
      <link>https://pydevtools.com/blog/running-for-python-packaging-council/</link>
      <pubDate>Thu, 20 Aug 2026 12:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/running-for-python-packaging-council/</guid>
      <description>&lt;p&gt;Python&amp;rsquo;s packaging ecosystem is getting its first elected governance body: the &lt;a href=&#34;https://blog.python.org/2026/08/2026-packaging-council-nominees/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Python Packaging Council&lt;/a&gt;. Five seats, seventeen nominees, and I&amp;rsquo;m one of them.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-772/&#34;&gt;PEP 772&lt;/a&gt; creates the council to fix a structural problem. The &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pypa/&#34;&gt;Python Packaging Authority (PyPA)&lt;/a&gt; has long coordinated packaging tools and standards, but its governance was informal: no elections, no regular meetings, no direct accountability to the community. The Packaging Council formalizes that authority. It will make binding decisions on packaging standards, tools, and implementations under a mandate from the Python Steering Council. One of its first jobs is defining how it works with PyPA.&lt;/p&gt;</description>
    </item>
    <item>
      <title>uv 0.12 Makes Every New Project a Package</title>
      <link>https://pydevtools.com/blog/uv-0-12-packaged-by-default/</link>
      <pubDate>Wed, 29 Jul 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/uv-0-12-packaged-by-default/</guid>
      <description>&lt;p&gt;Run &lt;code&gt;uv init&lt;/code&gt; on 0.12 and the &lt;code&gt;main.py&lt;/code&gt; you expected is not there.&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; uv init example
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; example
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; uv run example
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Hello from example!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container  hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copy code&#34;
    aria-label=&#34;Copy code&#34;
    data-copied-label=&#34;Copied!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Source code now lives in &lt;code&gt;src/example/__init__.py&lt;/code&gt;, &lt;code&gt;pyproject.toml&lt;/code&gt; carries a &lt;code&gt;[build-system]&lt;/code&gt; using &lt;code&gt;uv_build&lt;/code&gt;, and a &lt;code&gt;[project.scripts]&lt;/code&gt; entry turns &lt;code&gt;example&lt;/code&gt; into a runnable command. That is the headline breaking change in &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; 0.12.0, released July 28, 2026.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Ruff 0.16.0 Enables 7x More Rules by Default</title>
      <link>https://pydevtools.com/blog/ruff-0-16-0-default-rules/</link>
      <pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/ruff-0-16-0-default-rules/</guid>
      <description>&lt;p&gt;Upgrade &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;Ruff&lt;/a&gt; to 0.16.0 and your next &lt;code&gt;ruff check&lt;/code&gt; may flag hundreds of new violations. Your code didn&amp;rsquo;t change; Ruff enabled 354 additional rules by default.&lt;/p&gt;
&lt;p&gt;Since v0.1.0 in October 2023, the out-of-the-box selection stayed &lt;code&gt;[&amp;quot;E4&amp;quot;, &amp;quot;E7&amp;quot;, &amp;quot;E9&amp;quot;, &amp;quot;F&amp;quot;]&lt;/code&gt;: pyflakes detections and a narrow slice of pycodestyle. Those conservative defaults are why the handbook&amp;rsquo;s &lt;a href=&#34;https://pydevtools.com/handbook/how-to/how-to-configure-recommended-ruff-defaults/&#34;&gt;guide to recommended Ruff defaults&lt;/a&gt; covers enabling bugbear, simplify, pyupgrade, and the rest via &lt;code&gt;extend-select&lt;/code&gt;. With 0.16.0, many of those rules are on by default.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PyPI Now Rejects New Files on Releases Older Than 14 Days</title>
      <link>https://pydevtools.com/blog/pypi-rejects-new-files-after-14-days/</link>
      <pubDate>Thu, 23 Jul 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/pypi-rejects-new-files-after-14-days/</guid>
      <description>&lt;p&gt;Pinning &lt;code&gt;requests==2.32.0&lt;/code&gt; in your &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-a-lock-file/&#34;&gt;lockfile&lt;/a&gt; was supposed to keep you safe. If an attacker uploaded &lt;code&gt;requests==2.32.1&lt;/code&gt; with a backdoor, your locked environment wouldn&amp;rsquo;t touch it. But there was another path: a compromised maintainer token could upload a new wheel to the &lt;code&gt;2.32.0&lt;/code&gt; release itself (a Python 3.14-compatible build, say) without creating a new version number. Any user without a hash-verified lockfile, or who regenerated their lockfile after the upload, would pull the poisoned wheel under the same version number they already trusted.&lt;/p&gt;</description>
    </item>
    <item>
      <title>uv&#39;s Malware Check: What It Blocks and What It Misses</title>
      <link>https://pydevtools.com/blog/uv-malware-check-what-it-blocks-and-what-it-misses/</link>
      <pubDate>Wed, 22 Jul 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/uv-malware-check-what-it-blocks-and-what-it-misses/</guid>
      <description>&lt;p&gt;When &lt;a href=&#34;https://pydevtools.com/blog/litellm-supply-chain-attack-and-securing-python-dependencies/&#34;&gt;litellm was compromised&lt;/a&gt; in March, the malicious versions sat on PyPI for hours. When &lt;a href=&#34;https://pydevtools.com/blog/lightning-pypi-compromise-import-time-supply-chain-attack/&#34;&gt;lightning was hit&lt;/a&gt; in April, Socket flagged the bad releases 18 minutes after upload. Any &lt;code&gt;uv sync&lt;/code&gt; inside those windows installed the malware. &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; has a defense for the tail end of these incidents: an install-time check that refuses to install any package version with a published malware advisory. As of &lt;a href=&#34;https://github.com/astral-sh/uv/releases/tag/0.11.31&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;uv 0.11.31&lt;/a&gt;, you can turn it on in &lt;code&gt;pyproject.toml&lt;/code&gt; instead of hoping every developer remembers an environment variable.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ty now checks Pydantic like Pydantic does</title>
      <link>https://pydevtools.com/blog/ty-ships-first-class-pydantic-support/</link>
      <pubDate>Tue, 21 Jul 2026 14:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/ty-ships-first-class-pydantic-support/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt; shipped first-class Pydantic support across its July 2026 releases (0.0.57 through 0.0.61), and Pydantic author Sebastián Ramírez signed off on the work publicly. Before this, ty handled Pydantic models through the generic &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-681/&#34;&gt;PEP 681 &lt;code&gt;dataclass_transform&lt;/code&gt; marker&lt;/a&gt;, treating the field annotation as the source of truth. Now it models Pydantic&amp;rsquo;s actual runtime.&lt;/p&gt;
&lt;h2&gt;What ty checks now&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;what-ty-checks-now&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#what-ty-checks-now&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ty distinguishes Pydantic&amp;rsquo;s lax and strict validation modes and follows Pydantic&amp;rsquo;s coercion rules. It reads &lt;code&gt;model_config = ConfigDict(...)&lt;/code&gt; off the class, recognizing &lt;code&gt;strict&lt;/code&gt;, &lt;code&gt;frozen&lt;/code&gt;, &lt;code&gt;extra&lt;/code&gt;, &lt;code&gt;validate_by_name&lt;/code&gt;, and &lt;code&gt;validate_by_alias&lt;/code&gt;. It reads per-field metadata from &lt;code&gt;Field&lt;/code&gt; and &lt;code&gt;Annotated&lt;/code&gt;, including aliases, default factories, and a per-field &lt;code&gt;Strict()&lt;/code&gt; marker.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Python 3.15 makes UTF-8 the default encoding</title>
      <link>https://pydevtools.com/blog/python-315-utf8-default/</link>
      <pubDate>Sun, 19 Jul 2026 20:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/python-315-utf8-default/</guid>
      <description>&lt;p&gt;Python 3.15, due as a release candidate on August 4, 2026, ships &lt;a href=&#34;https://peps.python.org/pep-0686/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PEP 686&lt;/a&gt; enabled by default. Every text-mode &lt;code&gt;open()&lt;/code&gt; call and every &lt;code&gt;subprocess.run(text=True)&lt;/code&gt; without an explicit &lt;code&gt;encoding=&lt;/code&gt; argument will use UTF-8 instead of the system locale encoding. On Windows, redirected &lt;code&gt;sys.stdin&lt;/code&gt;, &lt;code&gt;sys.stdout&lt;/code&gt;, and &lt;code&gt;sys.stderr&lt;/code&gt; are also affected; streams attached to a console already used UTF-8 before this change.&lt;/p&gt;
&lt;p&gt;On macOS and Linux, UTF-8 locales are already standard, so almost nothing changes in practice. On Windows, where the system code page is typically cp1252, cp932, or a regional variant, any code that omits &lt;code&gt;encoding=&lt;/code&gt; will behave differently after the upgrade.&lt;/p&gt;</description>
    </item>
    <item>
      <title>uv Wants to Secure Your CLI Tools</title>
      <link>https://pydevtools.com/blog/uv-wants-to-secure-your-cli-tools/</link>
      <pubDate>Tue, 07 Jul 2026 07:35:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/uv-wants-to-secure-your-cli-tools/</guid>
      <description>&lt;p&gt;Tools like Ruff, pre-commit, and httpie sit on a developer&amp;rsquo;s machine for months. Your project dependencies get lockfiles and vulnerability scans, but these globally installed CLI tools have no recorded resolution at all. &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; is working to change that. A &lt;a href=&#34;https://github.com/astral-sh/uv/releases/tag/0.11.25&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;preview feature&lt;/a&gt; in 0.11.25 starts by writing a &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-a-lock-file/&#34;&gt;&lt;code&gt;uv.lock&lt;/code&gt;&lt;/a&gt; for each tool you install, laying the groundwork for a future &lt;code&gt;uv tool audit&lt;/code&gt; command.&lt;/p&gt;
&lt;div class=&#34;hx:overflow-x-auto hx:mt-6 hx:flex hx:flex-col hx:rounded-lg hx:border hx:py-4 hx:px-4 hx:border-gray-200 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200&#34;&gt;
  &lt;p class=&#34;hx:flex hx:items-center hx:font-medium&#34;&gt;&lt;svg height=16px class=&#34;hx:inline-block hx:align-middle hx:mr-2&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; fill=&#34;none&#34; viewBox=&#34;0 0 24 24&#34; stroke-width=&#34;2&#34; stroke=&#34;currentColor&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; d=&#34;M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z&#34;/&gt;&lt;/svg&gt;Note&lt;/p&gt;</description>
    </item>
    <item>
      <title>uv Solved the Packaging UX, Not Packaging</title>
      <link>https://pydevtools.com/blog/uv-solved-packaging-ux-hard-problems-remain/</link>
      <pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/uv-solved-packaging-ux-hard-problems-remain/</guid>
      <description>&lt;p&gt;Someone asked me what the new &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-772/&#34;&gt;Python Packaging Council&lt;/a&gt; could possibly work on &amp;ldquo;now that uv has largely standardized packaging.&amp;rdquo; &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; has made installing packages, managing environments, and resolving dependencies fast enough that packaging feels solved. That assumption mistakes one layer of the stack for the whole thing.&lt;/p&gt;
&lt;p&gt;uv addressed the developer workflow: fast installs, fast resolution, a single CLI that replaces &lt;a href=&#34;https://pydevtools.com/handbook/reference/pip/&#34;&gt;pip&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/pip-tools/&#34;&gt;pip-tools&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/pyenv/&#34;&gt;pyenv&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/pipx/&#34;&gt;pipx&lt;/a&gt;, and &lt;a href=&#34;https://pydevtools.com/handbook/reference/virtualenv/&#34;&gt;virtualenv&lt;/a&gt;. That layer matters, and uv did it well. But the hardest unsolved problems sit in layers uv consumes rather than controls.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Pixi Can Now Build Packages From Source</title>
      <link>https://pydevtools.com/blog/pixi-can-now-build-packages-from-source/</link>
      <pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/pixi-can-now-build-packages-from-source/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/pixi/&#34;&gt;Pixi&lt;/a&gt; shipped &lt;a href=&#34;https://prefix.dev/blog/pixi-build&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pixi-build&lt;/a&gt;, a preview feature that lets pixi build packages from source instead of only consuming prebuilt conda packages. The feature borrows its architecture from Python&amp;rsquo;s &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-517/&#34;&gt;PEP 517&lt;/a&gt; build system, but targets conda packages instead of wheels.&lt;/p&gt;
&lt;h2&gt;How it works&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;how-it-works&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#how-it-works&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Python developers already know the &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-a-build-backend/&#34;&gt;build backend&lt;/a&gt; model: a frontend (pip, &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;, &lt;code&gt;python -m build&lt;/code&gt;) creates an isolated environment and calls standardized hooks on a backend (hatchling, setuptools, maturin) that produces artifacts. Pixi Build applies the same separation. Language-specific backends accept configuration and source code and produce conda packages:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Astral Shuts Down pyx, Open-Sources the Part That Mattered</title>
      <link>https://pydevtools.com/blog/astral-winds-down-pyx-open-sources-gpu-packaging/</link>
      <pubDate>Wed, 17 Jun 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/astral-winds-down-pyx-open-sources-gpu-packaging/</guid>
      <description>&lt;p&gt;Astral is winding down &lt;a href=&#34;https://astral.sh/blog/introducing-pyx&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pyx&lt;/a&gt;, its hosted commercial registry, and open-sourcing the GPU index and prebuilt-wheel infrastructure underneath it, with the artifacts free. Charlie Marsh confirmed the plan on &lt;a href=&#34;https://talkpython.fm/episodes/show/552/astral-joins-openai&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Talk Python #552&lt;/a&gt;, recorded about a month after the &lt;a href=&#34;https://pydevtools.com/blog/openai-acquires-astral/&#34;&gt;OpenAI acquisition closed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The commercial layer was the part Astral could afford to lose. The open tools and the GPU-packaging engine are what survive. For anyone who feared the OpenAI deal would lock the good parts behind a paywall, that ordering is the reassuring signal.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Blanket 1.0: An Early Look at Larry Hastings&#39;s Deterministic Threading Tests</title>
      <link>https://pydevtools.com/blog/blanket-1-0-early-look/</link>
      <pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/blanket-1-0-early-look/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.python.org/users/larry/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Larry Hastings&lt;/a&gt; (CPython core developer behind the &lt;a href=&#34;https://pyvideo.org/speaker/larry-hastings.html&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Gilectomy&lt;/a&gt; GIL-removal work) released &lt;a href=&#34;https://github.com/larryhastings/blanket&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Blanket&lt;/a&gt; 1.0 around his PyCon US 2026 talk, &lt;a href=&#34;https://us.pycon.org/2026/schedule/presentation/51/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;ldquo;Conquer multithreaded Python with Blanket&amp;rdquo;&lt;/a&gt;. It puts the Python thread scheduler under test control, making threaded tests deterministic. The repo is 10 days old with six stars, too early for the handbook to recommend, but it lands just as &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-703/&#34;&gt;PEP 703 makes free-threaded Python official&lt;/a&gt; for 3.14.&lt;/p&gt;
&lt;h2&gt;The problem Blanket solves&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;the-problem-blanket-solves&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#the-problem-blanket-solves&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A race between two threads might fire one run in a hundred, at the mercy of the OS scheduler, leaving you to either ignore it or hand-write brittle &lt;code&gt;threading.Event&lt;/code&gt; plumbing to force the bug out. &lt;a href=&#34;https://pydevtools.com/handbook/how-to/how-to-use-free-threaded-python-in-a-uv-project/&#34;&gt;Free-threaded Python&lt;/a&gt; makes it worse: code the GIL used to serialize is now genuinely concurrent, so tests that quietly passed start flaking.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PyPI Moved 1.92 Exabytes Last Year. Its Safety Team Is One Person.</title>
      <link>https://pydevtools.com/blog/pypi-exabyte-year-one-person-safety-team/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/pypi-exabyte-year-one-person-safety-team/</guid>
      <description>&lt;p&gt;On July 26, 2025, &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pypi/&#34;&gt;PyPI&lt;/a&gt; maintainers received phishing emails pointing at &lt;code&gt;pypj.org&lt;/code&gt;. The domain proxied the real PyPI login page in real time. The address bar showed &lt;code&gt;hxxps://pypj.org/&lt;/code&gt;, the lowercase &lt;code&gt;j&lt;/code&gt; close enough to an &lt;code&gt;i&lt;/code&gt; that four maintainers signed in anyway. The proxy captured each TOTP code in the fraction of a second PyPI accepted it, logged in, generated API tokens, and uploaded two malicious releases of &lt;a href=&#34;https://pypi.org/project/num2words/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;num2words&lt;/a&gt; (0.5.15 and 0.5.16) before anyone noticed. PyPI &lt;a href=&#34;https://blog.pypi.org/posts/2025-07-31-incident-report-phishing-attack/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;shipped a phishing-domain detector on July 28 at 11:37 EDT&lt;/a&gt; and revoked the tokens; the registrar parked the domain shortly after.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Bernát Gábor&#39;s Recap of the 2026 Python Packaging Summit</title>
      <link>https://pydevtools.com/blog/python-packaging-summit-2026-recap/</link>
      <pubDate>Mon, 18 May 2026 12:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/python-packaging-summit-2026-recap/</guid>
      <description>&lt;p&gt;The &lt;a href=&#34;https://us.pycon.org/2026/events/packaging-summit/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;2026 Python Packaging Summit&lt;/a&gt; ran on Friday, May 15 in Long Beach. Bernát Gábor&amp;rsquo;s &lt;a href=&#34;https://bernat.tech/posts/pycon-us-2026-packaging-summit-recap/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;hour-by-hour recap&lt;/a&gt; is the long read; the pre-summit &lt;a href=&#34;https://pydevtools.com/blog/python-packaging-summit-2026/&#34;&gt;preview&lt;/a&gt; is the companion. Worth reading the whole thing if you ship or install Python packages.&lt;/p&gt;
&lt;p&gt;The handbook-relevant headlines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/wheel/&#34;&gt;Wheel 2.0&lt;/a&gt; was restructured into a small core PEP plus optional sub-PEPs, with &lt;a href=&#34;https://peps.python.org/pep-0784/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Zstandard compression&lt;/a&gt; as the first sub-PEP. Roughly 25% smaller wheels on the top 1,000 PyPI projects. Broad adoption is years out.&lt;/li&gt;
&lt;li&gt;Mike Fiedler laid out PyPI&amp;rsquo;s safety math: project creation up 3.1x and upload bytes up 3.5x since 2024, against one funded full-time safety engineer. &lt;a href=&#34;https://pydevtools.com/handbook/explanation/why-use-trusted-publishing-for-pypi/&#34;&gt;Trusted publishing&lt;/a&gt; adoption climbed from ~10% to ~30%.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-772/&#34;&gt;PEP 772&lt;/a&gt; (Packaging Council) was accepted. First elections this fall, aligned with the PSF board cycle, with PSF voting membership required to nominate or be nominated.&lt;/li&gt;
&lt;li&gt;PEP 803 defines &lt;code&gt;abi3t&lt;/code&gt;, a stable ABI for &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-703/&#34;&gt;free-threaded CPython&lt;/a&gt;. A single wheel can target both interpreters via a compound &lt;code&gt;cp315-abi3.abi3t&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;Mobile wheels work end to end (&lt;a href=&#34;https://pydevtools.com/handbook/reference/cibuildwheel/&#34;&gt;cibuildwheel&lt;/a&gt; supports Android and iOS) but adoption is small: 11 of the top 360 packages ship Android wheels, 9 ship iOS.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 class=&#34;pdt-learn-more&#34;&gt;Learn more&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;learn-more&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#learn-more&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://bernat.tech/posts/pycon-us-2026-packaging-summit-recap/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Bernát Gábor&amp;rsquo;s full recap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hackmd.io/@jezdez/pycon2026-packaging-summit/edit&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;HackMD collaborative summit notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pydevtools.com/blog/python-packaging-summit-2026/&#34;&gt;The 2026 Packaging Summit Returns to PyCon US&lt;/a&gt; (pre-summit preview)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://peps.python.org/pep-0777/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PEP 777, Wheel 2.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://peps.python.org/pep-0803/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PEP 803, abi3t&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <title>If you&#39;re on mypy in 2026, Pyrefly is the obvious upgrade</title>
      <link>https://pydevtools.com/blog/pyrefly-1-0-is-the-obvious-mypy-upgrade/</link>
      <pubDate>Wed, 13 May 2026 11:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/pyrefly-1-0-is-the-obvious-mypy-upgrade/</guid>
      <description>&lt;p&gt;The case for staying on &lt;a href=&#34;https://pydevtools.com/handbook/reference/mypy/&#34;&gt;mypy&lt;/a&gt; has often been migration cost. &lt;a href=&#34;https://pydevtools.com/handbook/reference/pyrefly/&#34;&gt;Pyrefly&lt;/a&gt; 1.0, released May 12, 2026, makes the mechanical half close to free:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv add --dev pyrefly
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv run pyrefly init
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv run pyrefly check --baseline&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;pyrefly-baseline.json --update-baseline
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uv run pyrefly check&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container  hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copy code&#34;
    aria-label=&#34;Copy code&#34;
    data-copied-label=&#34;Copied!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;pyrefly init&lt;/code&gt; reads an existing &lt;code&gt;mypy.ini&lt;/code&gt; or &lt;code&gt;[tool.mypy]&lt;/code&gt; section, writes the equivalent Pyrefly configuration, and turns on a preset called &lt;code&gt;legacy&lt;/code&gt; that disables the checks mypy does not have. Pyrefly&amp;rsquo;s inference is more aggressive than mypy&amp;rsquo;s on the checks both tools run, so a real codebase will surface new diagnostics on the first check pass. The count depends on the codebase and how much was typed before. The baseline-snapshot step writes those errors to JSON so the final &lt;code&gt;check&lt;/code&gt; returns clean and CI stays green while the team triages on its own schedule.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Python 3.14.5 rolls back the incremental garbage collector</title>
      <link>https://pydevtools.com/blog/python-3145-rolls-back-the-incremental-garbage-collector/</link>
      <pubDate>Mon, 11 May 2026 09:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/python-3145-rolls-back-the-incremental-garbage-collector/</guid>
      <description>&lt;p&gt;Python 3.14.5 &lt;a href=&#34;https://blog.python.org/2026/05/python-3145-is-out/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;landed on 10 May 2026&lt;/a&gt;, pulled the incremental garbage collector that shipped in 3.14.0 through 3.14.4, and put the 3.13 generational collector back. Patch releases almost never reverse a major implementation change. The &lt;a href=&#34;https://docs.python.org/3.14/whatsnew/3.14.html#garbage-collection&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;What&amp;rsquo;s New page&lt;/a&gt; names the cause: &amp;ldquo;due to a number of reports of significant memory pressure in production environments, it has been reverted back to the generational GC from 3.13.&amp;rdquo;&lt;/p&gt;
&lt;h2&gt;Recap what the incremental collector promised&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;recap-what-the-incremental-collector-promised&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#recap-what-the-incremental-collector-promised&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-python/&#34;&gt;CPython&lt;/a&gt; collects cyclic garbage with a tracing collector that runs alongside refcounting. Up through 3.13, that collector was generational with three generations. Objects that survived a collection moved up a generation, and the collector visited older generations less often. A full collection of the oldest generation traced every object the program kept alive, which on long-running processes produced multi-hundred-millisecond pauses.&lt;/p&gt;</description>
    </item>
    <item>
      <title>mypy 2.0 picks parallelism over a rewrite</title>
      <link>https://pydevtools.com/blog/mypy-2-0-parallel-type-checking/</link>
      <pubDate>Sat, 09 May 2026 09:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/mypy-2-0-parallel-type-checking/</guid>
      <description>&lt;p&gt;For most of the past year, the speed conversation around Python type checkers has had one shape: &lt;a href=&#34;https://pydevtools.com/handbook/reference/mypy/&#34;&gt;mypy&lt;/a&gt; is the slow incumbent, and the fast alternatives (&lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/pyrefly/&#34;&gt;Pyrefly&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/zuban/&#34;&gt;Zuban&lt;/a&gt;) earn their speed by being written in Rust. &lt;a href=&#34;https://mypy-lang.blogspot.com/2026/05/mypy-20-relased.html&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;mypy 2.0&lt;/a&gt;, released May 6, 2026, takes a different route. It runs across multiple cores.&lt;/p&gt;
&lt;h2&gt;Run mypy across cores&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;run-mypy-across-cores&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#run-mypy-across-cores&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The headline feature is &lt;code&gt;--num-workers N&lt;/code&gt; (or &lt;code&gt;-nN&lt;/code&gt;). Pass a worker count and mypy splits type checking across processes:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Locking dependencies for PEP 723 single-file scripts in May 2026</title>
      <link>https://pydevtools.com/blog/locking-dependencies-for-pep-723-scripts/</link>
      <pubDate>Fri, 08 May 2026 09:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/locking-dependencies-for-pep-723-scripts/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-723/&#34;&gt;PEP 723&lt;/a&gt; plus &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; made single-file Python scripts easy to share. Declare dependencies in a commented TOML block, drop the file in a gist, and &lt;code&gt;uv run script.py&lt;/code&gt; builds an ephemeral environment and runs it without a project directory or activation step. Locking those dependencies so the script behaves the same six months from now is still awkward. The inline &lt;code&gt;dependencies&lt;/code&gt; list pins the top-level requirements you specify and nothing else; the transitive graph gets re-resolved every time the script runs on a new machine.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Ruff Already Rewrites Your Python to Be More Idiomatic</title>
      <link>https://pydevtools.com/blog/ruff-already-rewrites-your-python-to-be-more-idiomatic/</link>
      <pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/ruff-already-rewrites-your-python-to-be-more-idiomatic/</guid>
      <description>&lt;p&gt;Open any Python codebase that&amp;rsquo;s been through a few hands and you&amp;rsquo;ll find the same fossils: a &lt;code&gt;for&lt;/code&gt; loop adding items to a set one at a time, a conditional slice that could be &lt;code&gt;removesuffix()&lt;/code&gt;, a two-line &lt;code&gt;open()&lt;/code&gt;/&lt;code&gt;read()&lt;/code&gt; that &lt;code&gt;pathlib&lt;/code&gt; handles in one call. The code works. It passes every linter you&amp;rsquo;ve enabled. And a reviewer who knows Python well would rewrite all of it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;Ruff&lt;/a&gt; can already do that rewriting. It ported 36 rules from &lt;a href=&#34;https://github.com/dosisod/refurb&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;refurb&lt;/a&gt; into a category called &lt;code&gt;FURB&lt;/code&gt;, and every one of them ships with an auto-fix. Most projects haven&amp;rsquo;t turned them on because Ruff&amp;rsquo;s defaults only enable Pyflakes (&lt;code&gt;F&lt;/code&gt;) and a subset of pycodestyle (&lt;code&gt;E&lt;/code&gt;). The rules that catch non-idiomatic patterns sit behind an opt-in flag, invisible to the people who&amp;rsquo;d benefit most.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Lightning Got Owned: When `import lightning` Steals Your Credentials</title>
      <link>https://pydevtools.com/blog/lightning-pypi-compromise-import-time-supply-chain-attack/</link>
      <pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/lightning-pypi-compromise-import-time-supply-chain-attack/</guid>
      <description>&lt;p&gt;&lt;code&gt;import lightning&lt;/code&gt; shows up at the top of millions of PyTorch training scripts. On April 30, 2026, that line was enough to ship a developer&amp;rsquo;s credentials to an attacker.&lt;/p&gt;
&lt;p&gt;Two malicious versions of the &lt;a href=&#34;https://pypi.org/project/lightning/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;lightning&lt;/a&gt; PyPI package, &lt;code&gt;2.6.2&lt;/code&gt; and &lt;code&gt;2.6.3&lt;/code&gt;, were uploaded today. Lightning averages 311 thousand downloads a day, and any CI that ran &lt;code&gt;uv sync --upgrade&lt;/code&gt; between the upload and the takedown pulled the bad wheel. Neither version corresponds to a GitHub release on &lt;code&gt;Lightning-AI/pytorch-lightning&lt;/code&gt;; the latest tagged release there is still &lt;code&gt;2.6.1&lt;/code&gt; from January 30. The bad code was uploaded directly to PyPI, which is the same pattern as the &lt;a href=&#34;https://pydevtools.com/blog/litellm-supply-chain-attack-and-securing-python-dependencies/&#34;&gt;litellm compromise&lt;/a&gt; six weeks ago. &lt;a href=&#34;https://socket.dev/blog/lightning-pypi-package-compromised&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Socket&lt;/a&gt; flagged both releases 18 minutes after upload, and PyPI has since quarantined the entire project.&lt;/p&gt;</description>
    </item>
    <item>
      <title>What Reddit taught me about why people switch to uv</title>
      <link>https://pydevtools.com/blog/what-reddit-taught-me-about-why-people-switch-to-uv/</link>
      <pubDate>Mon, 27 Apr 2026 09:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/what-reddit-taught-me-about-why-people-switch-to-uv/</guid>
      <description>&lt;p&gt;A thread titled &lt;a href=&#34;https://www.reddit.com/r/Python/comments/1mfd3ww/but_really_why_use_uv/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;ldquo;But really, why use uv?&amp;rdquo;&lt;/a&gt; landed on r/Python and pulled 476 upvotes. The original poster wasn&amp;rsquo;t antagonistic. They used Python casually, installed packages two or three times a month, and couldn&amp;rsquo;t see why &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; was worth adding to a setup that already worked.&lt;/p&gt;
&lt;p&gt;The comments convinced them. Reading through the top responses surfaces something the standard uv pitch misses: the compelling case for casual users is not the same as the compelling case for power users.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ty and pyrefly find different bugs</title>
      <link>https://pydevtools.com/blog/ty-and-pyrefly-find-different-bugs/</link>
      <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/ty-and-pyrefly-find-different-bugs/</guid>
      <description>&lt;div class=&#34;hx:overflow-x-auto hx:mt-6 hx:flex hx:flex-col hx:rounded-lg hx:border hx:py-4 hx:px-4 hx:border-gray-200 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200&#34;&gt;
  &lt;p class=&#34;hx:flex hx:items-center hx:font-medium&#34;&gt;&lt;svg height=16px class=&#34;hx:inline-block hx:align-middle hx:mr-2&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; fill=&#34;none&#34; viewBox=&#34;0 0 24 24&#34; stroke-width=&#34;2&#34; stroke=&#34;currentColor&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; d=&#34;M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z&#34;/&gt;&lt;/svg&gt;Note&lt;/p&gt;

  &lt;div class=&#34;hx:w-full hx:min-w-0 hx:leading-7&#34;&gt;
    &lt;div class=&#34;hx:mt-6 hx:leading-7 hx:first:mt-0&#34;&gt;&lt;p&gt;&lt;strong&gt;Update 2026-04-29&lt;/strong&gt;: An earlier version of this post used a multi-line Liskov-violation panel from &lt;code&gt;rich/_null_file.py&lt;/code&gt; as an example of ty catching a real protocol bug. That diagnostic turned out to be a &lt;a href=&#34;https://github.com/astral-sh/ty/issues/2237&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ty false positive&lt;/a&gt;: ty reports the base parameter type as &lt;code&gt;Buffer&lt;/code&gt; when checking overrides on subclasses of &lt;code&gt;IO[str]&lt;/code&gt;, even though the parameterization should resolve &lt;code&gt;AnyStr&lt;/code&gt; to &lt;code&gt;str&lt;/code&gt;. mypy and pyright are both silent on the same code. The &amp;ldquo;Where ty pulls ahead&amp;rdquo; section has been rewritten with a tuple-arity finding instead, and the conclusion&amp;rsquo;s reference to &amp;ldquo;Liskov panels&amp;rdquo; has been dropped. Thanks to a reader for the catch.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Did pip 26 close the gap with uv?</title>
      <link>https://pydevtools.com/blog/did-pip-26-close-the-gap-with-uv/</link>
      <pubDate>Fri, 24 Apr 2026 09:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/did-pip-26-close-the-gap-with-uv/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/pip/&#34;&gt;pip&lt;/a&gt; 26.0 closed two of &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;&amp;rsquo;s clearest feature gaps in January: &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pep-723/&#34;&gt;PEP 723&lt;/a&gt; script installs and a datetime-based package filter that mirrors uv&amp;rsquo;s &lt;code&gt;--exclude-newer&lt;/code&gt;. The &lt;a href=&#34;https://news.ycombinator.com/item?id=45751400&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;October 2025 Hacker News thread&lt;/a&gt; that kept circling back to &amp;ldquo;pip has quietly caught up&amp;rdquo; now reads less contrarian than it did at the time, and the &lt;a href=&#34;https://pydevtools.com/blog/openai-acquires-astral/&#34;&gt;OpenAI acquisition of Astral&lt;/a&gt; in March changes the &amp;ldquo;is this safe to depend on&amp;rdquo; calculus at the same time.&lt;/p&gt;
&lt;h2&gt;Review pip&amp;rsquo;s 2025 to 2026 releases&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;review-pips-2025-to-2026-releases&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#review-pips-2025-to-2026-releases&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Four pip releases have landed since April 2025, and the first three narrowed gaps uv was best known for:&lt;/p&gt;</description>
    </item>
    <item>
      <title>How Astral Uses Its Own Tools</title>
      <link>https://pydevtools.com/blog/how-astral-uses-its-own-tools/</link>
      <pubDate>Thu, 23 Apr 2026 09:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/how-astral-uses-its-own-tools/</guid>
      <description>&lt;p&gt;Astral runs &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;ruff&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;, and &lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt; inside the repos that build them. Their &lt;code&gt;pyproject.toml&lt;/code&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/how-to/how-to-set-up-pre-commit-hooks-for-a-python-project/&#34;&gt;&lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;&lt;/a&gt;, and CI files are public, and a walk through the three shows how the people making ruff, ty, and uv wire their own toolchain into the projects that produce it.&lt;/p&gt;
&lt;h2&gt;Build ty from source and type-check Python with it&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;build-ty-from-source-and-type-check-python-with-it&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#build-ty-from-source-and-type-check-python-with-it&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The ruff repository ships ty as part of its crate tree. Three CI steps build ty from source and run it against the repo&amp;rsquo;s own Python:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Astral told you how they secure uv. Here&#39;s what to keep.</title>
      <link>https://pydevtools.com/blog/astral-security-post-what-to-keep/</link>
      <pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/astral-security-post-what-to-keep/</guid>
      <description>&lt;p&gt;Astral published &lt;a href=&#34;https://astral.sh/blog/open-source-security-at-astral&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;a detailed writeup&lt;/a&gt; of how they secure the org that ships uv, Ruff, and ty. It&amp;rsquo;s a good post. It&amp;rsquo;s also, for most readers, the wrong post.&lt;/p&gt;
&lt;p&gt;Most of what Astral describes is team-scale GitHub hygiene: org-wide branch protection rulesets, workflow audits with &lt;code&gt;zizmor&lt;/code&gt;, action pinning with &lt;code&gt;pinact&lt;/code&gt;, isolated GitHub Apps for privileged operations. If you run a project with outside contributors, read the whole thing. If you&amp;rsquo;re one person shipping a Python package, a lot of it is overkill for the threat model you actually face.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PyPI&#39;s Second Audit Found 14 Bugs. Two Remain.</title>
      <link>https://pydevtools.com/blog/pypi-second-security-audit/</link>
      <pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/pypi-second-security-audit/</guid>
      <description>&lt;p&gt;PyPI completed its &lt;a href=&#34;https://blog.pypi.org/posts/2026-04-16-pypi-completes-second-audit/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;second external security audit&lt;/a&gt; today. Trail of Bits found 14 issues in the Warehouse codebase: 2 High, 1 Medium, 7 Low, 4 Informational, 0 Critical. Twelve were patched. Two were accepted as known gaps. The work was funded by the &lt;a href=&#34;https://www.sovereign.tech/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Sovereign Tech Agency&lt;/a&gt;, with remediation by PSF&amp;rsquo;s Mike Fiedler through &lt;a href=&#34;https://alpha-omega.dev/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Alpha-Omega&lt;/a&gt; support.&lt;/p&gt;
&lt;p&gt;An audit of &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pypi/&#34;&gt;PyPI&lt;/a&gt; is an audit of the index infrastructure, not of the packages hosted on it. None of the findings describe malware in the wild. The patched bugs are the predictable part; the accepted ones are where the signal lives.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Your Python Wheels Still Target 2009 CPUs</title>
      <link>https://pydevtools.com/blog/your-python-wheels-still-target-2009-cpus/</link>
      <pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/your-python-wheels-still-target-2009-cpus/</guid>
      <description>&lt;p&gt;Intel shipped AVX2 in 2013. AMD reached it with Excavator-era parts around 2015. Eleven years later, the default NumPy wheel &lt;a href=&#34;https://pydevtools.com/handbook/reference/pip/&#34;&gt;pip&lt;/a&gt; downloads on an &lt;code&gt;x86_64&lt;/code&gt; Linux box is still compiled to run on processors from roughly 2003, the year AMD launched the Opteron and defined the AMD64 baseline. Every SIMD instruction that has shipped since is off-limits to the compiler that produced that wheel.&lt;/p&gt;
&lt;p&gt;The reason is structural. A &lt;a href=&#34;https://pydevtools.com/handbook/reference/wheel/&#34;&gt;wheel&lt;/a&gt; filename encodes three pieces of compatibility metadata: Python version, ABI, and platform. &amp;ldquo;Platform&amp;rdquo; for mainstream Linux wheels is typically &lt;code&gt;manylinux_2_17_x86_64&lt;/code&gt;, which says &amp;ldquo;works on any glibc 2.17+ x86-64 distro&amp;rdquo; and nothing about which CPU instruction sets the machine supports. When a project publishes one binary for all of those users, it has to target the lowest common denominator.&lt;/p&gt;</description>
    </item>
    <item>
      <title>uv won developer hearts. Now it has to win READMEs.</title>
      <link>https://pydevtools.com/blog/uv-admired-but-not-adopted/</link>
      <pubDate>Tue, 14 Apr 2026 09:00:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/uv-admired-but-not-adopted/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; ranked as the &lt;a href=&#34;https://survey.stackoverflow.co/2025/technology#admired-and-desired&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;most admired technology&lt;/a&gt; in Stack Overflow&amp;rsquo;s 2025 Developer Survey at 74%. A recent &lt;a href=&#34;https://aleyan.com/blog/2026-why-arent-we-uv-yet/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;analysis by Andrey Aleyan&lt;/a&gt; of the top 100,000 Python repositories on GitHub put uv adoption (measured by the presence of &lt;code&gt;uv.lock&lt;/code&gt;) at roughly 10%. The gap between &amp;ldquo;developers love this&amp;rdquo; and &amp;ldquo;developers ship this&amp;rdquo; is wider than any tooling story in recent memory.&lt;/p&gt;
&lt;p&gt;Aleyan&amp;rsquo;s diagnosis is that AI coding agents are the bottleneck: their training data is overwhelmingly &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;, so they emit &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;. That diagnosis is correct but incomplete. Adoption is bottlenecked by three reinforcing forces, and the AI-agent story is only one of them.&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Python Packaging Summit Returns to PyCon US</title>
      <link>https://pydevtools.com/blog/python-packaging-summit-2026/</link>
      <pubDate>Thu, 09 Apr 2026 12:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/python-packaging-summit-2026/</guid>
      <description>&lt;p&gt;The &lt;a href=&#34;https://us.pycon.org/2026/events/packaging-summit/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Python Packaging Summit&lt;/a&gt; returns Friday, May 15, 2026 in Room 201A of the Long Beach Convention Center, running 1:45 to 5:45 PM on the Friday of PyCon US. Pradyun Gedam, C.A.M. Gerlach, and Jannis Leidel co-chair again. Attendance is capped and invite-only: submit the &lt;a href=&#34;https://forms.gle/tu4zHaFYDCFFYMqx6&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;attendance form&lt;/a&gt; and &lt;a href=&#34;https://forms.gle/3ht7mTZ5N6FktnfH8&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;topic-pitch form&lt;/a&gt; by April 30 (AoE). The schedule drops around May 4.&lt;/p&gt;
&lt;p&gt;The summit puts the people who build &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/pip/&#34;&gt;pip&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/poetry/&#34;&gt;Poetry&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/hatch/&#34;&gt;Hatch&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/conda/&#34;&gt;conda&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-pypi/&#34;&gt;PyPI&lt;/a&gt;, and the major &lt;a href=&#34;https://pydevtools.com/handbook/reference/wheel/&#34;&gt;wheel&lt;/a&gt;-producing libraries in one room to argue through the year&amp;rsquo;s thorniest packaging questions. The notes from &lt;a href=&#34;https://hackmd.io/@pradyunsg/pycon2024-pack-summit&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;2024&lt;/a&gt; and &lt;a href=&#34;https://hackmd.io/@pradyunsg/pycon2025-packaging-summit&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;2025&lt;/a&gt; are the best public window into where &lt;a href=&#34;https://pydevtools.com/handbook/reference/pyproject.toml/&#34;&gt;pyproject.toml&lt;/a&gt;, wheel tags, and &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-a-lock-file/&#34;&gt;lock files&lt;/a&gt; are headed.&lt;/p&gt;</description>
    </item>
    <item>
      <title>How uv Solves Dependencies So Fast</title>
      <link>https://pydevtools.com/blog/how-uv-solves-dependencies-so-fast/</link>
      <pubDate>Thu, 09 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/how-uv-solves-dependencies-so-fast/</guid>
      <description>&lt;div class=&#34;hx:overflow-x-auto hx:mt-6 hx:flex hx:flex-col hx:rounded-lg hx:border hx:py-4 hx:px-4 hx:border-gray-200 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-green-200 hx:bg-green-100 hx:text-green-900 hx:dark:border-green-200/30 hx:dark:bg-green-900/30 hx:dark:text-green-200&#34;&gt;
  &lt;p class=&#34;hx:flex hx:items-center hx:font-medium&#34;&gt;&lt;svg height=16px class=&#34;hx:inline-block hx:align-middle hx:mr-2&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; fill=&#34;none&#34; viewBox=&#34;0 0 24 24&#34; stroke-width=&#34;2&#34; stroke=&#34;currentColor&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; d=&#34;M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z&#34;/&gt;&lt;/svg&gt;Tip&lt;/p&gt;

  &lt;div class=&#34;hx:w-full hx:min-w-0 hx:leading-7&#34;&gt;
    &lt;div class=&#34;hx:mt-6 hx:leading-7 hx:first:mt-0&#34;&gt;&lt;p&gt;Want more on uv? Browse every uv tutorial, how-to, reference, and explanation on the &lt;a href=&#34;https://pydevtools.com/handbook/topics/uv/&#34;&gt;uv topic page&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Python dependency resolution is NP-hard in the formal sense: it reduces to &lt;a href=&#34;https://en.wikipedia.org/wiki/Boolean_satisfiability_problem&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Boolean satisfiability&lt;/a&gt;. In a &lt;a href=&#34;https://www.janestreet.com/tech-talks/uv-an-extremely-fast-python-package-manager/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Jane Street tech talk&lt;/a&gt; (&lt;a href=&#34;https://youtu.be/gSKTfG1GXYQ&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;YouTube&lt;/a&gt;), Charlie Marsh walked through why &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;&amp;rsquo;s resolver has to solve this problem and the architectural decisions that let it do so 10-100x faster than &lt;a href=&#34;https://pydevtools.com/handbook/reference/pip/&#34;&gt;pip&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>LLM-Powered Copycats Are Flooding PyPI</title>
      <link>https://pydevtools.com/blog/llm-powered-copycats-are-flooding-pypi/</link>
      <pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/llm-powered-copycats-are-flooding-pypi/</guid>
      <description>&lt;p&gt;A developer named Roman Dubrovin &lt;a href=&#34;https://pypi.org/project/repowise/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;published repowise&lt;/a&gt;, a tool for generating structured wikis from codebases, as his first PyPI package. The next morning, he searched for it on PyPI and found three new packages he didn&amp;rsquo;t recognize: &lt;code&gt;repowise-pro&lt;/code&gt;, &lt;code&gt;repowise-enhanced&lt;/code&gt;, and &lt;code&gt;repowise-next&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All three had been uploaded within a 90-minute window. All three carried the same description: &amp;ldquo;Codebase intelligence that thinks ahead — outperforms repowise on every dimension.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;They weren&amp;rsquo;t empty shells. Someone had forked Dubrovin&amp;rsquo;s AGPL-3.0 licensed source code, run it through an LLM to patch a couple of minor issues, and republished under new names without attribution or license compliance. A community member &lt;a href=&#34;https://www.reddit.com/r/Python/comments/1sek3gq/i_published_my_first_pypi_package_few_ago_copycat/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;confirmed&lt;/a&gt; that all three packages traced back to the same person and the same GitHub repository.&lt;/p&gt;</description>
    </item>
    <item>
      <title>In 2012, Guido Had No Idea NumPy Had Its Own Packaging System</title>
      <link>https://pydevtools.com/blog/guido-had-no-idea-about-numpy-distutils/</link>
      <pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/guido-had-no-idea-about-numpy-distutils/</guid>
      <description>&lt;p&gt;In a &lt;a href=&#34;https://youtu.be/QjXJLVINsSA&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;recently resurfaced 2012 PyData panel discussion&lt;/a&gt;, Fernando Perez, Travis Oliphant, and David Cournapeau sat down with Guido van Rossum to discuss the scientific Python community&amp;rsquo;s needs. The conversation wandered through operator overloading (which eventually produced the &lt;code&gt;@&lt;/code&gt; matrix multiplication operator in Python 3.5) and import performance on supercomputers. The packaging segment is the one worth revisiting.&lt;/p&gt;
&lt;p&gt;David Cournapeau, who maintained NumPy&amp;rsquo;s build infrastructure, explained that NumPy had written its own packaging layer on top of &lt;a href=&#34;https://pydevtools.com/handbook/reference/distutils/&#34;&gt;distutils&lt;/a&gt; that was &amp;ldquo;as much code as distutils itself.&amp;rdquo; NumPy had to override nearly every piece of distutils to handle Fortran compilers, C++ extensions, &lt;a href=&#34;https://cython.org/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Cython&lt;/a&gt;, cross-platform compiler flags, and builds on exotic hardware like IBM Blue Gene supercomputers.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Migrating from mypy to ty: Lessons from FastAPI</title>
      <link>https://pydevtools.com/blog/migrating-from-mypy-to-ty-lessons-from-fastapi/</link>
      <pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/migrating-from-mypy-to-ty-lessons-from-fastapi/</guid>
      <description>&lt;p&gt;Sebastián Ramírez, creator of &lt;a href=&#34;https://github.com/tiangolo/fastapi&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;FastAPI&lt;/a&gt;, &lt;a href=&#34;https://x.com/tiangolo/status/1907849737048363046&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;recently announced&lt;/a&gt; that all his Python projects now use &lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt; for type checking. That includes FastAPI, &lt;a href=&#34;https://github.com/fastapi/typer&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Typer&lt;/a&gt;, &lt;a href=&#34;https://github.com/fastapi/sqlmodel&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SQLModel&lt;/a&gt;, &lt;a href=&#34;https://github.com/fastapi/asyncer&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Asyncer&lt;/a&gt;, and &lt;a href=&#34;https://github.com/fastapi/fastapi-cli&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;FastAPI CLI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Migrating the projects wasn&amp;rsquo;t a hard switch. It was incremental, messy in the middle, and completed at different speeds across different projects. That pattern offers a useful template for anyone considering the same move.&lt;/p&gt;
&lt;p&gt;Ramírez didn&amp;rsquo;t rip out &lt;a href=&#34;https://pydevtools.com/handbook/reference/mypy/&#34;&gt;mypy&lt;/a&gt; and replace it with ty in a single PR. Instead, he added ty alongside mypy and ran both checkers in parallel. Four of his five major repos still operate this way. Only SQLModel has fully dropped mypy.&lt;/p&gt;</description>
    </item>
    <item>
      <title>LiteLLM Got Owned, and Your Dependencies Might Be Next</title>
      <link>https://pydevtools.com/blog/litellm-supply-chain-attack-and-securing-python-dependencies/</link>
      <pubDate>Tue, 24 Mar 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/litellm-supply-chain-attack-and-securing-python-dependencies/</guid>
      <description>&lt;p&gt;Earlier today, someone published malicious versions of &lt;a href=&#34;https://pypi.org/project/litellm/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;litellm&lt;/a&gt; (versions 1.82.7 and 1.82.8) to PyPI. No corresponding release appeared on GitHub. The package was uploaded directly to PyPI, bypassing the normal release process, which points to a compromised maintainer account or token.&lt;/p&gt;
&lt;div class=&#34;hx:overflow-x-auto hx:mt-6 hx:flex hx:flex-col hx:rounded-lg hx:border hx:py-4 hx:px-4 hx:border-gray-200 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200&#34;&gt;
  &lt;p class=&#34;hx:flex hx:items-center hx:font-medium&#34;&gt;&lt;svg height=16px class=&#34;hx:inline-block hx:align-middle hx:mr-2&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; fill=&#34;none&#34; viewBox=&#34;0 0 24 24&#34; stroke-width=&#34;2&#34; stroke=&#34;currentColor&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; d=&#34;M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z&#34;/&gt;&lt;/svg&gt;Note&lt;/p&gt;</description>
    </item>
    <item>
      <title>OpenAI to Acquire Astral</title>
      <link>https://pydevtools.com/blog/openai-acquires-astral/</link>
      <pubDate>Thu, 19 Mar 2026 00:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/openai-acquires-astral/</guid>
      <description>&lt;p&gt;OpenAI &lt;a href=&#34;https://openai.com/index/openai-to-acquire-astral/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;announced today&lt;/a&gt; that it will acquire &lt;a href=&#34;https://astral.sh/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Astral&lt;/a&gt;, the company behind &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;Ruff&lt;/a&gt;, and &lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt;. The Astral team will join OpenAI&amp;rsquo;s Codex group after the deal closes, subject to regulatory approval.&lt;/p&gt;
&lt;h2&gt;What this means for Codex&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;what-this-means-for-codex&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#what-this-means-for-codex&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://openai.com/index/codex/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Codex&lt;/a&gt;, OpenAI&amp;rsquo;s coding agent, has grown to over 2 million weekly active users, with 3x user growth and 5x usage increase since the start of 2026. Acquiring Astral gives OpenAI direct ownership of tools that millions of Python developers already depend on. OpenAI plans to integrate Astral&amp;rsquo;s tools more deeply into Codex so the agent can interact with the toolchain developers already use.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Charlie Marsh on uv, Coding Agents, and the Changing Open Source Contract</title>
      <link>https://pydevtools.com/blog/charlie-marsh-test-set-interview/</link>
      <pubDate>Tue, 24 Feb 2026 12:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/charlie-marsh-test-set-interview/</guid>
      <description>&lt;p&gt;Charlie Marsh (founder of &lt;a href=&#34;https://astral.sh/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Astral&lt;/a&gt;, creator of &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;ruff&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;, and &lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt;) joined Michael Chow and Wes McKinney on &lt;a href=&#34;https://posit.co/thetestset/episode/charlie-marsh-more-productive-but-a-lot-less-fun/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;The Test Set&lt;/a&gt;. They covered uv&amp;rsquo;s adoption strategy, how coding agents are changing Astral&amp;rsquo;s development process, and what&amp;rsquo;s happening to open source when writing code becomes cheap.&lt;/p&gt;
&lt;h2&gt;uv&amp;rsquo;s compatibility layer was the point&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;uvs-compatibility-layer-was-the-point&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#uvs-compatibility-layer-was-the-point&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When uv first shipped, it only had &lt;code&gt;uv pip install&lt;/code&gt; and &lt;code&gt;uv venv&lt;/code&gt;. Some people found this odd. Charlie says it was deliberate:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Seven Claude Code Skills for Python from the Creator of spaCy</title>
      <link>https://pydevtools.com/blog/matthew-honnibal-claude-skills-for-python/</link>
      <pubDate>Mon, 16 Feb 2026 08:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/matthew-honnibal-claude-skills-for-python/</guid>
      <description>&lt;p&gt;Matthew Honnibal, co-founder of &lt;a href=&#34;https://explosion.ai/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Explosion&lt;/a&gt; and creator of &lt;a href=&#34;https://spacy.io/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;spaCy&lt;/a&gt;, has published &lt;a href=&#34;https://github.com/honnibal/claude-skills&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;a collection of Claude Code skills&lt;/a&gt; for Python development. It&amp;rsquo;s one of the more thoughtful skill collections I&amp;rsquo;ve seen.&lt;/p&gt;
&lt;p&gt;The collection ships as &lt;a href=&#34;https://code.claude.com/docs/en/slash-commands&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;slash commands&lt;/a&gt;: markdown files you place in &lt;code&gt;~/.claude/commands/&lt;/code&gt;. When you type &lt;code&gt;/&amp;lt;name&amp;gt;&lt;/code&gt; in &lt;a href=&#34;https://pydevtools.com/handbook/explanation/claude-code-complete-guide/&#34;&gt;Claude Code&lt;/a&gt;, the file&amp;rsquo;s contents are injected as instructions. Think of them as reusable prompts that give Claude a specific methodology for a task, rather than leaving it to improvise.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Pydantic Monty: A Secure Python Interpreter for AI Agents</title>
      <link>https://pydevtools.com/blog/pydantic-monty-secure-python-interpreter-for-ai-agents/</link>
      <pubDate>Fri, 06 Feb 2026 10:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/pydantic-monty-secure-python-interpreter-for-ai-agents/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pydantic.dev/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Pydantic&lt;/a&gt; has released &lt;a href=&#34;https://github.com/pydantic/monty&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Monty&lt;/a&gt;, a minimal Python interpreter written in Rust for safely executing LLM-generated code.&lt;/p&gt;
&lt;p&gt;AI agents that generate code face a tradeoff: trust the generated code completely, or spin up containers for isolation. Containers are slow and resource-intensive. Trusting arbitrary code is risky. Monty sidesteps both by sandboxing execution at the interpreter level, blocking dangerous operations while running Python in microseconds. The handbook&amp;rsquo;s guide on &lt;a href=&#34;https://pydevtools.com/handbook/tutorial/set-up-a-python-project-for-claude-code/&#34;&gt;setting up a Python project for Claude Code&lt;/a&gt; and &lt;a href=&#34;https://pydevtools.com/handbook/explanation/modern-python-project-setup-guide-for-ai-assistants/&#34;&gt;modern Python project setup for AI assistants&lt;/a&gt; provide additional context for working with AI agents in Python projects.&lt;/p&gt;</description>
    </item>
    <item>
      <title>FOSDEM Talk: Modern Python Monorepo with uv, Workspaces, and prek</title>
      <link>https://pydevtools.com/blog/fosdem-talk-modern-python-monorepo/</link>
      <pubDate>Thu, 05 Feb 2026 08:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/fosdem-talk-modern-python-monorepo/</guid>
      <description>&lt;p&gt;Jarek Potiuk, Apache Airflow PMC member and the project&amp;rsquo;s top committer, gave a talk at FOSDEM 2026 on how Airflow manages its massive Python monorepo. The &lt;a href=&#34;https://ftp.belnet.be/mirror/FOSDEM/video/2026/ua2220/WE7NHM-modern-python-monorepo-apache-airflow.av1.webm&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;recording is now available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Airflow ships over 120 separate Python distributions from a single repository, with 700+ dependencies and 3,600+ contributors. That scale forced the team to modernize their tooling. The talk walks through how they did it.&lt;/p&gt;
&lt;h2&gt;uv Workspaces for Monorepo Management&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;uv-workspaces-for-monorepo-management&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#uv-workspaces-for-monorepo-management&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The new setup centers on &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; workspaces. Rather than splitting 120 distributions across separate repositories or bundling everything into an unmanageable monolith, Airflow uses uv&amp;rsquo;s workspace feature to let distributions depend on each other via local sources instead of pulling from PyPI. The handbook&amp;rsquo;s &lt;a href=&#34;https://pydevtools.com/handbook/how-to/how-to-set-up-a-python-monorepo-with-uv-workspaces/&#34;&gt;guide to setting up a Python monorepo with uv workspaces&lt;/a&gt; covers the same pattern at a smaller scale.&lt;/p&gt;</description>
    </item>
    <item>
      <title>uvx.sh: Install Python tools without uv or Python</title>
      <link>https://pydevtools.com/blog/uvx-sh-install-python-tools-without-uv-or-python/</link>
      <pubDate>Thu, 22 Jan 2026 12:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/uvx-sh-install-python-tools-without-uv-or-python/</guid>
      <description>&lt;p&gt;Astral has released &lt;a href=&#34;https://uvx.sh&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;uvx.sh&lt;/a&gt;, a service that generates installation scripts for any Python tool on PyPI. With a single curl or PowerShell command, users can install tools like &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;ruff&lt;/a&gt; or &lt;a href=&#34;https://pydevtools.com/handbook/reference/pytest/&#34;&gt;pytest&lt;/a&gt; without having &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; or Python already installed on their system.&lt;/p&gt;
&lt;h2&gt;How It Works&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;how-it-works&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#how-it-works&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;uvx.sh dynamically generates installation scripts for any PyPI package. The URL pattern is simple:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;uvx.sh/{package}/install.sh     # macOS/Linux
uvx.sh/{package}/install.ps1    # Windows&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container  hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copy code&#34;
    aria-label=&#34;Copy code&#34;
    data-copied-label=&#34;Copied!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For example, to install ruff:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Teaching Claude Code Quality Patterns with a Custom Skill</title>
      <link>https://pydevtools.com/blog/dignified-python-313-claude-skill/</link>
      <pubDate>Fri, 09 Jan 2026 09:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/dignified-python-313-claude-skill/</guid>
      <description>&lt;p&gt;Dagster&amp;rsquo;s &lt;a href=&#34;https://github.com/dagster-io/erk/tree/d0e3c177edc5f5013d149794f5cbee459c7b9ccd/.claude/skills/dignified-python-313&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;dignified-python-313 skill&lt;/a&gt; guides Claude Code to write Python 3.13 code that matches their project conventions.&lt;/p&gt;
&lt;h2&gt;What It Covers&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;what-it-covers&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#what-it-covers&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Modern Python 3.13 Patterns: Use native type syntax (&lt;code&gt;list[str]&lt;/code&gt;, &lt;code&gt;str | None&lt;/code&gt;) instead of older alternatives. Skip &lt;code&gt;from __future__ import annotations&lt;/code&gt; since Python 3.13 doesn&amp;rsquo;t need it.&lt;/p&gt;
&lt;p&gt;CLI Development: Build Click interfaces with proper error handling. Use &lt;code&gt;click.echo()&lt;/code&gt; for output, direct errors to stderr, and treat CLI commands as error boundaries that catch exceptions and exit with &lt;code&gt;SystemExit(1)&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ty is Built with AI</title>
      <link>https://pydevtools.com/blog/ai-driven-software-development/</link>
      <pubDate>Fri, 02 Jan 2026 08:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/ai-driven-software-development/</guid>
      <description>&lt;p&gt;Tool creators are building their tools with AI coding assistants.&lt;/p&gt;
&lt;p&gt;Boris Cherny, creator of &lt;a href=&#34;https://code.claude.com/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Claude Code&lt;/a&gt;, &lt;a href=&#34;https://x.com/bcherny/status/2004626064187031831&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;didn&amp;rsquo;t open an IDE in December but wrote 200 PRs with Claude Code&lt;/a&gt;. Charlie Marsh, creator of &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;ruff&lt;/a&gt;, &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;, and &lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt;, &lt;a href=&#34;https://x.com/charliermarsh/status/2006792788369965393&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;noted&lt;/a&gt; that all &lt;a href=&#34;https://github.com/astral-sh/ruff/pulls?q=is%3Apr&amp;#43;author%3Acharliermarsh&amp;#43;label%3Aty&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;his PRs&lt;/a&gt; to ty were created with Claude Code. The tools building themselves.&lt;/p&gt;
&lt;h2&gt;Integration with Python Tooling&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;integration-with-python-tooling&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#integration-with-python-tooling&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The handbook&amp;rsquo;s &lt;a href=&#34;https://pydevtools.com/handbook/explanation/modern-python-project-setup-guide-for-ai-assistants/&#34;&gt;Modern Python Project Setup Guide for AI Assistants&lt;/a&gt; provides structured guidance so AI tools default to &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; and modern patterns rather than falling back to &lt;code&gt;pip&lt;/code&gt; commands.&lt;/p&gt;</description>
    </item>
    <item>
      <title>How uv Achieved Its Performance</title>
      <link>https://pydevtools.com/blog/how-uv-achieved-its-performance/</link>
      <pubDate>Fri, 26 Dec 2025 18:00:00 +0000</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/how-uv-achieved-its-performance/</guid>
      <description>&lt;p&gt;Andrew Nesbitt&amp;rsquo;s &lt;a href=&#34;https://nesbitt.io/2025/12/26/how-uv-got-so-fast.html&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;ldquo;How uv got so fast&amp;rdquo;&lt;/a&gt; explains that &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;&amp;rsquo;s speed comes less from Rust and more from eliminating work entirely.&lt;/p&gt;
&lt;h2&gt;Speed Through Elimination&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;speed-through-elimination&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#speed-through-elimination&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Rather than optimizing slow code paths, &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; just removes them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No &lt;code&gt;.egg&lt;/code&gt; support&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;pip.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;No bytecode compilation by default&lt;/li&gt;
&lt;li&gt;Ignoring &lt;code&gt;requires-python&lt;/code&gt; upper bounds reduces resolver backtracking&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Rust-Independent Optimizations&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;rust-independent-optimizations&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#rust-independent-optimizations&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many of &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;&amp;rsquo;s performance gains could work in &lt;a href=&#34;https://pydevtools.com/handbook/reference/pip/&#34;&gt;pip&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parallel downloads instead of sequential&lt;/li&gt;
&lt;li&gt;Smart HTTP range requests for metadata&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt; got fast by launching when PEP 658 infrastructure was ready and by skipping backward compatibility requirements that slow down &lt;a href=&#34;https://pydevtools.com/handbook/reference/pip/&#34;&gt;pip&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ty: Astral&#39;s New Python Type Checker Released</title>
      <link>https://pydevtools.com/blog/ty-beta/</link>
      <pubDate>Tue, 16 Dec 2025 22:43:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/ty-beta/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://astral.sh&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Astral&lt;/a&gt; released &lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt;, a Python type checker written in Rust, today. The company behind uv and Ruff designed ty as an alternative to &lt;a href=&#34;https://pydevtools.com/handbook/reference/mypy/&#34;&gt;mypy&lt;/a&gt; and &lt;a href=&#34;https://pydevtools.com/handbook/reference/pyright/&#34;&gt;pyright&lt;/a&gt;, with performance as a primary focus.&lt;/p&gt;
&lt;h2&gt;Speed Benchmarks&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;speed-benchmarks&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#speed-benchmarks&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;According to Astral&amp;rsquo;s benchmarks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ty checks the home-assistant project in 2.19 seconds (20x faster than mypy&amp;rsquo;s 45.66 seconds)&lt;/li&gt;
&lt;li&gt;After editing a file in PyTorch, ty recomputes diagnostics in 4.7ms (80x faster than pyright&amp;rsquo;s 386ms)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Key Features&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;key-features&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#key-features&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ty includes several design choices that distinguish it from existing type checkers:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Teaching LLMs Python Best Practices</title>
      <link>https://pydevtools.com/blog/teaching-llms-python-best-practices/</link>
      <pubDate>Fri, 21 Nov 2025 09:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/teaching-llms-python-best-practices/</guid>
      <description>&lt;p&gt;Large language models possess extensive Python knowledge from training data, yet they consistently struggle with modern tooling practices. Despite being trained on massive amounts of Python code, frontier models often default to outdated patterns like direct &lt;code&gt;python&lt;/code&gt; or &lt;code&gt;pip&lt;/code&gt; commands instead of using virtual environments and modern tools like &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;how is it, that despite every frontier LLM being insanely python maxxed they rarely use virtural envs, and even adding rules like &amp;quot;we are using uv, do everything with uv&amp;quot; they ignore it and think the system python is broken. like, every time. that&amp;#39;s actually an accomplishment&lt;/p&gt;</description>
    </item>
    <item>
      <title>Claude Code Hooks for uv Projects</title>
      <link>https://pydevtools.com/blog/claude-code-hooks-for-uv/</link>
      <pubDate>Tue, 11 Nov 2025 06:00:00 -0500</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/claude-code-hooks-for-uv/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://code.claude.com/docs/en/overview&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Claude Code&lt;/a&gt; supports &lt;a href=&#34;https://code.claude.com/docs/en/hooks-guide&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;custom hooks&lt;/a&gt; that guide the AI agent toward project-specific workflows. For Python projects using &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;uv&lt;/a&gt;, hooks can prevent Claude from falling back to &lt;code&gt;pip&lt;/code&gt; and &lt;code&gt;python&lt;/code&gt; commands.&lt;/p&gt;
&lt;div class=&#34;hx:overflow-x-auto hx:mt-6 hx:flex hx:flex-col hx:rounded-lg hx:border hx:py-4 hx:px-4 hx:border-gray-200 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200&#34;&gt;
  &lt;p class=&#34;hx:flex hx:items-center hx:font-medium&#34;&gt;&lt;svg height=16px class=&#34;hx:inline-block hx:align-middle hx:mr-2&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; fill=&#34;none&#34; viewBox=&#34;0 0 24 24&#34; stroke-width=&#34;2&#34; stroke=&#34;currentColor&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; d=&#34;M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z&#34;/&gt;&lt;/svg&gt;Note&lt;/p&gt;

  &lt;div class=&#34;hx:w-full hx:min-w-0 hx:leading-7&#34;&gt;
    &lt;div class=&#34;hx:mt-6 hx:leading-7 hx:first:mt-0&#34;&gt;&lt;p&gt;This post builds on the &lt;a href=&#34;https://pydevtools.com/blog/interceptors/&#34;&gt;interceptor approach&lt;/a&gt; for teaching AI agents about uv. Interceptors provide runtime feedback; hooks prevent problematic commands before execution.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Sync with uv: Eliminate Pre-commit Version Drift</title>
      <link>https://pydevtools.com/blog/sync-with-uv-eliminate-pre-commit-version-drift/</link>
      <pubDate>Fri, 26 Sep 2025 15:01:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/sync-with-uv-eliminate-pre-commit-version-drift/</guid>
      <description>&lt;p&gt;anaging tool versions across multiple configuration files creates persistent headaches in Python development. When you upgrade &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;ruff&lt;/a&gt; in your &lt;code&gt;pyproject.toml&lt;/code&gt;, you must remember to manually update the &lt;a href=&#34;https://pre-commit.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pre-commit&lt;/a&gt; hook version in &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;. This version drift causes inconsistent behavior between local development and &lt;a href=&#34;https://pydevtools.com/handbook/how-to/how-to-set-up-pre-commit-hooks-for-a-python-project/&#34;&gt;pre-commit checks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/tsvikas/sync-with-uv&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;sync-with-uv&lt;/a&gt; library solves this by automatically synchronizing tool versions between &lt;a href=&#34;https://pydevtools.com/handbook/reference/uv/&#34;&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt;&amp;rsquo;s &lt;a href=&#34;https://pydevtools.com/handbook/explanation/what-is-a-lock-file/&#34;&gt;lockfile&lt;/a&gt; and &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;the-problem&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#the-problem&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Permalink for this section&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Consider this common scenario:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# pyproject.toml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dependency-groups&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;lint&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ruff&amp;gt;=0.1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mypy&amp;gt;=1.0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container  hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copy code&#34;
    aria-label=&#34;Copy code&#34;
    data-copied-label=&#34;Copied!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# .pre-commit-config.yaml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;repos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;repo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;https://github.com/astral-sh/ruff-pre-commit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;rev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;v0.1.5 &lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# Must manually sync with pyproject.toml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;hooks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ruff&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container  hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copy code&#34;
    aria-label=&#34;Copy code&#34;
    data-copied-label=&#34;Copied!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When &lt;code&gt;uv lock --upgrade&lt;/code&gt; updates ruff to 0.1.8, the pre-commit hook stays pinned to 0.1.5. This creates confusing behavior where local checks differ from pre-commit results. The same gap applies to &lt;a href=&#34;https://pydevtools.com/handbook/reference/mypy/&#34;&gt;mypy&lt;/a&gt; and any other tool pinned in both files.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Analysis of the New Wave of Python Type Checkers</title>
      <link>https://pydevtools.com/blog/analysis-of-the-new-wave-of-python-type-checkers/</link>
      <pubDate>Wed, 17 Sep 2025 20:31:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/analysis-of-the-new-wave-of-python-type-checkers/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://sinon.github.io/future-python-type-checkers/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Rob Hand&amp;rsquo;s comparison of emerging type checkers&lt;/a&gt; is the first comprehensive look at three new Rust-based tools: &lt;a href=&#34;https://pydevtools.com/handbook/reference/ty/&#34;&gt;ty&lt;/a&gt; (Astral), &lt;a href=&#34;https://pydevtools.com/handbook/reference/pyrefly/&#34;&gt;pyrefly&lt;/a&gt; (Meta), and &lt;a href=&#34;https://pydevtools.com/handbook/reference/zuban/&#34;&gt;zuban&lt;/a&gt; (David Halter).&lt;/p&gt;
&lt;div class=&#34;hx:overflow-x-auto hx:mt-6 hx:flex hx:flex-col hx:rounded-lg hx:border hx:py-4 hx:px-4 hx:border-gray-200 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200&#34;&gt;
  &lt;p class=&#34;hx:flex hx:items-center hx:font-medium&#34;&gt;&lt;svg height=16px class=&#34;hx:inline-block hx:align-middle hx:mr-2&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; fill=&#34;none&#34; viewBox=&#34;0 0 24 24&#34; stroke-width=&#34;2&#34; stroke=&#34;currentColor&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; d=&#34;M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z&#34;/&gt;&lt;/svg&gt;Note&lt;/p&gt;

  &lt;div class=&#34;hx:w-full hx:min-w-0 hx:leading-7&#34;&gt;
    &lt;div class=&#34;hx:mt-6 hx:leading-7 hx:first:mt-0&#34;&gt;&lt;p&gt;This analysis was published in September 2025 when ty was pre-beta (known internally as Red-Knot). ty&amp;rsquo;s typing-spec conformance has improved significantly since then, from roughly 15% at the time to 86.5% (122/141 tests, ty 0.0.65, July 2026). For current conformance scores across all major type checkers, see &lt;a href=&#34;https://pydevtools.com/handbook/explanation/how-do-mypy-pyright-and-ty-compare/&#34;&gt;How do Python type checkers compare?&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>prek: pre-commit, but fast</title>
      <link>https://pydevtools.com/blog/prek-pre-commit-but-fast/</link>
      <pubDate>Tue, 16 Sep 2025 10:40:00 -0400</pubDate>
      <author>Tim Hopper</author>
      <guid>https://pydevtools.com/blog/prek-pre-commit-but-fast/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pre-commit.com/&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pre-commit&lt;/a&gt; is an essential tool in my development workflow for running &lt;a href=&#34;https://pydevtools.com/handbook/reference/ruff/&#34;&gt;ruff&lt;/a&gt; and other tools. However, nobody wants to wait for their commits to be saved, and pre-commit isn&amp;rsquo;t always the fastest.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://pydevtools.com/handbook/reference/prek/&#34;&gt;prek&lt;/a&gt; is a Rust-based drop-in replacement that runs 10x faster while using half the disk space.&lt;/p&gt;
&lt;p&gt;Replacing pre-commit with prek is painless:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install with &lt;code&gt;uv tool install prek&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;pre-commit uninstall &amp;amp;&amp;amp; prek install&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it. No configuration changes, no compatibility issues.&lt;/p&gt;</description>
    </item>
    
  </channel>
</rss>
