{"@attributes":{"version":"2.0"},"channel":{"title":"Python Developer Tooling Handbook \u2013 Python Developer Tooling Handbook","link":"https:\/\/pydevtools.com\/handbook\/","description":"A practical guide to Python's packaging, linting, testing, and typing tools. Covers uv, ruff, pytest, mypy, and more.","generator":"Hugo -- gohugo.io","language":"en","lastBuildDate":"Mon, 08 Jun 2026 15:33:37 -0400","item":[{"title":"uv: Python Package and Project Manager","link":"https:\/\/pydevtools.com\/handbook\/reference\/uv\/","pubDate":"Mon, 08 Jun 2026 15:33:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/uv\/","description":"<p>uv is a high-performance Python package and project manager that provides a unified interface for installing Python versions, managing dependencies, creating virtual environments, running scripts, formatting code, building packages, and publishing to package indexes.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"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\"\/><\/svg>Tip<\/p>"},{"title":"pipx: Install Python CLI Tools in Isolation","link":"https:\/\/pydevtools.com\/handbook\/reference\/pipx\/","pubDate":"Mon, 08 Jun 2026 15:33:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/pipx\/","description":"<p>pipx is a command-line tool that installs and runs Python command-line applications in isolated environments. Each installed application gets its own <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-virtual-environment\/\">virtual environment<\/a>, preventing dependency conflicts between tools while making executables available on <code>PATH<\/code>.<\/p>\n<h2>Key features<span class=\"hx:absolute hx:-mt-20\" id=\"key-features\"><\/span>\n    <a href=\"#key-features\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<ul>\n<li><strong>One venv per tool.<\/strong> Every installation lives in its own virtual environment under <code>$PIPX_HOME\/venvs\/&lt;name&gt;<\/code>, so dependencies never collide across tools.<\/li>\n<li><strong>Ephemeral runs.<\/strong> <code>pipx run &lt;tool&gt;<\/code> builds a cached venv on first use and reuses it for 14 idle days before garbage-collecting it.<\/li>\n<li><strong>In-place dependency edits.<\/strong> <code>pipx inject &lt;venv&gt; &lt;package&gt;<\/code> adds a package to an existing tool&rsquo;s environment without rebuilding; <code>pipx uninject<\/code> removes it.<\/li>\n<li><strong>Parallel installs of the same tool.<\/strong> <code>pipx install --suffix=2 ruff<\/code> keeps a second copy alongside the existing one, exposed on <code>PATH<\/code> as <code>ruff2<\/code>. The <code>--suffix<\/code> flag is marked experimental in pipx 1.7.<\/li>\n<li><strong>System-wide install mode.<\/strong> <code>pipx install --global<\/code> and the <code>PIPX_GLOBAL_*<\/code> variables install tools for every user on a machine instead of the current user.<\/li>\n<li><strong>Migration support.<\/strong> <code>pipx list --json &gt; snap.json<\/code> followed by <code>pipx install-all snap.json<\/code> rebuilds every venv on a new machine or Python version.<\/li>\n<li><strong>Optional uv backend.<\/strong> <code>pipx install pipx[uv]<\/code> switches the venv-creation step to uv for faster installs while keeping the pipx CLI unchanged.<\/li>\n<li><strong>Man-page integration.<\/strong> Tool man pages are symlinked under <code>$PIPX_MAN_DIR<\/code> so <code>man &lt;tool&gt;<\/code> works after install.<\/li>\n<\/ul>\n<h2>Usage<span class=\"hx:absolute hx:-mt-20\" id=\"usage\"><\/span>\n    <a href=\"#usage\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<div class=\"hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group\/code\">\n\n<div><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\">pipx install ruff                   <span class=\"c1\"># install a tool<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">pipx run cowsay hello               <span class=\"c1\"># run a tool without installing<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">pipx upgrade ruff                   <span class=\"c1\"># upgrade an installed tool<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">pipx inject mkdocs mkdocs-material  <span class=\"c1\"># add a dep to an existing tool&#39;s venv<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">pipx list                           <span class=\"c1\"># list installed tools<\/span><\/span><\/span><\/code><\/pre><\/div><\/div><div class=\"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\">\n  <button\n    class=\"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\"\n    title=\"Copy code\"\n    aria-label=\"Copy code\"\n    data-copied-label=\"Copied!\"\n  >\n    <div class=\"hextra-copy-icon hx:group-[.copied]\/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4\"><\/div>\n<div class=\"hextra-success-icon hx:hidden hx:group-[.copied]\/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4\"><\/div>\n  <\/button>\n<\/div>\n<\/div>\n<p>For example, <code>pipx install csvkit<\/code> installs the <a href=\"https:\/\/csvkit.readthedocs.io\/en\/latest\/\"target=\"_blank\" rel=\"noopener\">csvkit<\/a> suite of command-line tools in an isolated environment and makes its tools available on <code>PATH<\/code>.<\/p>"},{"title":"Getting started with uv","link":"https:\/\/pydevtools.com\/handbook\/tutorial\/getting-started-with-uv\/","pubDate":"Mon, 08 Jun 2026 15:33:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/tutorial\/getting-started-with-uv\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> handles Python installation, project setup, dependency management, and script execution in a single tool. This tutorial walks through each of those capabilities so you can start using uv for any Python project.<\/p>\n<h2>Prerequisites<span class=\"hx:absolute hx:-mt-20\" id=\"prerequisites\"><\/span>\n    <a href=\"#prerequisites\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>You need a terminal (Terminal on macOS, PowerShell on Windows, or any Linux shell). No prior Python installation is required.<\/p>\n<p><a href=\"https:\/\/git-scm.com\/downloads\"target=\"_blank\" rel=\"noopener\">Git<\/a> is optional. If Git is on your <code>PATH<\/code>, <code>uv init<\/code> initializes a repository in the new project. uv does not install Git itself; install it separately if you want version control. The tutorial steps work either way.<\/p>"},{"title":"Do you still need tox or nox if you use uv?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/do-you-still-need-tox-or-nox-if-you-use-uv\/","pubDate":"Mon, 08 Jun 2026 15:33:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/do-you-still-need-tox-or-nox-if-you-use-uv\/","description":"<p>For many projects, no. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> can run your test suite against multiple Python versions without any extra tool. But <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/tox\/\">tox<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/nox\/\">nox<\/a> do more than version switching, and the gap between &ldquo;run <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pytest\/\">pytest<\/a> on 3.12&rdquo; and &ldquo;test five Python versions against two dependency sets in parallel&rdquo; is where they still matter.<\/p>\n<h2>What uv handles on its own<span class=\"hx:absolute hx:-mt-20\" id=\"what-uv-handles-on-its-own\"><\/span>\n    <a href=\"#what-uv-handles-on-its-own\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p><code>uv run --python<\/code> selects a Python version and executes a command in your project&rsquo;s environment. If that version is not installed, uv downloads it automatically:<\/p>"},{"title":"Create your first Python project with uv","link":"https:\/\/pydevtools.com\/handbook\/tutorial\/create-your-first-python-project\/","pubDate":"Mon, 08 Jun 2026 15:33:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/tutorial\/create-your-first-python-project\/","description":"<p>This Python uv tutorial walks you through building a text analysis tool that counts words, measures sentence length, and reports word frequency. No prior Python experience required; <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> handles the Python install, project scaffolding, and dependency management for you.<\/p>\n<h2>Prerequisites<span class=\"hx:absolute hx:-mt-20\" id=\"prerequisites\"><\/span>\n    <a href=\"#prerequisites\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Before we begin, make sure you have uv installed on your system. You can install it following <a href=\"https:\/\/docs.astral.sh\/uv\/getting-started\/installation\/\"target=\"_blank\" rel=\"noopener\">the directions from the uv documentation<\/a>.<\/p>\n<p><a href=\"https:\/\/git-scm.com\/downloads\"target=\"_blank\" rel=\"noopener\">Git<\/a> is optional. uv does not install Git for you, but if Git is already on your <code>PATH<\/code>, <code>uv init<\/code> initializes a Git repository in the new project. The tutorial works either way.<\/p>"},{"title":"How to configure Cursor for Ruff","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-cursor-for-ruff\/","pubDate":"Mon, 08 Jun 2026 13:38:03 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-cursor-for-ruff\/","description":"<p>Cursor is a fork of VS Code, so the <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a> extension installs and behaves the same way. The one Cursor-specific difference is the extension source: Cursor uses the <a href=\"https:\/\/open-vsx.org\/\"target=\"_blank\" rel=\"noopener\">Open VSX Registry<\/a> instead of the VS Code Marketplace. The extension ID (<code>charliermarsh.ruff<\/code>) is the same.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"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\"\/><\/svg>Tip<\/p>"},{"title":"How to configure Cursor for pytest","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-cursor-for-pytest\/","pubDate":"Mon, 08 Jun 2026 13:37:02 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-cursor-for-pytest\/","description":"<p>Cursor&rsquo;s Testing sidebar runs <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pytest\/\">pytest<\/a> tests without leaving the editor, shows pass\/fail status inline next to each test, and opens the debugger on a single test with one click. Getting this working in a <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>-managed project takes three steps: installing pytest, enabling it in <code>.vscode\/settings.json<\/code>, and optionally setting pytest defaults in <code>pyproject.toml<\/code>.<\/p>\n<h2>Confirm prerequisites<span class=\"hx:absolute hx:-mt-20\" id=\"confirm-prerequisites\"><\/span>\n    <a href=\"#confirm-prerequisites\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<ul>\n<li><a href=\"https:\/\/cursor.com\/\"target=\"_blank\" rel=\"noopener\">Cursor editor<\/a> installed<\/li>\n<li>A Python project managed with <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a><\/li>\n<li>pytest installed as a dev dependency:<\/li>\n<\/ul>\n<div class=\"hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group\/code\">\n\n<div><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\">uv add --dev pytest<\/span><\/span><\/code><\/pre><\/div><\/div><div class=\"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\">\n  <button\n    class=\"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\"\n    title=\"Copy code\"\n    aria-label=\"Copy code\"\n    data-copied-label=\"Copied!\"\n  >\n    <div class=\"hextra-copy-icon hx:group-[.copied]\/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4\"><\/div>\n<div class=\"hextra-success-icon hx:hidden hx:group-[.copied]\/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4\"><\/div>\n  <\/button>\n<\/div>\n<\/div>\n<h2>Enable pytest in Cursor<span class=\"hx:absolute hx:-mt-20\" id=\"enable-pytest-in-cursor\"><\/span>\n    <a href=\"#enable-pytest-in-cursor\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Add these settings to <code>.vscode\/settings.json<\/code> at your project root:<\/p>"},{"title":"Set up Ruff for formatting and checking your code","link":"https:\/\/pydevtools.com\/handbook\/tutorial\/set-up-ruff-for-formatting-and-checking-your-code\/","pubDate":"Mon, 08 Jun 2026 13:35:05 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/tutorial\/set-up-ruff-for-formatting-and-checking-your-code\/","description":"<p>This tutorial helps you set up Ruff to automatically format your Python code and check it for common errors and style issues.<\/p>\n<h2>Prerequisites<span class=\"hx:absolute hx:-mt-20\" id=\"prerequisites\"><\/span>\n    <a href=\"#prerequisites\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Before starting, make sure you have uv installed on your system. You can install it following the <a href=\"https:\/\/docs.astral.sh\/uv\/getting-started\/installation\/\"target=\"_blank\" rel=\"noopener\">installation guide<\/a>.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 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\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">You do not need Python installed - uv will handle installing it automatically.<\/div>\n  <\/div>\n<\/div>\n\n<h2>Creating a sample project<span class=\"hx:absolute hx:-mt-20\" id=\"creating-a-sample-project\"><\/span>\n    <a href=\"#creating-a-sample-project\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Let&rsquo;s create a new project to demonstrate Ruff:<\/p>"},{"title":"What is a build frontend?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-build-frontend\/","pubDate":"Mon, 08 Jun 2026 13:34:15 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-build-frontend\/","description":"<p>A build frontend is a tool that invokes <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-build-backend\/\">build backends<\/a> to create Python packages. It manages the build environment and delegates the work of producing artifacts to the backend.<\/p>\n<h2>Build frontends in use<span class=\"hx:absolute hx:-mt-20\" id=\"build-frontends-in-use\"><\/span>\n    <a href=\"#build-frontends-in-use\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<ul>\n<li><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pip\/\">pip<\/a>: default package installer; builds packages during installation when no wheel is available<\/li>\n<li><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/build\/\">build<\/a>: dedicated <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pep-517\/\">PEP 517<\/a> builder from PyPA<\/li>\n<li><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>: acts as a build frontend via <code>uv build<\/code><\/li>\n<li><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/poetry\/\">poetry<\/a>\/<a href=\"https:\/\/pydevtools.com\/handbook\/reference\/hatch\/\">hatch<\/a>\/<a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pdm\/\">pdm<\/a>: project management tools with build capabilities<\/li>\n<\/ul>\n<h2>What a build frontend handles<span class=\"hx:absolute hx:-mt-20\" id=\"what-a-build-frontend-handles\"><\/span>\n    <a href=\"#what-a-build-frontend-handles\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<ul>\n<li>Discovering and loading the declared build backend<\/li>\n<li>Creating an isolated build environment<\/li>\n<li>Handling build configuration from <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyproject.toml\/\">pyproject.toml<\/a><\/li>\n<li>Processing build output (<a href=\"https:\/\/pydevtools.com\/handbook\/reference\/wheel\/\">wheels<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/sdist\/\">sdists<\/a>)<\/li>\n<li>Reporting build progress and errors<\/li>\n<\/ul>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 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\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">Build frontends implement <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pep-517\/\"target=\"_blank\" rel=\"noopener\">PEP 517&rsquo;s<\/a> standardized hook API for interacting with backends. Any compliant frontend can invoke any compliant backend.<\/div>\n  <\/div>\n<\/div>\n\n<h2>Running builds<span class=\"hx:absolute hx:-mt-20\" id=\"running-builds\"><\/span>\n    <a href=\"#running-builds\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Using the <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/build\/\">build<\/a> frontend:<\/p>"},{"title":"What is a Python Interpreter?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-python-interpreter\/","pubDate":"Mon, 08 Jun 2026 13:31:44 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-python-interpreter\/","description":"<p>A Python interpreter is the executable program that reads <code>.py<\/code> files and runs them. When someone says &ldquo;I have Python 3.12 installed,&rdquo; they mean a <code>python3.12<\/code> binary exists somewhere on their system that can execute Python code. Every Python project needs an interpreter, and most of the complexity in Python tooling comes from managing which interpreter runs where.<\/p>\n<h2>Where interpreters come from<span class=\"hx:absolute hx:-mt-20\" id=\"where-interpreters-come-from\"><\/span>\n    <a href=\"#where-interpreters-come-from\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Python interpreters arrive on a system through several channels:<\/p>"},{"title":"conda-forge: Community Conda Package Channel","link":"https:\/\/pydevtools.com\/handbook\/reference\/conda-forge\/","pubDate":"Mon, 08 Jun 2026 13:31:44 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/conda-forge\/","description":"<p>conda-forge is a community-maintained collection of <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/conda-package\/\">conda packages<\/a> distributed through a public channel on <a href=\"https:\/\/anaconda.org\/conda-forge\"target=\"_blank\" rel=\"noopener\">anaconda.org<\/a>. Over 25,000 feedstocks (package repositories) produce builds for Linux, macOS, and Windows across multiple CPU architectures. Each feedstock contains a build recipe in its own GitHub repository, and automated infrastructure rebuilds packages when dependencies change or new platforms appear.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Note<\/p>"},{"title":"Basedpyright: Python Type Checker (pyright Fork)","link":"https:\/\/pydevtools.com\/handbook\/reference\/basedpyright\/","pubDate":"Mon, 08 Jun 2026 13:31:44 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/basedpyright\/","description":"<p>Basedpyright is a static type checker and language server for Python, maintained by DetachHead as a community fork of Microsoft&rsquo;s <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyright\/\">pyright<\/a>. It tracks upstream pyright closely and adds diagnostic rules, stricter defaults, and language-server features that Microsoft keeps exclusive to its closed-source Pylance extension.<\/p>\n<p>The fork exists because pyright ships as an npm package and depends on Node.js, which makes it awkward to pin as a project dependency, and because the editor features most Python developers associate with &ldquo;pyright&rdquo; actually live in Pylance, whose license restricts it to official Microsoft VS Code builds. Basedpyright packages the language server on <a href=\"https:\/\/pypi.org\/project\/basedpyright\/\"target=\"_blank\" rel=\"noopener\">PyPI<\/a> so <code>uv add --dev basedpyright<\/code> works, and re-implements Pylance-style features (inlay hints, semantic highlighting, docstring completion) in the open-source server so VS Code forks like Cursor, VSCodium, and Positron can use them.<\/p>"},{"title":"Set up a Django project with uv","link":"https:\/\/pydevtools.com\/handbook\/tutorial\/set-up-a-django-project-with-uv\/","pubDate":"Sat, 06 Jun 2026 08:36:11 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/tutorial\/set-up-a-django-project-with-uv\/","description":"<p>Most <a href=\"https:\/\/www.djangoproject.com\/\"target=\"_blank\" rel=\"noopener\">Django<\/a> setup guides still tell you to install Python and a virtual environment by hand before you reach <code>runserver<\/code>. This tutorial uses <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> for the Python install and the <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-virtual-environment\/\">virtual environment<\/a>. Every <code>manage.py<\/code> command runs against a pinned interpreter.<\/p>\n<h2>Confirm uv is installed<span class=\"hx:absolute hx:-mt-20\" id=\"confirm-uv-is-installed\"><\/span>\n    <a href=\"#confirm-uv-is-installed\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>If you do not already have uv, follow the <a href=\"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-install-uv\/\">installation guide<\/a>. No separate Python install is required; uv will download an interpreter on first use.<\/p>"},{"title":"Conda","link":"https:\/\/pydevtools.com\/handbook\/reference\/conda\/","pubDate":"Sat, 06 Jun 2026 08:35:46 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/conda\/","description":"<p>Conda is a language-agnostic, cross-platform package and <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-virtual-environment\/\">environment<\/a> manager. Where <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pip\/\">pip<\/a> installs Python packages from <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pypi\/\">PyPI<\/a>, conda resolves dependencies across Python, R, C\/C++, Fortran, and other languages in a single dependency graph. The current stable release is 26.3.1 (March 2026).<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Note<\/p>"},{"title":"How to set up pre-commit hooks for a Python project","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-set-up-pre-commit-hooks-for-a-python-project\/","pubDate":"Fri, 05 Jun 2026 04:45:57 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-set-up-pre-commit-hooks-for-a-python-project\/","description":"<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-amber-200 hx:bg-amber-100 hx:text-amber-900 hx:dark:border-amber-200\/30 hx:dark:bg-amber-900\/30 hx:dark:text-amber-200\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">This guide assumes you have a Python project managed with <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\"target=\"_blank\" rel=\"noopener\">uv<\/a>. If you haven&rsquo;t created a project yet, see the <a href=\"https:\/\/pydevtools.com\/handbook\/tutorial\/create-your-first-python-project\/\"target=\"_blank\" rel=\"noopener\">project creation tutorial<\/a>.<\/div>\n  <\/div>\n<\/div>\n\n<p><a href=\"https:\/\/pre-commit.com\/\"target=\"_blank\" rel=\"noopener\">pre-commit<\/a> is a framework that manages Git hooks. It runs tools like linters and formatters automatically before each commit, catching problems before they reach version control. This guide walks through installing pre-commit, configuring hooks for a Python project, and integrating it with <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/mypy\/\">mypy<\/a>.<\/p>"},{"title":"What is PEP 621?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pep-621-compatibility\/","pubDate":"Fri, 05 Jun 2026 02:39:03 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pep-621-compatibility\/","description":"<p><a href=\"https:\/\/peps.python.org\/pep-0621\/\"target=\"_blank\" rel=\"noopener\">PEP 621<\/a> defines the <code>[project]<\/code> table in <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyproject.toml\/\">pyproject.toml<\/a> for declaring project name, version, description, dependencies, license, and other core metadata. Any compliant build tool can read this table without executing Python code or recognizing tool-specific configuration.<\/p>\n<h2>How tools stored metadata before PEP 621<span class=\"hx:absolute hx:-mt-20\" id=\"how-tools-stored-metadata-before-pep-621\"><\/span>\n    <a href=\"#how-tools-stored-metadata-before-pep-621\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Before PEP 621, every build tool stored project metadata in its own format. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/setuptools\/\">Setuptools<\/a> used <code>setup.py<\/code> (executable Python) or <code>setup.cfg<\/code> (an INI format with a setuptools-specific schema). <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/flit\/\">Flit<\/a> used <code>[tool.flit.metadata]<\/code>. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/poetry\/\">Poetry<\/a> stored everything under <code>[tool.poetry]<\/code>.<\/p>"},{"title":"How to configure VS Code for type checking in a uv project","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-vs-code-for-type-checking-in-a-uv-project\/","pubDate":"Thu, 04 Jun 2026 05:18:25 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-vs-code-for-type-checking-in-a-uv-project\/","description":"<p>VS Code doesn&rsquo;t show type errors by default. Adding inline diagnostics takes two steps: installing a type checker as a dev dependency with uv, and installing the matching VS Code extension. This guide covers two routes for a <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>-managed project: <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ty\/\">ty<\/a> with the official ty extension, and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/mypy\/\">mypy<\/a> with Microsoft&rsquo;s Mypy Type Checker extension.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-amber-200 hx:bg-amber-100 hx:text-amber-900 hx:dark:border-amber-200\/30 hx:dark:bg-amber-900\/30 hx:dark:text-amber-200\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">This guide assumes a uv-managed project with a <code>.venv<\/code> directory. If you haven&rsquo;t set one up yet, see <a href=\"https:\/\/pydevtools.com\/handbook\/tutorial\/create-your-first-python-project\/\"target=\"_blank\" rel=\"noopener\">create your first Python project<\/a>.<\/div>\n  <\/div>\n<\/div>\n\n<h2>Pick the right extension<span class=\"hx:absolute hx:-mt-20\" id=\"pick-the-right-extension\"><\/span>\n    <a href=\"#pick-the-right-extension\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<table>\n\t<thead>\n\t\t\t<tr>\n\t\t\t\t\t<th><\/th>\n\t\t\t\t\t<th>ty extension<\/th>\n\t\t\t\t\t<th>mypy extension<\/th>\n\t\t\t<\/tr>\n\t<\/thead>\n\t<tbody>\n\t\t\t<tr>\n\t\t\t\t\t<td><strong>Extension ID<\/strong><\/td>\n\t\t\t\t\t<td><code>astral-sh.ty<\/code><\/td>\n\t\t\t\t\t<td><code>ms-python.mypy-type-checker<\/code><\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><strong>Replaces Pylance<\/strong><\/td>\n\t\t\t\t\t<td>Yes (by default)<\/td>\n\t\t\t\t\t<td>No<\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><strong>Completions source<\/strong><\/td>\n\t\t\t\t\t<td>ty<\/td>\n\t\t\t\t\t<td>Pylance<\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><strong>Config in <code>pyproject.toml<\/code><\/strong><\/td>\n\t\t\t\t\t<td><code>[tool.ty]<\/code><\/td>\n\t\t\t\t\t<td><code>[tool.mypy]<\/code><\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><strong>Best for<\/strong><\/td>\n\t\t\t\t\t<td>New projects on the Astral toolchain<\/td>\n\t\t\t\t\t<td>Projects already on mypy<\/td>\n\t\t\t<\/tr>\n\t<\/tbody>\n<\/table>\n<p>If your project already runs mypy in CI, start with the mypy extension so the editor matches the pipeline. If you&rsquo;re adding type checking for the first time, the ty extension gives you a type checker and language server in one install.<\/p>"},{"title":"Flit: Python Build and Publish Tool","link":"https:\/\/pydevtools.com\/handbook\/reference\/flit\/","pubDate":"Thu, 04 Jun 2026 04:52:41 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/flit\/","description":"<p>Flit is a Python packaging tool for building and publishing pure-Python packages. It provides a CLI for scaffolding (<code>flit init<\/code>), local installation (<code>flit install<\/code>), and PyPI publishing (<code>flit publish<\/code>). Its build backend, flit-core, ships as a separate package with zero runtime dependencies and can be used independently of the flit CLI.<\/p>\n<p>Flit does not manage virtual environments or resolve dependencies; those are handled by separate tools such as <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>.<\/p>\n<h2>When to use Flit<span class=\"hx:absolute hx:-mt-20\" id=\"when-to-use-flit\"><\/span>\n    <a href=\"#when-to-use-flit\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Flit suits pure-Python libraries that have no C extensions and want a direct path from source to PyPI with minimal configuration. It requires less boilerplate than <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/setuptools\/\">setuptools<\/a> and has no lock file or virtualenv features to configure around.<\/p>"},{"title":"pip-tools: Python Dependency Pinning Tools","link":"https:\/\/pydevtools.com\/handbook\/reference\/pip-tools\/","pubDate":"Thu, 04 Jun 2026 04:51:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/pip-tools\/","description":"<p>pip-tools is a set of command-line utilities for managing Python dependency pinning. It consists of two commands: <code>pip-compile<\/code> for resolving and locking dependencies, and <code>pip-sync<\/code> for synchronizing a virtual environment to match a <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-lock-file\/\">lockfile<\/a>.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Note<\/p>"},{"title":"How to maintain a uv project","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-maintain-a-uv-project\/","pubDate":"Thu, 04 Jun 2026 04:51:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-maintain-a-uv-project\/","description":"<p>A <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> project needs regular care, like a dog. Most of that care can run on autopilot: Dependabot for lockfile bumps, <code>uv audit<\/code> in CI for vulnerability scans, and a scheduled workflow for <code>prek auto-update<\/code>. What&rsquo;s left is a short workstation check on the first Monday of the month.<\/p>\n<p>The rest of this guide gives the <a href=\"https:\/\/pydevtools.com\/handbook\/tutorial\/setting-up-github-actions-with-uv\/\">GitHub Actions<\/a> configs for the three automated jobs, walks through the manual monthly checks, and closes with the one rule that holds the whole thing together: don&rsquo;t bundle a maintenance bump into a release.<\/p>"},{"title":"direnv: Per-Directory Environment Variables for Python","link":"https:\/\/pydevtools.com\/handbook\/reference\/direnv\/","pubDate":"Thu, 04 Jun 2026 04:51:37 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/direnv\/","description":"<p>direnv is a shell extension that loads and unloads environment variables based on the current working directory. It reads a per-directory <code>.envrc<\/code> file when entering a folder and reverts the environment when leaving. Although direnv is language-agnostic, it is widely used in Python projects to activate <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-virtual-environment\/\">virtual environments<\/a> and export project-specific settings without polluting the global shell.<\/p>\n<h2>Key Features<span class=\"hx:absolute hx:-mt-20\" id=\"key-features\"><\/span>\n    <a href=\"#key-features\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<ul>\n<li>Loads variables from <code>.envrc<\/code> on <code>cd<\/code> into a directory and unloads them on <code>cd<\/code> out<\/li>\n<li>Provides a stdlib of layout helpers, including <code>layout python<\/code>, <code>layout python3<\/code>, <code>layout pyenv<\/code>, and <code>layout pipenv<\/code><\/li>\n<li>Supports bash, zsh, fish, tcsh, elvish, and PowerShell<\/li>\n<li>Requires explicit per-directory authorization with <code>direnv allow<\/code> before any new <code>.envrc<\/code> runs<\/li>\n<\/ul>\n<h2>Installation<span class=\"hx:absolute hx:-mt-20\" id=\"installation\"><\/span>\n    <a href=\"#installation\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>direnv is distributed through most package managers:<\/p>"},{"title":"Modern Python Project Setup Guide for AI Assistants","link":"https:\/\/pydevtools.com\/handbook\/explanation\/modern-python-project-setup-guide-for-ai-assistants\/","pubDate":"Thu, 04 Jun 2026 04:50:21 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/modern-python-project-setup-guide-for-ai-assistants\/","description":"<div class=\"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-purple-200 hx:bg-purple-100 hx:text-purple-900 hx:dark:border-purple-200\/30 hx:dark:bg-purple-900\/30 hx:dark:text-purple-200\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Important<\/p>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\"><p>For AI assistants: this guide is meant to be ingested directly. Paste it (or link it) into your project&rsquo;s <code>CLAUDE.md<\/code>, <code>AGENTS.md<\/code>, <code>.cursorrules<\/code>, or <code>.github\/copilot-instructions.md<\/code> so coding agents follow the same conventions you would. A <a href=\"index.md\">plain markdown version<\/a> is available for direct ingestion into agent contexts.<\/p>"},{"title":"Sphinx: Python Documentation Generator","link":"https:\/\/pydevtools.com\/handbook\/reference\/sphinx\/","pubDate":"Wed, 03 Jun 2026 02:50:43 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/sphinx\/","description":"<p>Sphinx is a documentation generator that produces HTML, PDF, ePub, and other formats from source files written in <a href=\"https:\/\/docutils.sourceforge.io\/rst.html\"target=\"_blank\" rel=\"noopener\">reStructuredText<\/a> or <a href=\"https:\/\/myst-parser.readthedocs.io\/\"target=\"_blank\" rel=\"noopener\">Markdown<\/a>. It powers the official <a href=\"https:\/\/docs.python.org\/\"target=\"_blank\" rel=\"noopener\">Python documentation<\/a> and is the default documentation tool across most of the scientific Python ecosystem.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Note<\/p>"},{"title":"Read the Docs: Documentation Hosting for Python Projects","link":"https:\/\/pydevtools.com\/handbook\/reference\/read-the-docs\/","pubDate":"Wed, 03 Jun 2026 02:50:43 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/read-the-docs\/","description":"<p>Read the Docs is a hosted documentation platform that watches a Git repository for pushes and automatically builds, versions, and serves the resulting documentation at a stable public URL. It hosts over 100,000 open-source projects, including Flask, Jupyter, and Godot.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Note<\/p>"},{"title":"MkDocs: Markdown Documentation for Python Projects","link":"https:\/\/pydevtools.com\/handbook\/reference\/mkdocs\/","pubDate":"Wed, 03 Jun 2026 02:50:43 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/mkdocs\/","description":"<p>MkDocs is a static site generator that builds documentation websites from Markdown source files. A project&rsquo;s structure, navigation, theme, and plugins are declared in a single <code>mkdocs.yml<\/code> file at the repo root.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Note<\/p>"},{"title":"pyproject-fmt: Opinionated pyproject.toml Formatter","link":"https:\/\/pydevtools.com\/handbook\/reference\/pyproject-fmt\/","pubDate":"Tue, 02 Jun 2026 07:51:36 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/pyproject-fmt\/","description":"<p>pyproject-fmt is an opinionated formatter for <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyproject.toml\/\"><code>pyproject.toml<\/code><\/a> files. It applies packaging-aware rules that a general TOML formatter cannot: sorting dependency lists, normalizing version specifiers and package names, reordering tables into the canonical <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pep-621-compatibility\/\">PEP 621 project metadata<\/a> order, and generating Python version classifiers from <code>requires-python<\/code>. It is maintained by the tox-dev organization in the <code>toml-fmt<\/code> monorepo.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 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\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">pyproject-fmt only formats <code>pyproject.toml<\/code>. For arbitrary TOML files, use a general formatter like <a href=\"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-format-pyproject-toml-with-taplo\/\"target=\"_blank\" rel=\"noopener\">taplo<\/a>.<\/div>\n  <\/div>\n<\/div>\n\n<h2>When to use pyproject-fmt<span class=\"hx:absolute hx:-mt-20\" id=\"when-to-use-pyproject-fmt\"><\/span>\n    <a href=\"#when-to-use-pyproject-fmt\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Use pyproject-fmt when a project&rsquo;s <code>pyproject.toml<\/code> should follow one packaging-aware style without per-project debate. It takes the same opinionated stance as <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a>&rsquo;s formatter or Black: a small set of options and a single canonical output, which keeps diffs small and reviews focused. It complements rather than replaces a code formatter, since it touches only the project manifest and leaves Python source untouched.<\/p>"},{"title":"How to format pyproject.toml with taplo","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-format-pyproject-toml-with-taplo\/","pubDate":"Tue, 02 Jun 2026 07:51:36 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-format-pyproject-toml-with-taplo\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> does not include a built-in formatter for <code>pyproject.toml<\/code>. <a href=\"https:\/\/taplo.tamasfe.dev\/\"target=\"_blank\" rel=\"noopener\">Taplo<\/a> is a TOML formatter and validator that keeps <code>pyproject.toml<\/code> files consistently styled across a project.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"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\"\/><\/svg>Tip<\/p>"},{"title":"Why Installing GPU Python Packages Is So Complicated","link":"https:\/\/pydevtools.com\/handbook\/explanation\/installing-cuda-python-packages\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/installing-cuda-python-packages\/","description":"<p>Python <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/wheel\/\">wheels<\/a> have no way to express GPU hardware requirements. A wheel&rsquo;s three metadata tags (Python version, ABI, and platform) tell an installer everything about the CPU environment, but nothing about the GPU or which CUDA version is available.<\/p>\n<p>PyTorch ships different binaries for CUDA 12.6, 12.8, 13.0, and CPU-only builds. Without GPU metadata in wheels, it has no standard way to publish all four variants to <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pypi\/\">PyPI<\/a> and let installers pick the right one. Every CUDA-dependent project has had to invent its own distribution workaround.<\/p>"},{"title":"What is a Python language server?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-python-language-server\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-python-language-server\/","description":"<p>Type a dot after a variable in your editor and a list of methods appears. Hover a function and its signature shows up in a tooltip. Right-click an import and &ldquo;Go to definition&rdquo; jumps to the source. Most Python developers have used these features for years without knowing the name of the program behind them.<\/p>\n<p>That program is a <strong>language server<\/strong>. It is a separate process running alongside your editor, and it is doing most of the work readers usually credit to &ldquo;the editor.&rdquo; Naming it explains why VS Code, Neovim, Cursor, and Zed give you the same autocomplete on the same file, and why a faster type checker makes your editor feel faster.<\/p>"},{"title":"What Are Wheel Variants?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/what-are-wheel-variants\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/what-are-wheel-variants\/","description":"<p>A default NumPy <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/wheel\/\">wheel<\/a> for <code>x86_64<\/code> has to run on every x86-64 CPU that has ever shipped, with the baseline instruction set fixed when AMD64 was defined in 2003. Every SIMD instruction Intel and AMD have added since then is off-limits to the installer, because a wheel filename has no place to declare &ldquo;needs AVX2&rdquo; or &ldquo;needs SSE4&rdquo;. On scientific workloads, Ralf Gommers of Quansight put the performance left on the table at <a href=\"https:\/\/talkpython.fm\/episodes\/show\/544\/wheel-next-packaging-peps\"target=\"_blank\" rel=\"noopener\">10x to 20x<\/a>.<\/p>"},{"title":"uv vs pixi vs conda for Scientific Python","link":"https:\/\/pydevtools.com\/handbook\/explanation\/uv-vs-pixi-vs-conda-for-scientific-python\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/uv-vs-pixi-vs-conda-for-scientific-python\/","description":"<p>Your scientific Python project needs CUDA, GDAL, or HDF5, and <code>pip install<\/code> cannot help. Which tool should manage those native dependencies alongside your Python code? The answer depends on where your packages live: <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-pypi\/\">PyPI<\/a>, conda-forge, or both.<\/p>\n<p>Three tools cover the spectrum. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> installs from PyPI. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pixi\/\">pixi<\/a> installs from conda-forge and PyPI together. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/conda\/\">conda<\/a> installs from conda channels.<\/p>\n<h2>What each tool can install<span class=\"hx:absolute hx:-mt-20\" id=\"what-each-tool-can-install\"><\/span>\n    <a href=\"#what-each-tool-can-install\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>uv resolves packages from PyPI, plus Git URLs, local paths, and private indexes. It cannot install non-Python dependencies like the CUDA toolkit, GDAL, or HDF5.<\/p>"},{"title":"ty: Python Type Checker by OpenAI","link":"https:\/\/pydevtools.com\/handbook\/reference\/ty\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/ty\/","description":"<p>ty (pronounced <em>tee-why<\/em>) is a static type checker and language server for Python from Astral, the creators of <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>, now part of OpenAI. It performs static analysis on Python code to identify type-related issues before runtime.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 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\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">ty was initially known by the code name &ldquo;Red-Knot&rdquo; during its early development phase. The tool is in beta, with a stable 1.0 release targeted for 2026.<\/div>\n  <\/div>\n<\/div>\n\n<h2>When to use ty<span class=\"hx:absolute hx:-mt-20\" id=\"when-to-use-ty\"><\/span>\n    <a href=\"#when-to-use-ty\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>ty is an alternative to the handbook&rsquo;s recommended type checker, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyrefly\/\">Pyrefly<\/a>. Its &ldquo;gradual guarantee&rdquo; ensures that adding type annotations to working code never introduces new errors, which makes incremental adoption predictable in a partially-typed codebase. ty also runs 10-100x faster than <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/mypy\/\">mypy<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyright\/\">pyright<\/a> and pairs cleanly with <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> in the OpenAI toolchain. Pyrefly is recommended over ty for most new projects because ty is still in beta as of 2026 and its typing-spec conformance trails Pyrefly&rsquo;s; choose ty if the gradual guarantee matters more than feature completeness, or if OpenAI-toolchain coherence is a priority. For a full comparison across all major type checkers, see <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-mypy-pyright-and-ty-compare\/\">How do Python type checkers compare?<\/a>.<\/p>"},{"title":"Set up a Python project optimized for Claude Code","link":"https:\/\/pydevtools.com\/handbook\/tutorial\/set-up-a-python-project-for-claude-code\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/tutorial\/set-up-a-python-project-for-claude-code\/","description":"<p><a href=\"https:\/\/claude.com\/product\/claude-code\"target=\"_blank\" rel=\"noopener\">Claude Code<\/a> writes Python fluently, but without project-specific configuration it falls back on <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pip\/\">pip<\/a> and bare <code>python<\/code> instead of <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>. This tutorial builds a Python project with uv, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a>, and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pytest\/\">pytest<\/a>, then layers on Claude Code integration: a CLAUDE.md file that teaches Claude your conventions and hooks that enforce them, plus on-demand skills for tool expertise.<\/p>\n<h2>Prerequisites<span class=\"hx:absolute hx:-mt-20\" id=\"prerequisites\"><\/span>\n    <a href=\"#prerequisites\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<ul>\n<li><a href=\"https:\/\/docs.astral.sh\/uv\/getting-started\/installation\/\"target=\"_blank\" rel=\"noopener\">uv installed<\/a><\/li>\n<li><a href=\"https:\/\/code.claude.com\/docs\/en\/overview\"target=\"_blank\" rel=\"noopener\">Claude Code installed<\/a><\/li>\n<li><a href=\"https:\/\/jqlang.github.io\/jq\/\"target=\"_blank\" rel=\"noopener\">jq<\/a> installed (used by one hook script)<\/li>\n<\/ul>\n<h2>Create the project<span class=\"hx:absolute hx:-mt-20\" id=\"create-the-project\"><\/span>\n    <a href=\"#create-the-project\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Scaffold a new Python package:<\/p>"},{"title":"Ruff: Python Linter and Formatter","link":"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/","description":"<p>Ruff is a fast Python linter and code formatter from <a href=\"https:\/\/astral.sh\/\"target=\"_blank\" rel=\"noopener\">OpenAI<\/a> (the makers of <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ty\/\">ty<\/a>). It replaces multiple Python code quality tools (like flake8, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/black\/\">Black<\/a>, isort, and pyupgrade) with a single binary.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 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\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">Ruff includes over 900 built-in lint rules and can replace functionality from dozens of Python code quality tools.<\/div>\n  <\/div>\n<\/div>\n\n<h2>When to use Ruff<span class=\"hx:absolute hx:-mt-20\" id=\"when-to-use-ruff\"><\/span>\n    <a href=\"#when-to-use-ruff\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Use Ruff when you want a single, fast tool for linting and formatting Python code instead of maintaining separate configurations for <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/flake8\/\">flake8<\/a>, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/black\/\">Black<\/a>, isort, and other code quality tools. It is a strong default for any Python project, from small scripts to large monorepos, because it handles linting, formatting, and import sorting in one pass with minimal configuration. If you need deeper static analysis beyond what lint rules provide, pair Ruff with a type checker like <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/mypy\/\">mypy<\/a> or <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyright\/\">pyright<\/a>; for a comparison with traditional linting, see <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-ruff-and-pylint-compare\/\">How do Ruff and Pylint compare?<\/a>. For a full walkthrough of rule categories, configuration, and migrating an existing project, see <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/ruff-complete-guide\/\">Ruff: a complete guide<\/a>.<\/p>"},{"title":"How to write Claude Code hooks for Python projects","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-write-claude-code-hooks-for-python-projects\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-write-claude-code-hooks-for-python-projects\/","description":"<p><a href=\"https:\/\/code.claude.com\/\"target=\"_blank\" rel=\"noopener\">Claude Code<\/a> hooks are scripts that run at specific points during a session, letting you enforce project conventions programmatically. A <a href=\"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-claude-code-to-use-uv\/\">CLAUDE.md file<\/a> suggests how Claude should behave; hooks guarantee it by blocking disallowed commands, auto-formatting files, or injecting context before Claude sees your prompt. See the <a href=\"https:\/\/code.claude.com\/docs\/en\/hooks\"target=\"_blank\" rel=\"noopener\">official hooks documentation<\/a> for the full reference.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"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\"\/><\/svg>Tip<\/p>"},{"title":"How to use uv with VS Code devcontainers","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-use-uv-with-vs-code-devcontainers\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-use-uv-with-vs-code-devcontainers\/","description":"<p>A devcontainer hands every teammate the same Python interpreter and the same locked dependencies. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> makes that hand-off cheap: a single <code>uv sync --locked<\/code> reads <code>uv.lock<\/code> and installs the resolved graph in seconds, on every machine.<\/p>\n<p>This guide configures a VS Code devcontainer that uses uv to materialize the project&rsquo;s <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-virtual-environment\/\">virtual environment<\/a> inside the container, points VS Code at the in-container interpreter, and uses the <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/what-is-a-lock-file\/\">lockfile<\/a> to keep team environments in sync.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg>Note<\/p>"},{"title":"How to Use uv on Windows ARM64","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-use-uv-on-windows-arm64\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-use-uv-on-windows-arm64\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> ships a native Windows ARM64 binary, but <code>uv python install 3.13<\/code> still installs x86_64 CPython by default on Snapdragon machines. That is deliberate: x86_64 wheels still cover more packages on Windows on ARM, and Windows transparently runs them under emulation. Install the native uv binary first, then choose whether this project should stay on emulated x86_64 or move to a native <code>3.13-aarch64<\/code> interpreter.<\/p>\n<h2>Confirm you are on Windows ARM64<span class=\"hx:absolute hx:-mt-20\" id=\"confirm-you-are-on-windows-arm64\"><\/span>\n    <a href=\"#confirm-you-are-on-windows-arm64\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Open PowerShell and check the architecture:<\/p>"},{"title":"How to use Python skills with Claude Code","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-use-python-skills-with-claude-code\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-use-python-skills-with-claude-code\/","description":"<p>Skills bottle up your Python workflow. Instead of re-explaining &ldquo;tighten these type annotations&rdquo; or &ldquo;use <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> instead of pip&rdquo; every session, save the procedure to a markdown file once and let <a href=\"https:\/\/code.claude.com\/\"target=\"_blank\" rel=\"noopener\">Claude Code<\/a> reach for it when you ask, either by slash command or by describing the task in plain English. Long reference material that would crowd a <a href=\"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-use-the-pydevtools-claude-md-template\/\">CLAUDE.md<\/a> only loads when the skill runs.<\/p>\n<p>This guide installs OpenAI&rsquo;s Astral plugin, walks through writing a custom Python skill, and shows when a skill beats CLAUDE.md or a hook.<\/p>"},{"title":"How to try the ty type checker","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-try-the-ty-type-checker\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-try-the-ty-type-checker\/","description":"<p>This guide shows you how to try out <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ty\/\">ty<\/a>, a static type checker and language server, in your project.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 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\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">Looking to migrate from <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/mypy\/\"target=\"_blank\" rel=\"noopener\">mypy<\/a> to ty? See <a href=\"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-migrate-from-mypy-to-ty\/\"target=\"_blank\" rel=\"noopener\">How to migrate from mypy to ty<\/a> for detailed guidance.<\/div>\n  <\/div>\n<\/div>\n\n<h2>Prerequisites<span class=\"hx:absolute hx:-mt-20\" id=\"prerequisites\"><\/span>\n    <a href=\"#prerequisites\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>To proceed, you&rsquo;ll need <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>.<\/p>"},{"title":"How to migrate from uv to pip","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-migrate-from-uv-to-pip\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-migrate-from-uv-to-pip\/","description":"<p>Moving a uv-managed project to <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pip\/\">pip<\/a> is mostly an audit of <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyproject.toml\/\">pyproject.toml<\/a>, not a rebuild of the project. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> consolidates Python interpreter installs, virtual environments, resolution, package installation, and lockfile generation into one tool, but if a project&rsquo;s metadata sticks to packaging standards, most of the substitutes are short. This how-to walks through the audit and the runtime equivalents: <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyenv\/\">pyenv<\/a> on macOS and Linux, the official python.org installer on Windows.<\/p>\n<h2>Audit your pyproject.toml for uv-specific tables<span class=\"hx:absolute hx:-mt-20\" id=\"audit-your-pyprojecttoml-for-uv-specific-tables\"><\/span>\n    <a href=\"#audit-your-pyprojecttoml-for-uv-specific-tables\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Before exporting a lockfile, scan your <code>pyproject.toml<\/code> for settings under <code>[tool.uv]<\/code> and tables under <code>[tool.uv.*]<\/code>. Anything outside that namespace is already standardized and pip can read it. The settings that matter:<\/p>"},{"title":"How to migrate from pyright to ty","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-migrate-from-pyright-to-ty\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-migrate-from-pyright-to-ty\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ty\/\">ty<\/a> is a Python type checker developed by Astral, the creators of <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a>, now part of OpenAI. It ships as a single binary with no runtime dependencies, which eliminates <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyright\/\">pyright<\/a>&rsquo;s Node.js requirement.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-amber-200 hx:bg-amber-100 hx:text-amber-900 hx:dark:border-amber-200\/30 hx:dark:bg-amber-900\/30 hx:dark:text-amber-200\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">ty is currently in beta. While actively developed, it&rsquo;s still missing some features and may not be ready for full production adoption. This guide helps evaluate readiness and plan for migration.<\/div>\n  <\/div>\n<\/div>\n\n<h2>Installation and basic usage<span class=\"hx:absolute hx:-mt-20\" id=\"installation-and-basic-usage\"><\/span>\n    <a href=\"#installation-and-basic-usage\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<h3>Installing ty<span class=\"hx:absolute hx:-mt-20\" id=\"installing-ty\"><\/span>\n    <a href=\"#installing-ty\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h3>\n<p>ty is distributed as a standalone binary. The easiest way to use it is via <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uvx<\/a>:<\/p>"},{"title":"How to migrate from mypy to ty","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-migrate-from-mypy-to-ty\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-migrate-from-mypy-to-ty\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ty\/\">ty<\/a> is a fast Python type checker, developed by Astral, the creators of <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a>, now part of OpenAI. It runs faster than <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/mypy\/\">mypy<\/a>, especially on large codebases.<\/p>\n<div class=\"hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-amber-200 hx:bg-amber-100 hx:text-amber-900 hx:dark:border-amber-200\/30 hx:dark:bg-amber-900\/30 hx:dark:text-amber-200\">\n  <div class=\"hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2\"><svg height=1.2em class=\"hx:inline-block hx:align-middle\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\/><\/svg><\/div>\n\n  <div class=\"hx:w-full hx:min-w-0 hx:leading-7\">\n    <div class=\"hx:mt-6 hx:leading-7 hx:first:mt-0\">ty is currently in beta. While actively developed, it&rsquo;s still missing some features and may not be ready for full production adoption. This guide helps evaluate readiness and plan for migration.<\/div>\n  <\/div>\n<\/div>\n\n<h2>Understanding ty<span class=\"hx:absolute hx:-mt-20\" id=\"understanding-ty\"><\/span>\n    <a href=\"#understanding-ty\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<h3>When to consider migrating<span class=\"hx:absolute hx:-mt-20\" id=\"when-to-consider-migrating\"><\/span>\n    <a href=\"#when-to-consider-migrating\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h3>\n<ul>\n<li>Projects needing faster type checking on large codebases<\/li>\n<li>Teams already using other OpenAI tools (Ruff, uv)<\/li>\n<li>Projects not relying on mypy plugins<\/li>\n<\/ul>\n<h3>When to wait<span class=\"hx:absolute hx:-mt-20\" id=\"when-to-wait\"><\/span>\n    <a href=\"#when-to-wait\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h3>\n<ul>\n<li>Projects requiring Pydantic, Django, or SQLAlchemy mypy plugins<\/li>\n<li>Projects requiring mature, battle-tested tooling<\/li>\n<\/ul>\n<h2>Installation and basic usage<span class=\"hx:absolute hx:-mt-20\" id=\"installation-and-basic-usage\"><\/span>\n    <a href=\"#installation-and-basic-usage\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<h3>Installing ty<span class=\"hx:absolute hx:-mt-20\" id=\"installing-ty\"><\/span>\n    <a href=\"#installing-ty\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h3>\n<p>ty is distributed as a standalone binary. The easiest way to use it is via <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uvx<\/a>:<\/p>"},{"title":"How to install uv","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-install-uv\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-install-uv\/","description":"<p>You can install <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a>, the fast Python package and project manager, with a standalone installer, with pip, or with package managers like Homebrew, WinGet, and Scoop. The recommended method on every platform is the official standalone installer, because it requires no existing Python interpreter, no package manager, and no other prerequisites.<\/p>\n<p>You do not need Python installed first. The standalone installer downloads a self-contained uv binary, and uv can then install and manage Python interpreters for you.<\/p>"},{"title":"How to install OpenAI's Astral plugins for Claude Code","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-install-astral-plugins-for-claude-code\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-install-astral-plugins-for-claude-code\/","description":"<p>OpenAI publishes the official <a href=\"https:\/\/github.com\/astral-sh\/claude-code-plugins\"target=\"_blank\" rel=\"noopener\">Astral plugin for Claude Code<\/a> that bundles three skills (<code>\/astral:uv<\/code>, <code>\/astral:ruff<\/code>, <code>\/astral:ty<\/code>) and a <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ty\/\">ty<\/a> language server. The skills give Claude up-to-date usage guidance for each tool, and the language server feeds live type-checking diagnostics into the conversation.<\/p>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"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\"\/><\/svg>Tip<\/p>"},{"title":"How to configure Ruff with Claude Code","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-ruff-with-claude-code\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-ruff-with-claude-code\/","description":"<p><a href=\"https:\/\/code.claude.com\/\"target=\"_blank\" rel=\"noopener\">Claude Code<\/a> does not run <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a> by default, so Python edits land unformatted and lint errors slip through unless you wire Ruff in yourself. Out of the box, Claude often calls bare <code>ruff<\/code> (which may resolve outside the project&rsquo;s virtual environment) and skips formatting after edits. Wiring Ruff in correctly takes four layers, each with a different job.<\/p>\n<p>This guide covers all four and how they stack together:<\/p>\n<ul>\n<li>A CLAUDE.md instruction that points Claude at Ruff<\/li>\n<li>OpenAI&rsquo;s Astral plugin&rsquo;s <code>\/astral:ruff<\/code> skill for on-demand Ruff guidance<\/li>\n<li>A PostToolUse hook that auto-formats every Python edit<\/li>\n<li>A <a href=\"https:\/\/pre-commit.com\/\"target=\"_blank\" rel=\"noopener\">pre-commit<\/a> gate that catches anything Claude missed before commit time<\/li>\n<\/ul>\n<div class=\"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\">\n  <p class=\"hx:flex hx:items-center hx:font-medium\"><svg height=16px class=\"hx:inline-block hx:align-middle hx:mr-2\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" aria-hidden=\"true\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"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\"\/><\/svg>Tip<\/p>"},{"title":"How to configure Claude Code with a Python type checker","link":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-claude-code-with-a-python-type-checker\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/how-to\/how-to-configure-claude-code-with-a-python-type-checker\/","description":"<p><a href=\"https:\/\/code.claude.com\/\"target=\"_blank\" rel=\"noopener\">Claude Code<\/a> does not run a Python type checker by default, and the gap is wider than it looks. Listing <code>uv run ty check<\/code> in <code>CLAUDE.md<\/code> looks like enough configuration, but <a href=\"https:\/\/pyrefly.org\/blog\/pyrefly-agentic-loop\/\"target=\"_blank\" rel=\"noopener\">Pyrefly&rsquo;s documented agentic-loop pattern<\/a> shows that models routinely skip the command unless the type checker is wrapped in a loop that runs after every edit. With the wrapper in place, the model engages with errors; without it, the model treats the checker as optional.<\/p>"},{"title":"How Python tools adopt uv under the hood","link":"https:\/\/pydevtools.com\/handbook\/explanation\/how-python-tools-adopt-uv-under-the-hood\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/how-python-tools-adopt-uv-under-the-hood\/","description":"<p>You don&rsquo;t have to switch to <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> to get uv&rsquo;s speed. <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pixi\/\">Pixi<\/a>, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/hatch\/\">Hatch<\/a>, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pdm\/\">PDM<\/a>, and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/tox\/\">tox<\/a> now use uv&rsquo;s resolver and installer as their backend, which means your existing tool may already run on uv under the hood.<\/p>\n<h2>Which tools use uv<span class=\"hx:absolute hx:-mt-20\" id=\"which-tools-use-uv\"><\/span>\n    <a href=\"#which-tools-use-uv\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<table>\n\t<thead>\n\t\t\t<tr>\n\t\t\t\t\t<th>Tool<\/th>\n\t\t\t\t\t<th>What it delegates to uv<\/th>\n\t\t\t\t\t<th>How to enable<\/th>\n\t\t\t<\/tr>\n\t<\/thead>\n\t<tbody>\n\t\t\t<tr>\n\t\t\t\t\t<td><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pixi\/\">Pixi<\/a><\/td>\n\t\t\t\t\t<td>PyPI dependency resolution and installation<\/td>\n\t\t\t\t\t<td>Automatic (default behavior)<\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/hatch\/\">Hatch<\/a><\/td>\n\t\t\t\t\t<td>Dependency installation and venv creation<\/td>\n\t\t\t\t\t<td><code>installer = &quot;uv&quot;<\/code> in env config<\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pdm\/\">PDM<\/a><\/td>\n\t\t\t\t\t<td>Dependency resolution and installation<\/td>\n\t\t\t\t\t<td><code>pdm config use_uv true<\/code> (experimental)<\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/tox\/\">tox<\/a> (via <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/tox-uv\/\">tox-uv<\/a>)<\/td>\n\t\t\t\t\t<td>Dependency installation and venv creation<\/td>\n\t\t\t\t\t<td>Install the <code>tox-uv<\/code> plugin<\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/nox\/\">nox<\/a><\/td>\n\t\t\t\t\t<td>Dependency installation<\/td>\n\t\t\t\t\t<td>Enable <a href=\"https:\/\/nox.thea.codes\/en\/stable\/config.html#configuring-a-session-s-virtualenv\"target=\"_blank\" rel=\"noopener\">nox&rsquo;s uv backend<\/a><\/td>\n\t\t\t<\/tr>\n\t\t\t<tr>\n\t\t\t\t\t<td><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/prek\/\">prek<\/a><\/td>\n\t\t\t\t\t<td>Python version and venv management for hooks<\/td>\n\t\t\t\t\t<td>Automatic<\/td>\n\t\t\t<\/tr>\n\t<\/tbody>\n<\/table>\n<p>Pixi uses uv by default for all PyPI packages. Prek does the same for hook environments. The rest require opt-in configuration.<\/p>"},{"title":"How do uv and Poetry compare?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-uv-and-poetry-compare\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-uv-and-poetry-compare\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/poetry\/\">Poetry<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/uv\/\">uv<\/a> both manage Python projects through <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyproject.toml\/\">pyproject.toml<\/a>, create lockfiles, and handle virtual environments. Poetry has been the default recommendation for years; uv, built by Astral and now owned by OpenAI, arrived in 2024 with broader scope and faster dependency resolution. They differ in how.<\/p>\n<h2>Scope<span class=\"hx:absolute hx:-mt-20\" id=\"scope\"><\/span>\n    <a href=\"#scope\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<p>Poetry handles dependency management, virtual environments, building, and publishing. Everything else it delegates. A different Python version requires <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyenv\/\">pyenv<\/a>. Running a CLI tool without installing it globally requires <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pipx\/\">pipx<\/a>.<\/p>"},{"title":"How do Ruff and Pylint compare?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-ruff-and-pylint-compare\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-ruff-and-pylint-compare\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ruff\/\">Ruff<\/a> and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pylint\/\">Pylint<\/a> are Python code analysis tools with overlapping but distinct capabilities.<\/p>\n<h2>Core Differences<span class=\"hx:absolute hx:-mt-20\" id=\"core-differences\"><\/span>\n    <a href=\"#core-differences\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h2>\n<h3>Speed and Architecture<span class=\"hx:absolute hx:-mt-20\" id=\"speed-and-architecture\"><\/span>\n    <a href=\"#speed-and-architecture\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h3>\n<p>Ruff&rsquo;s Rust implementation delivers dramatically faster performance than Pylint&rsquo;s Python codebase. This speed advantage becomes particularly noticeable in large codebases where Ruff can analyze files orders of magnitude faster.<\/p>\n<h3>Rule Coverage<span class=\"hx:absolute hx:-mt-20\" id=\"rule-coverage\"><\/span>\n    <a href=\"#rule-coverage\" class=\"subheading-anchor\" aria-label=\"Permalink for this section\"><\/a><\/h3>\n<p>While Pylint implements approximately 409 rules, Ruff now exceeds 1,000 total rules, with around 209 overlapping with Pylint&rsquo;s ruleset. However, these numbers continue to change, especially as Ruff undergoes rapid development.<\/p>"},{"title":"How do Python type checkers compare?","link":"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-mypy-pyright-and-ty-compare\/","pubDate":"Tue, 02 Jun 2026 07:32:07 -0400","author":"Tim Hopper","guid":"https:\/\/pydevtools.com\/handbook\/explanation\/how-do-mypy-pyright-and-ty-compare\/","description":"<p><a href=\"https:\/\/pydevtools.com\/handbook\/reference\/mypy\/\">mypy<\/a>, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyright\/\">pyright<\/a>, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/ty\/\">ty<\/a>, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/pyrefly\/\">Pyrefly<\/a>, <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/basedpyright\/\">Basedpyright<\/a>, and <a href=\"https:\/\/pydevtools.com\/handbook\/reference\/zuban\/\">Zuban<\/a> all analyze type annotations to catch bugs before runtime. They read the same type-hint annotations (from <a href=\"https:\/\/peps.python.org\/pep-0484\/\"target=\"_blank\" rel=\"noopener\">PEP 484<\/a>, the standard that introduced Python&rsquo;s type system in 2014) and largely agree on the parts of the spec a working program uses. They disagree on speed, default strictness, treatment of unannotated code, editor integration, and licensing, and those disagreements are what make the choice matter.<\/p>\n<p>None of these tools lint style or format code; that is the job of a linter and formatter like <a href=\"https:\/\/pydevtools.com\/handbook\/explanation\/ruff-complete-guide\/\">Ruff<\/a>. Type checking and linting catch different classes of bugs, so most projects run both: Ruff for style, imports, and common mistakes, and one of these checkers for type errors.<\/p>"}]}}