<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>mattesilver</title>
    <link>https://qua.name/mattesilver/</link>
    <description></description>
    <pubDate>Sat, 18 Apr 2026 13:04:15 +0200</pubDate>
    <item>
      <title>Managing development tools</title>
      <link>https://qua.name/mattesilver/managing-development-tools</link>
      <description>&lt;![CDATA[A set of workshop tools&#xA;&#xA;I&#39;ve been looking for a tool to manage development tools like linters, test runners, etc. The tools are typically development tools, so for example pipx would qualify.&#xA;&#xA;A tool manager should make the environment reproducible and portable. The installed tools should work not only on my machine as well as on machine of everyone else who works on the same project, and they should not conflict with other tools anyone has installed on their systems.&#xA;&#xA;Ideally the tool would have a declarative configuration, so it&#39;s clear what&#39;s used and easy to upgrade.&#xA;&#xA;The problem with task managers (that aren&#39;t also tool managers) is that they don&#39;t control the tools, so a task that works on one system may fail on another. Worse, a task that works at one point in time may fail at another, because of a new tool version. So task manager should be either also a tool manager or it should be used with one.&#xA;&#xA;On the other hand, managing tools is also a big subject, it&#39;s pretty much a package manager, needing some sort of a descriptor for a lot of tools in various languages, that need to be installable on various systems and architectures.&#xA;&#xA;In short, the requirements boil down to:&#xA;&#xA;declarative tool SBOM&#xA;tool isolation (but with access to project dependencies)&#xA;task management&#xA;&#xA;Installing tools directly in the system&#xA;&#xA;The old way, where the user installs tools following the project readme. Not declarative, not reproducible, definitely not isolated.&#xA;&#xA;❌ declarative&#xA;❌ isolated&#xA;❌ task management&#xA;&#xA;Declarative package managers - Nix, Guix&#xA;&#xA;Environments are reproducible, declarative (both use functional languages). Rather difficult to use and very intrusive - you pretty much install another operating system in your operating system. I don&#39;t see any technical obstacle from making them single-user application, but they chose against it. Unless you already use one of them, it&#39;s a total overkill.&#xA;&#xA;Maybe they work better with dev containers, but even then there are  alternatives.&#xA;&#xA;✅ declarative&#xA;✅ isolated&#xA;❌ task management&#xA;&#xA;pipx&#xA;&#xA;pip for executable packages. Supports installation of multiple versions of the same tool, but there&#39;s no way to activate the required versions for a given project. And it only works for python tools (and those with a python wrapper).&#xA;&#xA;❌ declarative&#xA;❌ isolated (tools are isolated from each other, but shared across projects)&#xA;❌ task management&#xA;&#xA;uvx, npx, pnpx, etc&#xA;&#xA;Set of tools that let you run their respective languages projects as programs. Each works only with tools in its language, but it&#39;s not the biggest problem, since typically the tools are written in the same language, or have wrappers.&#xA;&#xA;They arent really designed to manage a toolbox, rather for one-off execution. There&#39;s no declarative versioning.&#xA;&#xA;uvx is a part of uv, which is a package manager, and also python version manager, but somehow tool manager is out of scope 🤷.&#xA;&#xA;❌ declarative&#xA;✅ isolated&#xA;❌ task management&#xA;&#xA;Tools as dev dependencies&#xA;&#xA;It&#39;s really not what dev dependencies are meant for: everything is installed in a shared environment (venv, nodemodules). In the nodejs environment this should be fine, every package maintains its own dependencies, even if versions differ - that&#39;s why you end up with huge nodemodules. In python you might end up with conflicting dependencies (this somehow doesn&#39;t stop poetry project to use dev dependency group this way). Again, they only work for tools written in the same language as the project.&#xA;&#xA;✅ declarative&#xA;❌ isolated&#xA;❓ task management (not in python, package.json has scripts)&#xA;&#xA;pre-commit&#xA;&#xA;Technically ticks many boxes, but not really intended as a dev tool manager.&#xA;&#xA;Uses git repositories as a distributed source of packages, keeps them isolated and cached per project. Versions are fully declarative - you can even pin full SHA hashes.&#xA;&#xA;This is all great, but it&#39;s literal git&#39;s pre-commit utility, which means it&#39;s designed for fast and predictable execution. It&#39;s not intended as a general purpose tool manager - it doesn&#39;t provide the tools with full project environment (it doesn&#39;t include dependencies), it doesn&#39;t support test runners (also because testing is too slow for a pre-commit hook) and even support for some linters (see pythons mypy) is suboptimal (again, mypy works best with access to dependencies).&#xA;&#xA;✅ declarative&#xA;✅ isolated (actually too isolated for my purpose)&#xA;❌ task management&#xA;&#xA;Mise&#xA;Works both as a package manager (tools) and task manager (replacement for package.json scripts, rather than for make). The only tool with the express goal of being a tool and task manager.&#xA;&#xA;It doesn&#39;t handle tools with plugins well (e.g. pydantic.mypy) - it uses a shared directory for installs, but only tool name and version are used for tools &#34;identity&#34;. which means if you have two projects, both using mypy, one using pydantic with mypy plugin, there&#39;s a conflict (there&#39;s a workaround to this, but not good for collaboration).&#xA;&#xA;It also does funny things to $PATH so it doesn&#39;t work great with other tools, like IDEs or pre-commit.&#xA;&#xA;✅ declarative&#xA;❌ isolated (buggy)&#xA;✅ task management&#xA;&#xA;Dev containers&#xA;&#xA;This isn&#39;t even a tool, it&#39;s simply a container (docker, podman, you name it) that has your tools installed and has access to your project directory.&#xA;&#xA;It&#39;s great for isolation and reproducibility, as long your installs are versioned. The isolation makes curl | sudo sh - a bit more acceptable.&#xA;&#xA;I haven&#39;t worked with it, but the concept seems very reasonable, especially for occasional build of applications that you only use (but not develop).&#xA;&#xA;Conclusion&#xA;&#xA;I don&#39;t see here a perfect tool, and I&#39;m not even looking for a polyglot multi-project / monorepo manager.&#xA;&#xA;Mise comes very close, and I&#39;m currently using it but it has a problem with isolation and it doesn&#39;t seem interested in fixing it, so I wouldn&#39;t recommend it on it&#39;s own in a collaborative environment.&#xA;&#xA;I&#39;ll take a closer look at mise in a dev container. Dev containers would provide the isolation and mise covers declarative tool and task management quite well.&#xA;&#xA;CC BY-NC-SA 4.0]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1640682841767-cdfce3aea6e0" alt="A set of workshop tools"></p>

<p>I&#39;ve been looking for a tool to manage development tools like linters, test runners, etc. The tools are typically development tools, so for example pipx would qualify.</p>

<p>A tool manager should make the environment reproducible and portable. The installed tools should work not only on my machine as well as on machine of everyone else who works on the same project, and they should not conflict with other tools anyone has installed on their systems.</p>

<p>Ideally the tool would have a declarative configuration, so it&#39;s clear what&#39;s used and easy to upgrade.</p>

<p>The problem with task managers (that aren&#39;t also tool managers) is that they don&#39;t control the tools, so a task that works on one system may fail on another. Worse, a task that works at one point in time may fail at another, because of a new tool version. So task manager should be either also a tool manager or it should be used with one.</p>

<p>On the other hand, managing tools is also a big subject, it&#39;s pretty much a package manager, needing some sort of a descriptor for a lot of tools in various languages, that need to be installable on various systems and architectures.</p>

<p>In short, the requirements boil down to:</p>
<ul><li>declarative tool SBOM</li>
<li>tool isolation (but with access to project dependencies)</li>
<li>task management</li></ul>

<h2 id="installing-tools-directly-in-the-system" id="installing-tools-directly-in-the-system">Installing tools directly in the system</h2>

<p>The old way, where the user installs tools following the project readme. Not declarative, not reproducible, definitely not isolated.</p>

<p>❌ declarative
❌ isolated
❌ task management</p>

<h2 id="declarative-package-managers-nix-guix" id="declarative-package-managers-nix-guix">Declarative package managers – Nix, Guix</h2>

<p>Environments are reproducible, declarative (both use functional languages). Rather difficult to use and very intrusive – you pretty much install another operating system in your operating system. I don&#39;t see any technical obstacle from making them single-user application, but they chose against it. Unless you already use one of them, it&#39;s a total overkill.</p>

<p>Maybe they work better with dev containers, but even then there are  alternatives.</p>

<p>✅ declarative
✅ isolated
❌ task management</p>

<h2 id="pipx-https-pipx-pypa-io" id="pipx-https-pipx-pypa-io"><a href="https://pipx.pypa.io/" rel="nofollow">pipx</a></h2>

<p><code>pip</code> for executable packages. Supports installation of multiple versions of the same tool, but there&#39;s no way to activate the required versions for a given project. And it only works for python tools (and those with a python wrapper).</p>

<p>❌ declarative
❌ isolated (tools are isolated from each other, but shared across projects)
❌ task management</p>

<h2 id="uvx-npx-pnpx-etc" id="uvx-npx-pnpx-etc">uvx, npx, pnpx, etc</h2>

<p>Set of tools that let you run their respective languages projects as programs. Each works only with tools in its language, but it&#39;s not the biggest problem, since typically the tools are written in the same language, or have wrappers.</p>

<p>They arent really designed to manage a toolbox, rather for one-off execution. There&#39;s no declarative versioning.</p>

<p><code>uvx</code> is a part of <code>uv</code>, which is a package manager, and also python version manager, but somehow tool manager is out of scope 🤷.</p>

<p>❌ declarative
✅ isolated
❌ task management</p>

<h2 id="tools-as-dev-dependencies" id="tools-as-dev-dependencies">Tools as dev dependencies</h2>

<p>It&#39;s really not what dev dependencies are meant for: everything is installed in a shared environment (venv, node<em>modules). In the nodejs environment this should be fine, every package maintains its own dependencies, even if versions differ – that&#39;s why you end up with huge node</em>modules. In python you might end up with conflicting dependencies (this somehow doesn&#39;t stop <code>poetry</code> project to use dev dependency group this way). Again, they only work for tools written in the same language as the project.</p>

<p>✅ declarative
❌ isolated
❓ task management (not in python, package.json has scripts)</p>

<h2 id="pre-commit-https-pre-commit-com" id="pre-commit-https-pre-commit-com"><a href="https://pre-commit.com" rel="nofollow">pre-commit</a></h2>

<p>Technically ticks many boxes, but not really intended as a dev tool manager.</p>

<p>Uses git repositories as a distributed source of packages, keeps them isolated and cached per project. Versions are fully declarative – you can even pin full SHA hashes.</p>

<p>This is all great, but it&#39;s literal git&#39;s pre-commit utility, which means it&#39;s designed for fast and predictable execution. It&#39;s not intended as a general purpose tool manager – it doesn&#39;t provide the tools with full project environment (it doesn&#39;t include dependencies), it doesn&#39;t support test runners (also because testing is too slow for a pre-commit hook) and even support for some linters (see pythons <code>mypy</code>) is suboptimal (again, <code>mypy</code> works best with access to dependencies).</p>

<p>✅ declarative
✅ isolated (actually too isolated for my purpose)
❌ task management</p>

<h2 id="mise-https-mise-jdx-dev" id="mise-https-mise-jdx-dev"><a href="https://mise.jdx.dev/" rel="nofollow">Mise</a></h2>

<p>Works both as a package manager (tools) and task manager (replacement for package.json scripts, rather than for <code>make</code>). The only tool with the express goal of being a tool and task manager.</p>

<p>It doesn&#39;t handle tools with plugins well (e.g. pydantic.mypy) – it uses a shared directory for installs, but only tool name and version are used for tools “identity”. which means if you have two projects, both using mypy, one using pydantic with mypy plugin, there&#39;s a conflict (there&#39;s a workaround to this, but not good for collaboration).</p>

<p>It also does funny things to <code>$PATH</code> so it doesn&#39;t work great with other tools, like IDEs or pre-commit.</p>

<p>✅ declarative
❌ isolated (buggy)
✅ task management</p>

<h2 id="dev-containers" id="dev-containers">Dev containers</h2>

<p>This isn&#39;t even a tool, it&#39;s simply a container (docker, podman, you name it) that has your tools installed and has access to your project directory.</p>

<p>It&#39;s great for isolation and reproducibility, as long your installs are versioned. The isolation makes <code>curl | sudo sh -</code> a bit more acceptable.</p>

<p>I haven&#39;t worked with it, but the concept seems very reasonable, especially for occasional build of applications that you only use (but not develop).</p>

<h1 id="conclusion" id="conclusion">Conclusion</h1>

<p>I don&#39;t see here a perfect tool, and I&#39;m not even looking for a polyglot multi-project / monorepo manager.</p>

<p>Mise comes very close, and I&#39;m currently using it but it has a problem with isolation and it doesn&#39;t seem interested in fixing it, so I wouldn&#39;t recommend it on it&#39;s own in a collaborative environment.</p>

<p>I&#39;ll take a closer look at mise in a dev container. Dev containers would provide the isolation and mise covers declarative tool and task management quite well.</p>

<p><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="nofollow"><img src="https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png" alt="CC BY-NC-SA 4.0"></a></p>
]]></content:encoded>
      <guid>https://qua.name/mattesilver/managing-development-tools</guid>
      <pubDate>Thu, 25 Dec 2025 12:40:13 +0100</pubDate>
    </item>
    <item>
      <title>State of client side HTTP in python</title>
      <link>https://qua.name/mattesilver/state-of-client-side-http-in-python</link>
      <description>&lt;![CDATA[I&#39;m developing a small package for writing typed Web API clients, similar to Uplink, only compatible with OpenAPI (lapidary).&#xA;I use these clients mostly in Web servers, so I decided to make it async. So far I&#39;ve settled on HTTPX, but I want to keep tabs on available python packages.&#xA;&#xA;urllib.request&#xA;  See also The Requests package is recommended for a higher-level HTTP client interface. &#xA;&#xA;It accepts request headers only as a dict, and I&#39;ve decided that it doesn&#39;t count as support for HTTP&#xA;&#xA;❌ HTTP/1.1&#xA;❌ HTTP/2&#xA;❌ HTTP/3&#xA;❌ asyncio&#xA;&#xA;urllib3&#xA;&#xA;Fast and robust, slowly acquiring HTTP/2 support, but only sync.&#xA;&#xA;✅ HTTP/1.1&#xA;❌ HTTP/2&#xA;❌ HTTP/3&#xA;❌ asyncio&#xA;&#xA;requests&#xA;This the one every python example tells you to use. It&#39;s under a feature freeze since forever - HTTP moves on but requests is as perfect as possible. It also &#34;broke the internet&#34; when it moved to urllib3 2.0.0, which dropped support for older TLS algorithms, in a minor version release. As long as the API doesn&#39;t change in an incompatible way, right?&#xA;&#xA;✅ HTTP/1.1&#xA;❌ HTTP/2&#xA;❌ HTTP/3&#xA;❌ asyncio&#xA;&#xA;pycURL&#xA;&#xA;It&#39;s super fast, since it&#39;s super thin wrapper over libcURL.&#xA;&#xA;Also a great showcase of how not to write python wrappers. Might as well use ctypes.&#xA;&#xA;✅ HTTP/1.1&#xA;✅ HTTP/2&#xA;✅ HTTP/3 (I think it&#39;s supported, libcURL says it&#39;s experimental)&#xA;❌ asyncio but non-blocking, since it&#39;s a native code.&#xA;&#xA;HTTPX&#xA;&#xA;The most popular async HTTP client package for python. Supports HTTP/2 but claims it&#39;s &#34;not as robust&#34; as 1.1. Both sync and async.&#xA;&#xA;API is mostly compatible with requests (it&#39;s not a feature). Depending on usage pattern, it&#39;s slightly slower or much slower than aiohttp.&#xA;&#xA;✅ HTTP/1.1&#xA;✅ HTTP/2 (discouraged, opt-in)&#xA;❌ HTTP/3&#xA;✅ sync &amp; async&#xA;&#xA;aiohttp&#xA;API is close to the protocol, so a bit more complex than alternatives, but I like it. One of the fastest packages in python.&#xA;&#xA;The community seems more focused on the server side (more features and plugins).&#xA;&#xA;✅ HTTP/1.1&#xA;❌ HTTP/2&#xA;❌ HTTP/3&#xA;✅ asyncio-only&#xA;&#xA;Aiosonic&#xA;&#xA;Looks and feels like HTTPX.&#xA;&#xA;Depending on usage pattern, it&#39;s either a bit slower or a bit faster than aiohttp. Probably there&#39;s something wrong with it, because nobody seems to be using it. Definitely worth a closer look.&#xA;&#xA;✅ HTTP/1.1&#xA;✅ HTTP/2 - beta&#xA;❌ HTTP/3&#xA;✅ sync &amp; asyncio&#xA;&#xA;Aioquic&#xA;&#xA;Really low level HTTP/3 &amp; QUIC package. Only for genuine H3 users. Could use better examples - example client has 600+ lines. Or maybe it&#39;s that hard to use...&#xA;&#xA;❌ HTTP/1.1&#xA;❌ HTTP/2&#xA;✅ HTTP/3&#xA;✅ asyncio-only&#xA;&#xA;Niquests&#xA;&#xA;Async fork of requests with HTTP/2 and 3. Fast, full featured but not user-friendly.&#xA;&#xA;It uses a forked urllib3 (by the same author) that shadows it, so if you happen to have a transitive dependency on the original, it will mess up your environment. The author is fine with it. I recommend against using it.&#xA;&#xA;✅ HTTP/1.1&#xA;✅ HTTP/2&#xA;✅ HTTP/3&#xA;✅ sync &amp; asyncio&#xA;&#xA;Reqwest wrappers&#xA;&#xA;This makes a lot of sense - Rust is a popular language, reqwest is a popular rust package, keep it DRY. None of the wrappers are widely popular, but some projects are live and definitely worth watching.&#xA;gufo-http - I think it&#39;s the oldest and still maintained&#xA;pyreqwest&#xA;httpr&#xA;&#xA;Found a few more, just search reqwest in pypi.&#xA;&#xA;✅ HTTP/1.1&#xA;✅ HTTP/2&#xA;❌ HTTP/3 - maybe, it&#39;s still experimental in reqwest&#xA;✅ sync and asyncio&#xA;&#xA;Conclusion&#xA;&#xA;Honestly, the clients for the protocol that makes the Web makes me wonder is Python still alive? There isn&#39;t a single popular and stable package with HTTP/2 (2015!) or HTTP/3 (2022) either sync or async.&#xA;Is Python only for Django and LLM? Have everyone else switch to Rust and/or Deno?&#xA;&#xA;CC BY-NC-SA 4.0]]&gt;</description>
      <content:encoded><![CDATA[<p>I&#39;m developing a small package for writing typed Web API clients, similar to <a href="https://uplink.prkumar.dev/" rel="nofollow">Uplink</a>, only compatible with OpenAPI (<a href="https://github.com/python-lapidary/lapidary" rel="nofollow">lapidary</a>).
I use these clients mostly in Web servers, so I decided to make it async. So far I&#39;ve settled on HTTPX, but I want to keep tabs on available python packages.</p>

<h2 id="urllib-request-https-docs-python-org-3-library-urllib-request-html" id="urllib-request-https-docs-python-org-3-library-urllib-request-html"><a href="https://docs.python.org/3/library/urllib.request.html" rel="nofollow">urllib.request</a></h2>

<blockquote><p>See also The Requests package is recommended for a higher-level HTTP client interface.</p></blockquote>

<p>It accepts request headers only as a dict, and I&#39;ve decided that it doesn&#39;t count as support for HTTP</p>

<p>❌ HTTP/1.1
❌ HTTP/2
❌ HTTP/3
❌ asyncio</p>

<h2 id="urllib3-https-pypi-org-project-urllib3" id="urllib3-https-pypi-org-project-urllib3"><a href="https://pypi.org/project/urllib3/" rel="nofollow">urllib3</a></h2>

<p>Fast and robust, slowly acquiring HTTP/2 support, but only sync.</p>

<p>✅ HTTP/1.1
❌ <a href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support" rel="nofollow">HTTP/2</a>
❌ HTTP/3
❌ asyncio</p>

<h2 id="requests-https-pypi-org-project-requests" id="requests-https-pypi-org-project-requests"><a href="https://pypi.org/project/requests/" rel="nofollow">requests</a></h2>

<p>This the one every python example tells you to use. It&#39;s under a feature freeze since forever – HTTP moves on but requests is as perfect as possible. It also “broke the internet” when it moved to urllib3 2.0.0, which dropped support for older TLS algorithms, in a minor version release. As long as the API doesn&#39;t change in an incompatible way, right?</p>

<p>✅ HTTP/1.1
❌ HTTP/2
❌ HTTP/3
❌ asyncio</p>

<h2 id="pycurl-http-pycurl-io" id="pycurl-http-pycurl-io"><a href="http://pycurl.io/" rel="nofollow">pycURL</a></h2>

<p>It&#39;s super fast, since it&#39;s super thin wrapper over libcURL.</p>

<p>Also a great showcase of how <em>not</em> to write python wrappers. Might as well use ctypes.</p>

<p>✅ HTTP/1.1
✅ HTTP/2
✅ HTTP/3 (I think it&#39;s supported, libcURL says it&#39;s experimental)
❌ asyncio but non-blocking, since it&#39;s a native code.</p>

<h2 id="httpx-https-pypi-org-project-httpx" id="httpx-https-pypi-org-project-httpx"><a href="https://pypi.org/project/httpx/" rel="nofollow">HTTPX</a></h2>

<p>The most popular <em>async</em> HTTP client package for python. Supports HTTP/2 but claims it&#39;s “not as robust” as 1.1. Both sync and async.</p>

<p>API is mostly compatible with requests (it&#39;s not a feature). Depending on usage pattern, it&#39;s slightly slower or much slower than aiohttp.</p>

<p>✅ HTTP/1.1
✅ HTTP/2 (discouraged, opt-in)
❌ HTTP/3
✅ sync &amp; async</p>

<h2 id="aiohttp-https-docs-aiohttp-org-en-stable-client-html" id="aiohttp-https-docs-aiohttp-org-en-stable-client-html"><a href="https://docs.aiohttp.org/en/stable/client.html" rel="nofollow">aiohttp</a></h2>

<p>API is close to the protocol, so a bit more complex than alternatives, but I like it. One of the fastest packages in python.</p>

<p>The community seems more focused on the server side (more features and plugins).</p>

<p>✅ HTTP/1.1
❌ HTTP/2
❌ HTTP/3
✅ asyncio-only</p>

<h2 id="aiosonic-https-aiosonic-readthedocs-io-en-latest" id="aiosonic-https-aiosonic-readthedocs-io-en-latest"><a href="https://aiosonic.readthedocs.io/en/latest/" rel="nofollow">Aiosonic</a></h2>

<p>Looks and feels like HTTPX.</p>

<p>Depending on usage pattern, it&#39;s either a bit slower or a bit faster than aiohttp. Probably there&#39;s something wrong with it, because nobody seems to be using it. Definitely worth a closer look.</p>

<p>✅ HTTP/1.1
✅ HTTP/2 – beta
❌ HTTP/3
✅ sync &amp; asyncio</p>

<h2 id="aioquic-https-aioquic-readthedocs-io-en-latest" id="aioquic-https-aioquic-readthedocs-io-en-latest"><a href="https://aioquic.readthedocs.io/en/latest/" rel="nofollow">Aioquic</a></h2>

<p><em>Really</em> low level HTTP/3 &amp; QUIC package. Only for genuine H3 users. Could use better examples – example client has 600+ lines. Or maybe it&#39;s that hard to use...</p>

<p>❌ HTTP/1.1
❌ HTTP/2
✅ HTTP/3
✅ asyncio-only</p>

<h2 id="niquests-https-pypi-org-project-niquests" id="niquests-https-pypi-org-project-niquests"><a href="https://pypi.org/project/niquests/" rel="nofollow">Niquests</a></h2>

<p>Async fork of requests with HTTP/2 and 3. Fast, full featured but not user-friendly.</p>

<p>It uses a forked urllib3 (by the same author) that shadows it, so if you happen to have a transitive dependency on the original, it will mess up your environment. The author is fine with it. I recommend <em>against</em> using it.</p>

<p>✅ HTTP/1.1
✅ HTTP/2
✅ HTTP/3
✅ sync &amp; asyncio</p>

<h2 id="reqwest-https-docs-rs-reqwest-wrappers" id="reqwest-https-docs-rs-reqwest-wrappers"><a href="https://docs.rs/reqwest" rel="nofollow">Reqwest</a> wrappers</h2>

<p>This makes a lot of sense – Rust is a popular language, reqwest is a popular rust package, keep it DRY. None of the wrappers are widely popular, but some projects are live and definitely worth watching.
– <a href="https://pypi.org/project/gufo-http/" rel="nofollow">gufo-http</a> – I think it&#39;s the oldest and still maintained
– <a href="https://pypi.org/project/pyreqwest/" rel="nofollow">pyreqwest</a>
– <a href="https://pypi.org/project/httpr/" rel="nofollow">httpr</a></p>

<p>Found a few more, just <a href="https://pypi.org/search/?q=reqwest" rel="nofollow">search reqwest in pypi</a>.</p>

<p>✅ HTTP/1.1
✅ HTTP/2
❌ HTTP/3 – maybe, it&#39;s still <a href="https://github.com/seanmonstar/reqwest/issues/2303" rel="nofollow">experimental</a> in reqwest
✅ sync and asyncio</p>

<h1 id="conclusion" id="conclusion">Conclusion</h1>

<p>Honestly, the clients for the protocol that makes the Web makes me wonder is Python still alive? There isn&#39;t a single popular and stable package with HTTP/2 (2015!) or HTTP/3 (2022) either sync or async.
Is Python only for Django and LLM? Have everyone else switch to Rust and/or Deno?</p>

<p><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="nofollow"><img src="https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png" alt="CC BY-NC-SA 4.0"></a></p>
]]></content:encoded>
      <guid>https://qua.name/mattesilver/state-of-client-side-http-in-python</guid>
      <pubDate>Fri, 14 Nov 2025 19:32:41 +0100</pubDate>
    </item>
    <item>
      <title>How to implement social login with Litestar</title>
      <link>https://qua.name/mattesilver/how-to-implement-social-login-with-litestar</link>
      <description>&lt;![CDATA[Based on the Litestar Fullstack Inertia showcase project.&#xA;&#xA;Prerequisites&#xA;&#xA;For social authorization to work, you first need to register your application with the identity provider (e.g., GitHub).&#xA;&#xA;Implementation&#xA;&#xA;Frontend&#xA;&#xA;You need a login or registration page with a button/link that sends the browser to a backend route which starts the OAuth2 flow. For social login, it doesn’t matter whether you call it &#34;login&#34; or &#34;registration&#34;, as the first login typically creates the user.&#xA;&#xA;Implementation details:&#xA;In the showcase project it’s implemented in partials:&#xA;  Login form&#xA;  Registration form&#xA;Both partials use the same button, which handles clicks with router.post(route(&#34;github.register&#34;)).&#xA;route() is an Inertia.js helper function which simply resolves handler names to paths. &#xA;A plain link and HTTP GET would also work here, since no data is sent at this stage.&#xA;&#xA;Login/Register handler (backend)&#xA;&#xA;Create a route that redirects to the provider’s authorization URL and includes a callback pointing to your completion handler.&#xA;For production you should generate OAuth2 state and PKCE code verifier, and store them in the session.&#xA;&#xA;Implementation details:&#xA;See the authorization controller&#xA;You can use httpxoauth to get auth-related URLs for popular OAuth2 providers.&#xA;&#xA;Authorization callback handler (backend)&#xA;&#xA;Your callback (named &#34;github.complete&#34; in the showcase) should:&#xA;&#xA;Validate the store OAuth2 state (required for security; missing in the showcase)&#xA;Exchange the authorization code for an access token  &#xA;Create a user object in the local database and store the user Id in the session&#xA;Redirect to a post-login page (e.g., dashboard)&#xA;&#xA;Optionally:&#xA;Fetch user details (e.g., email) from the provider  &#xA;Provide user data to the frontend&#xA;&#xA;Implementation details:&#xA;The showcase doesn&#39;t use OAuth2 state or PKCE&#xA;GitHubOAuth2.getaccesstoken is used to exchange the auth code for a token (wrapped as a Litestar dependency, to facilitate re-use)&#xA;GitHubOAuth2.getid_email is used to retrieve users email&#xA;Inertia’s share() is used to pass user details to the frontend&#xA;&#xA;© 2025 Matte Silver&#xA;&#xA;CC BY-NC-SA 4.0]]&gt;</description>
      <content:encoded><![CDATA[<p>Based on the <a href="https://github.com/litestar-org/litestar-fullstack-inertia" rel="nofollow">Litestar Fullstack Inertia</a> showcase project.</p>

<h2 id="prerequisites" id="prerequisites">Prerequisites</h2>

<p>For social authorization to work, you first need to register your application with the identity provider (e.g., GitHub).</p>

<h2 id="implementation" id="implementation">Implementation</h2>

<h3 id="frontend" id="frontend">Frontend</h3>

<p>You need a login or registration page with a button/link that sends the browser to a backend route which starts the OAuth2 flow. For social login, it doesn’t matter whether you call it “login” or “registration”, as the first login typically creates the user.</p>

<p>Implementation details:
– In the showcase project it’s implemented in partials:
  – <a href="https://github.com/litestar-org/litestar-fullstack-inertia/blob/main/resources/pages/auth/partials/user-login-form.tsx" rel="nofollow">Login form</a>
  – <a href="https://github.com/litestar-org/litestar-fullstack-inertia/blob/main/resources/pages/auth/partials/user-registration-form.tsx" rel="nofollow">Registration form</a>
– Both partials use the same button, which handles clicks with <code>router.post(route(&#34;github.register&#34;))</code>.
– <code>route()</code> is an Inertia.js helper function which simply resolves handler names to paths.
– A plain link and HTTP GET would also work here, since no data is sent at this stage.</p>

<h3 id="login-register-handler-backend" id="login-register-handler-backend">Login/Register handler (backend)</h3>

<p>Create a route that redirects to the provider’s authorization URL and includes a callback pointing to your completion handler.
For production you should generate OAuth2 <code>state</code> and PKCE code verifier, and store them in the session.</p>

<p>Implementation details:
– See <a href="https://github.com/litestar-org/litestar-fullstack-inertia/blob/main/app/domain/accounts/controllers.py" rel="nofollow">the authorization controller</a>
– You can use <code>httpx_oauth</code> to get auth-related URLs for popular OAuth2 providers.</p>

<h3 id="authorization-callback-handler-backend" id="authorization-callback-handler-backend">Authorization callback handler (backend)</h3>

<p>Your callback (named “github.complete” in the showcase) should:</p>
<ol><li>Validate the store OAuth2 state (required for security; missing in the showcase)</li>
<li>Exchange the authorization code for an access token<br></li>
<li>Create a user object in the local database and store the user Id in the session</li>
<li>Redirect to a post-login page (e.g., dashboard)</li></ol>

<p>Optionally:
– Fetch user details (e.g., email) from the provider<br>
– Provide user data to the frontend</p>

<p>Implementation details:
– The showcase doesn&#39;t use OAuth2 state or PKCE
– <code>GitHubOAuth2.get_access_token</code> is used to exchange the auth code for a token (wrapped as a Litestar dependency, to facilitate re-use)
– <code>GitHubOAuth2.get_id_email</code> is used to retrieve users email
– Inertia’s <code>share()</code> is used to pass user details to the frontend</p>

<p>© 2025 Matte Silver</p>

<p><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="nofollow"><img src="https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png" alt="CC BY-NC-SA 4.0"></a></p>
]]></content:encoded>
      <guid>https://qua.name/mattesilver/how-to-implement-social-login-with-litestar</guid>
      <pubDate>Mon, 10 Nov 2025 18:19:11 +0100</pubDate>
    </item>
  </channel>
</rss>