<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: TAHRI Ahmed R.</title>
    <description>The latest articles on Forem by TAHRI Ahmed R. (@ousret).</description>
    <link>https://forem.com/ousret</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F206201%2Fb9d6042f-8482-4180-bcf4-012597a49eff.jpg</url>
      <title>Forem: TAHRI Ahmed R.</title>
      <link>https://forem.com/ousret</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ousret"/>
    <language>en</language>
    <item>
      <title>Revived the promise made six years ago for Requests 3</title>
      <dc:creator>TAHRI Ahmed R.</dc:creator>
      <pubDate>Tue, 02 Apr 2024 05:16:45 +0000</pubDate>
      <link>https://forem.com/ousret/revived-the-promise-made-six-years-ago-for-requests-3-2l98</link>
      <guid>https://forem.com/ousret/revived-the-promise-made-six-years-ago-for-requests-3-2l98</guid>
      <description>&lt;p&gt;Requests is undeniably unmissable for anyone willing to start in the Python ecosystem. Yet the community is broadly unaware of how sad the situation has become.&lt;/p&gt;

&lt;h3&gt;
  
  
  The incident
&lt;/h3&gt;

&lt;p&gt;Due to dramatics circumstances, it was partially locked and put in "maintenance mode" only, meaning &lt;br&gt;
that it would no longer evolve (features speaking).&lt;/p&gt;

&lt;p&gt;Some of you may have eared, more than six years ago that Requests was working toward a major version, namely the V3. &lt;br&gt;
There was even &lt;a href="https://vorpus.org/blog/why-im-not-collaborating-with-kenneth-reitz/"&gt;a completed fundraiser&lt;/a&gt; with a substantial amount, around $30k.&lt;/p&gt;

&lt;p&gt;At the time, there was a plan for bringing Async, and HTTP/2 support (most notably). You'll guess that none of exposed promises ever landed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Impacts
&lt;/h3&gt;

&lt;p&gt;This incident led to several impacts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintainers felt too much pressure on making steps toward a major, some of them said: &lt;a href="https://blog.ian.stapletoncordas.co/2024/02/a-retrospective-on-requests#footnote-5"&gt;"We just can't deliver the promises, and it's going hard to explain why"&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;All the back pressure landed at urllib3.&lt;/li&gt;
&lt;li&gt;Abruptly started to refuse any changes, saying: &lt;a href="https://requests.readthedocs.io/en/latest/dev/contributing/#feature-requests"&gt;"Requests is feature complete, and it's popularity makes it nearly impossible to fix bugs that may be considered feature to this point"&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Newer project started ditching HTTP client Requests by learning to use a new one.&lt;/li&gt;
&lt;li&gt;Some large scale projects bound to Requests did not migrate because of the potential high cost doing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's be clear, this article is not about saying "Requests maintainers are bad" but rather saying we don't agree, and everyone is entitled to his choices.&lt;/p&gt;

&lt;p&gt;For many years now, Requests has been frozen. Being left in a vegetative state and not evolving, &lt;a href="https://github.com/psf/requests/network/dependents"&gt;this blocked millions of developers&lt;/a&gt; from using more advanced features.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Journey into Waking Up
&lt;/h3&gt;

&lt;p&gt;Initially, like many other reasonable person, I said to myself "Let's wait, Requests will eventually unblock the situation!"&lt;br&gt;
Yet the years are still passing and nothing.&lt;/p&gt;

&lt;p&gt;Let's realize something, &lt;a href="https://medium.com/dev-genius/10-reasons-you-should-quit-your-http-client-98fd4c94bef3"&gt;Internet Explorer 11 just celebrated its tenth anniversary last year, and you’ll never guess that it &lt;br&gt;
has support for HTTP/2 integrated!&lt;/a&gt; Yet Requests is unable to do so.&lt;/p&gt;

&lt;p&gt;I said to myself: "Why are we waiting for this indefinitely? They don't owe us anything! There's a reasonable chance we are waiting... trapped in a circular reference.. Let's act."&lt;/p&gt;

&lt;p&gt;Then, I tried to get a firm grip on urllib3 base code, contributing &lt;a href="https://github.com/Ousret?from=2022-12-01&amp;amp;to=2022-12-31&amp;amp;org=urllib3&amp;amp;year_list=1"&gt;this&lt;/a&gt; and &lt;a href="https://github.com/Ousret?tab=overview&amp;amp;from=2023-12-01&amp;amp;to=2023-12-31&amp;amp;org=urllib3"&gt;there&lt;/a&gt; until I was ready to kick things up with &lt;a href="https://github.com/urllib3/urllib3/pull/3030"&gt;a&lt;br&gt;
proof of concept&lt;/a&gt; that would have put urllib3 far ahead. Without any breaking changes.&lt;br&gt;
I was delusional. This was a bit of a shock, but six months passed between my initial kick off and &lt;a href="https://github.com/jawah/urllib3.future/issues/46#issuecomment-1826845827"&gt;my formal give up&lt;/a&gt;, and here's why in a nutshell:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every bits of idea brought to the table was dismissed, and never seriously analysed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This felt as good as taking the coldest wind on your face while exiting your warm home. It's the hard truth of the OSS,&lt;br&gt;
it's not a democracy and it's not ruled by pure logic, but humans.&lt;/p&gt;

&lt;p&gt;No far latter on, urllib3 called out &lt;a href="https://opencollective.com/urllib3/updates/urllib3-is-fundraising-for-http-2-support"&gt;a fundraiser&lt;/a&gt; to help with HTTP/2 support (only) and claimed around $40k to be able to deliver. They probably meant well, but[...]&lt;/p&gt;

&lt;p&gt;This was the last straw for me, knowing the past mistakes, and actually the best incentive to break the cycle. Everything point to another year before having the slightest sign of a &lt;code&gt;PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n&lt;/code&gt; into our production pipes. They clearly lack the time to move forward (or incentives? or both?). No worries, as developers, we should share this burden for the greater good.&lt;/p&gt;

&lt;p&gt;From my perspective (my opinion only), the OSS ecosystem should not fund raise money for a decade old protocol, and most of time should deliver prior to asking. Entities raising money with no solid proof of concept, that's rare!&lt;/p&gt;

&lt;p&gt;This felt like a bit of a hostage situation, but it could only be me seeing it this way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;So many years and various expertise have been brought to Requests and urllib3.. Do we throw all of it into the garbage disposal? Engineers should be reasonable scientists and not zeled revisionists.&lt;/p&gt;

&lt;p&gt;We don't have to reinvent the wheel all over again, HTTP client Requests is well established and really pleasant in its usage. &lt;br&gt;
We believe that Requests has the most inclusive and developer friendly interfaces.&lt;/p&gt;

&lt;p&gt;So how to propose an attractive continuity for Requests? How hard could this be to (i) bring a true upgrade experience and (ii) keeping the compatibility as high as possible?&lt;/p&gt;

&lt;p&gt;Sometime the OSS ecosystem act like it is trying to land a rover on the Moon, let's be more realistic and bring our vision down to firm grounds.&lt;/p&gt;

&lt;p&gt;There's a chance offered to us to rehabilitate Requests (and urllib3) and give a fresh breath of air to us, the developers. Without having to learn something as basic as handling a HTTP client.&lt;/p&gt;

&lt;p&gt;So, the work started, without looking back.&lt;/p&gt;

&lt;p&gt;Just around my intent to give up on urllib3 evolution, we proposed a fork of both urllib3 and Requests, namely &lt;a href="https://github.com/jawah/urllib3.future"&gt;urllib3-future&lt;/a&gt; and &lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;They are (truly) drop-in replacements, at the time of writing, those forks are six-months old! And the whole work around this is roughly a year old. We listened to what the community needed by... triaging all the issues on major repositories (Requests, urllib3, and dependants of them) so that the upgrade would be truly useful.&lt;/p&gt;

&lt;p&gt;Are you seeing it coming? Great! &lt;b&gt;Let's look at the feature table comparison&lt;/b&gt; against requests, httpx and aiohttp!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;niquests&lt;/th&gt;
&lt;th&gt;requests&lt;/th&gt;
&lt;th&gt;httpx&lt;/th&gt;
&lt;th&gt;aiohttp&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HTTP/1.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HTTP/2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HTTP/3 over QUIC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Synchronous&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Asynchronous&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Thread Safe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;em&gt;N/A&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Task Safe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;em&gt;N/A&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OS Trust Store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Multiplexing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Limited&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DNSSEC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Customizable DNS Resolution&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DNS over HTTPS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DNS over QUIC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DNS over TLS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Multiple DNS Resolver&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Network Fine Tuning &amp;amp; Inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Limited&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Limited&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Certificate Revocation Protection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Session Persistence&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;In-memory Certificate CA &amp;amp; mTLS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Limited&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Limited&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SOCKS 4/5 Proxies&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HTTP/HTTPS Proxies&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TLS-in-TLS Support&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Direct HTTP/3 Negotiation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Happy Eyeballs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Package / SLSA Signed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And you would think... performance..? are bad?&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Look at the performance comparison&lt;/b&gt; against them!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Scenario:&lt;/em&gt; Fetch a thousand requests using 10 tasks or threads, each with a hundred requests using a single pool of 10 connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High-Level APIs&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client&lt;/th&gt;
&lt;th&gt;Average Delay to Complete&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;requests&lt;/td&gt;
&lt;td&gt;987 ms&lt;/td&gt;
&lt;td&gt;ThreadPoolExecutor. HTTP/1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;httpx&lt;/td&gt;
&lt;td&gt;735 ms&lt;/td&gt;
&lt;td&gt;Asyncio. HTTP/2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;niquests&lt;/td&gt;
&lt;td&gt;470 ms&lt;/td&gt;
&lt;td&gt;Asyncio. HTTP/2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Simplified APIs&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client&lt;/th&gt;
&lt;th&gt;Average Delay to Complete&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;requests core&lt;/td&gt;
&lt;td&gt;643 ms&lt;/td&gt;
&lt;td&gt;ThreadPoolExecutor. HTTP/1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;httpx core&lt;/td&gt;
&lt;td&gt;550 ms&lt;/td&gt;
&lt;td&gt;Asyncio. HTTP/2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aiohttp&lt;/td&gt;
&lt;td&gt;220 ms&lt;/td&gt;
&lt;td&gt;Asyncio. HTTP/1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;niquests core&lt;/td&gt;
&lt;td&gt;210 ms&lt;/td&gt;
&lt;td&gt;Asyncio. HTTP/2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Did you give up on HTTP/2 due to performance concerns? Think again! &lt;a href="https://niquests.readthedocs.io/en/latest/user/quickstart.html#multiplexed-connection"&gt;Multiplexing and response laziness&lt;/a&gt; open up a wide range&lt;br&gt;
of possibilities!&lt;/p&gt;

&lt;p&gt;And everything you are witnessing is in fact "Requests"-extended and you have nothing to learn again to get started. We kept 98% of the test cases from both Requests and urllib3, then extended them to cover newer features.&lt;/p&gt;

&lt;p&gt;We are looking forward to see you at the Niquests repository! Let's say it's a promotional offer, get everything and more for $̶4̶0̶k̶ $0!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;#1 You depends on Requests? Discover &lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt; instead.

&lt;ul&gt;
&lt;li&gt;Drop-in replacement, simple patch needed, tiny effort required. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;#2 You depends on urllib3? Discover &lt;a href="https://github.com/jawah/urllib3.future"&gt;urllib3.future&lt;/a&gt; instead. 

&lt;ul&gt;
&lt;li&gt;Drop-in replacement, no patch required, atom-like sized effort required. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By visiting the GitHub repository and documentation you will be able to learn more about this, and hopefully make a decisive switch!&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>opensource</category>
      <category>community</category>
    </item>
    <item>
      <title>10 reasons you should quit your HTTP client</title>
      <dc:creator>TAHRI Ahmed R.</dc:creator>
      <pubDate>Wed, 15 Nov 2023 11:47:52 +0000</pubDate>
      <link>https://forem.com/ousret/10-reasons-you-should-quit-your-http-client-1doi</link>
      <guid>https://forem.com/ousret/10-reasons-you-should-quit-your-http-client-1doi</guid>
      <description>&lt;p&gt;I have been both secretly, and publicly working in the HTTP client field for an entire year, here is what my newly acquired&lt;br&gt;
expertise got us. This will shock you, so be prepared. Seatbelt on! Let's travel back to the future.&lt;/p&gt;

&lt;p&gt;As a Python programmer, we should be aware that it is soon to be 2024, but most of the community lives like it's 2015.&lt;br&gt;
And we don't always know it. That's insane.&lt;/p&gt;

&lt;p&gt;Let us crawl out of the pit we inadvertently dug ourselves, &lt;strong&gt;together&lt;/strong&gt;. We've almost all started by using Requests, and now&lt;br&gt;
we are trapped and kind of forced out sometimes. The thing is... Habits die hard!&lt;/p&gt;

&lt;p&gt;I was so lazy to migrate my projects from Requests, that I decided to tackle the next Requests myself.&lt;/p&gt;
&lt;h3&gt;
  
  
  🏆 (I) HTTP/2 by default, HTTP/3 broadly available!
&lt;/h3&gt;

&lt;p&gt;👴 Internet Explorer 11 just celebrated its &lt;strong&gt;tenth&lt;/strong&gt; anniversary this year, and you'll never guess that it has&lt;br&gt;
&lt;a href="https://techcrunch.com/2013/06/26/microsoft-confirms-ie11-will-support-googles-spdy-protocol/"&gt;support for HTTP/2 integrated&lt;/a&gt; (&lt;em&gt;not activated by default&lt;sup&gt;*&lt;/sup&gt;&lt;/em&gt;). &lt;/p&gt;

&lt;p&gt;Saying that while the Python ecosystem has no HTTP client supporting HTTP/2, and HTTP/3 by default in November 2023.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jawah/niquests"&gt;How about a client that supports them all?&lt;/a&gt; With great incentives! 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FnOvg1gi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/85quub.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FnOvg1gi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/85quub.jpg" alt="Trophy earned for migrating" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;*: Doesn't this remind you of a particular HTTP client? HTTP/1 by default, optional HTTP/2?&lt;/small&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  🔀 (II) Multiplexed Connection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We've just almost murdered Async, and made you watch!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beware that reading down further will follow by a &lt;strong&gt;" Cannot be unseen "&lt;/strong&gt; in your head.&lt;/p&gt;

&lt;p&gt;🚨 Some ideas are just sticky, like " faster HTTP " &lt;strong&gt;requires&lt;/strong&gt; Async or your code will be slower. Wrong!&lt;/p&gt;

&lt;p&gt;Do you, too, work in a company with programs that exceed 100k lines involving synchronous HTTP requests? No way that the&lt;br&gt;
company would allow us to rewrite the whole thing to async? budget and mental sanity reasons? We are here to save the day.&lt;/p&gt;

&lt;p&gt;Let's do a quick MCQ!&lt;/p&gt;

&lt;p&gt;According to you, how long does this take to run? We are fetching &lt;a href="https://pie.dev/delay/3"&gt;this resource&lt;/a&gt;. The controller behind does a &lt;code&gt;sleep(3)&lt;/code&gt; and then returns a valid &lt;strong&gt;JSON&lt;/strong&gt; output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;

&lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://pie.dev/delay/3"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://pie.dev/delay/3"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A) 6 seconds&lt;/li&gt;
&lt;li&gt;B) 3 seconds&lt;/li&gt;
&lt;li&gt;C) 12 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;em&gt;¡spuoɔǝs 9 sɐʍ ɹǝʍsuɐ ǝɥ⊥&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Again!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;niquests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;

&lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multiplexed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://pie.dev/delay/3"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://pie.dev/delay/3"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A) 6 seconds&lt;/li&gt;
&lt;li&gt;B) 3 seconds&lt;/li&gt;
&lt;li&gt;C) 12 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;em&gt;¡spuoɔǝs Ɛ sɐʍ ɹǝʍsuɐ ǝɥ⊥&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://niquests.readthedocs.io/en/latest/user/quickstart.html#multiplexed-connection"&gt;&lt;code&gt;multiplexed&lt;/code&gt;&lt;/a&gt; enabled you have just transferred the whole async/await to the remote peer! Isn't awesome?!&lt;br&gt;
No hidden magic trick under the carpet, no async, no threads!&lt;/p&gt;

&lt;p&gt;Did you wonder how you would do this without &lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt;? Look 👉&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SNVNF_nH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282422195-1017cfd4-243d-4452-827a-dcf47ef05919.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SNVNF_nH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282422195-1017cfd4-243d-4452-827a-dcf47ef05919.png" alt="Without Niquests is going to be painful" width="800" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a non-negligible chance you'd rather write 6 lines with a mere &lt;a href="https://niquests.readthedocs.io/en/latest/user/quickstart.html#multiplexed-connection"&gt;&lt;code&gt;multiplexed=True&lt;/code&gt;&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;The great thing here is that we made it completely transparent for our fellow developers. You are &lt;em&gt;literally&lt;/em&gt; one toggle away from leveraging one or many multiplexed connections!&lt;/p&gt;
&lt;h3&gt;
  
  
  🔒 (III) Enhanced Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;TLS 1.2+ was just the starting block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Earlier this year, some HTTP clients decided to enforce &lt;strong&gt;TLS 1.2&lt;/strong&gt; as the minimum supported. We decided to go all in,&lt;br&gt;
not only did we enforce TLS 1.2, but we also enforced &lt;a href="https://ssl-config.mozilla.org/#server=nginx&amp;amp;version=1.23&amp;amp;config=intermediate&amp;amp;openssl=3.0.12&amp;amp;hsts=false&amp;amp;ocsp=false&amp;amp;guideline=5.7"&gt;the recommended cipher suite&lt;/a&gt; that Mozilla regularly updates so that&lt;br&gt;
we remain safe against sophisticated types of attacks. &lt;em&gt;Quantum CPUs are a thing, right?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OCSP!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No HTTP client is, in fact, capable of providing a basic level of insurance that the peer certificate is in fact (still)&lt;br&gt;
valid. With the growing size of issued Let's Encrypt (short-lived) certificates, more insurance is needed.&lt;br&gt;
It's not bulletproof as it follows the &lt;code&gt;fail-safe&lt;/code&gt; strategy that most browsers do unless &lt;a href="https://niquests.readthedocs.io/en/latest/user/advanced.html#ocsp-or-certificate-revocation"&gt;&lt;code&gt;strict-mode&lt;/code&gt;&lt;/a&gt; is enabled.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The dependency chain is certified! &lt;a href="https://slsa.dev/"&gt;SLSA&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Almost all the dependencies in the chain are verifiable at any moment.&lt;br&gt;
They all benefit from reproducible build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gIza6TpU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282318931-57119ee8-b534-4a36-a5c5-c5f052401f88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gIza6TpU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282318931-57119ee8-b534-4a36-a5c5-c5f052401f88.png" alt="SLSA and Dependencies Tree" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of our releases comes with SLSA provenance data (multiple.intoto.jsonl), which can be used to verify the source and provenance of the binaries with the &lt;a href="https://github.com/slsa-framework/slsa-verifier"&gt;slsa-verifier&lt;/a&gt; tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;slsa-verifier verify-artifact ./niquests-3.2.3-py3-none-any.whl --provenance-path multiple.intoto.jsonl --source-uri github.com/jawah/niquests --source-tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Certificates are validated against a trustable source 👇 (&lt;em&gt;Spoiler: Your OS&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🪪 (IV) System Root CAs
&lt;/h3&gt;

&lt;p&gt;Almost everyone is using Certifi or has seen it pass through your usual &lt;code&gt;pip install ...&lt;/code&gt; routine.&lt;br&gt;
In a nutshell, Certifi is a package that ships with a static collection of certificate authorities or root CAs if you&lt;br&gt;
prefer.&lt;/p&gt;

&lt;p&gt;Certifi was a mistake, a huge one. A workaround is great to move on, but only for a short period of time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This list of trusted CA is so critical that you should be scared if anything would happen, &lt;br&gt;
It is almost never updated by users, so your local copy is most likely too old and &lt;a href="https://security.snyk.io/vuln/SNYK-PYTHON-CERTIFI-5805047"&gt;may contain revoked certificate&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does not work at work!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, Certifi does not ship with your company's certificates! So requesting internal services may come with additional&lt;br&gt;
painful extra steps! Also for a local development environment that &lt;a href="https://github.com/FiloSottile/mkcert/issues/49"&gt;uses mkcert for example!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is why you should migrate!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our implementation takes away all of those concerns by looking at your OS trust store. Which is constantly updated in the background and protected properly.&lt;/p&gt;
&lt;h3&gt;
  
  
  ⚡ (V) Faster
&lt;/h3&gt;

&lt;p&gt;You may get up to 3X faster in your usual synchronous context if you ever switch to &lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt;.&lt;br&gt;
Without multiplexing, well, it is roughly aligned to competitors, except with Requests, we are faster in all comparable scenarios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ancFWUsT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283507-d04d4170-41a3-4f6e-8005-109a7cdd55fc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ancFWUsT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283507-d04d4170-41a3-4f6e-8005-109a7cdd55fc.png" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HTTP/3 is a bit less performant as of today, but still much faster than any competitor in a synchronous context.&lt;br&gt;
We are actively working toward making it faster than HTTP/2.&lt;/p&gt;

&lt;p&gt;Keep in mind that ~Re~&lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt; have a lot of features and that we made every effort conceivable to keep them!&lt;/p&gt;
&lt;h3&gt;
  
  
  🧠 (VI) In-memory certificate
&lt;/h3&gt;

&lt;p&gt;This one should come with enthusiasm to many advanced users! Python did not allow end-users to load client mTLS certificates and &lt;br&gt;
associated key without having it in a file. And Requests did not allow to pass on your root CAs without a file either.&lt;/p&gt;

&lt;p&gt;That's &lt;a href="https://niquests.readthedocs.io/en/latest/user/advanced.html#in-memory-certificates"&gt;a thing of the past now&lt;/a&gt;, we elaborated a clever way to work around this limitation.&lt;/p&gt;
&lt;h3&gt;
  
  
  🍰 (VII) Object-oriented headers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We should control ourselves when eating sugar, not when pushing the " sugar syntax " as a programmer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who did not, at the very least, waste a bit of time accessing HTTP headers from a response? Well!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;niquests&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;niquests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://1.1.1.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now how should we access the &lt;code&gt;max_age&lt;/code&gt; from header &lt;code&gt;Nel&lt;/code&gt; or &lt;code&gt;Report-To&lt;/code&gt; or &lt;code&gt;Strict-Transport-Security&lt;/code&gt; ASAP?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oheaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_age&lt;/span&gt;
&lt;span class="c1"&gt;# and
&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oheaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report_to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_age&lt;/span&gt;
&lt;span class="c1"&gt;# also
&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oheaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strict_transport_security&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_age&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are very much awake, yes, this is now that easy! Pseudo-oriented object headers are long overdue.&lt;/p&gt;

&lt;h3&gt;
  
  
  😫 (VIII) Fixed known (painful) issues
&lt;/h3&gt;

&lt;p&gt;We've heard you, we know how painful some encounters are, especially with bugs!&lt;br&gt;
Here is an extract of what we've fixed for you! (in addition to the In-memory client certificate)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An invalid content-type definition would cause the charset to be evaluated to True, thus making the program crash.&lt;/li&gt;
&lt;li&gt;Given proxies could be mutated when environment proxies were evaluated and injected. This package should not modify your inputs.&lt;/li&gt;
&lt;li&gt;Fixed &lt;code&gt;Transfer-Encoding&lt;/code&gt; wrongfully added to headers when the body is actually of length 0.&lt;/li&gt;
&lt;li&gt;Function &lt;code&gt;proxy_bypass_registry&lt;/code&gt; for Windows may be fooled by insufficient control on our end.&lt;/li&gt;
&lt;li&gt;Unattended override of manually provided Authorization if .netrc existed with an eligible entry.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚦 (IX) Type annotated
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Knows you are doing right from the start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M5H2onvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283504-6cb611cb-2789-4690-984a-543226eb5b36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M5H2onvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283504-6cb611cb-2789-4690-984a-543226eb5b36.png" alt="code-assist" width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Powerful IDEs like &lt;a href="https://www.jetbrains.com/pycharm/"&gt;PyCharm&lt;/a&gt; can easily leverage our definitions to guide you through the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3yYHWWG6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283508-4bb28224-45cb-4c04-be4f-9c1e53a08f97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3yYHWWG6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283508-4bb28224-45cb-4c04-be4f-9c1e53a08f97.png" alt="probable-error" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just spotted something weird. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tHx6n_pT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283509-d3057d37-029a-444a-a165-1983d82baf8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tHx6n_pT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-production-user-asset-6210df.s3.amazonaws.com/9326700/282283509-d3057d37-029a-444a-a165-1983d82baf8a.png" alt="why-error" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...And here's why!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 Yes, actually typeshed already has some definitions for Requests, but they are (most of them) incomplete.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ☎️ (X) Plain better ~customer~"developer"-care
&lt;/h3&gt;

&lt;p&gt;It's a cliché linked to small entities, but, it can make all the difference.&lt;br&gt;
You won't felt wronged or thrown away because I am deeply convinced that knowledge is best acquired by good confrontations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's a promise, we'll never say to go away to Stackoverflow. We will assume this position forever. For us, even the most insignificant Q&amp;amp;A is like a real commit to the project.&lt;/li&gt;
&lt;li&gt;We have a great turnover for responses to your concerns, proposals or criticisms.&lt;/li&gt;
&lt;li&gt;Reports are good, always. Never be scared of filling in an issue even if you are not sure of anything. Even less to submit a PR.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ➕ Bonus
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Main feature matrix&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client / Features Matrix&lt;/th&gt;
&lt;th&gt;(Async)&lt;/th&gt;
&lt;th&gt;Multiplexed&lt;/th&gt;
&lt;th&gt;HTTP/1.1&lt;/th&gt;
&lt;th&gt;HTTP/2&lt;/th&gt;
&lt;th&gt;HTTP/3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;requests&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aiohttp&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;httpx&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅***&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/jawah/niquests"&gt;niquests&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;✅**&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt; is compatible with Python, and PyPy 3.7+, and we remain attached to not dropping any interpreter version unless strictly required to!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Versioning&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All releases on the same Major version will be guaranteed to be backward compatible. Unless forced to.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Did you mention Async?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes! &lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt; support &lt;a href="https://niquests.readthedocs.io/en/latest/user/quickstart.html#async-session"&gt;Async&lt;/a&gt;, while it's not true Async**, it serves the purpose of having non-blocking code in your event loop! You may combine Async with the usage of a multiplexed connection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Drop-in replacement for Requests&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You have nothing to do (most of the time) to switch to &lt;a href="https://github.com/jawah/niquests"&gt;Niquests&lt;/a&gt;. A simple &lt;code&gt;CTRL+R&lt;/code&gt; on &lt;code&gt;import requests&lt;/code&gt; to &lt;code&gt;import niquests as requests&lt;/code&gt; suffice! or on &lt;code&gt;from requests import&lt;/code&gt; to &lt;code&gt;from niquests import&lt;/code&gt;.&lt;br&gt;
Any trouble migrating? Come and let us know what happened. We will find a way, trust me.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Much more you don't know about&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many topics (e.g. improvements) haven't been shared in this article, come and discover &lt;a href="https://github.com/jawah/niquests/blob/main/HISTORY.md"&gt;all the great things we delivered!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FYI Niquests is more resilient to most bot detectors provided you'd put the right headers &lt;em&gt;(e.g. UA, Sec-CH-UA, etc..)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;**: We ported &lt;code&gt;sync_to_async&lt;/code&gt; used in asgiref, as Niquests is mostly thread-safe. It is transparent for the end-user.&lt;/small&gt;&lt;br&gt;&lt;br&gt;
&lt;small&gt;***: Not enabled by default.&lt;/small&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📆 What's next?
&lt;/h3&gt;

&lt;p&gt;With your support, we can project ourselves much more than 2024!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Advanced proxy combination&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today we support tunneling from HTTP/1.1, meaning we can to HTTP/1.1 -&amp;gt; HTTP/2 but not HTTP/2 -&amp;gt; HTTP/2 or even less&lt;br&gt;
HTTP/3 -&amp;gt; HTTP/2 or 3.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support for DoH, and DoQ&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DNS over QUIC is soon to be a must-have.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.cloudflare.com/announcing-encrypted-client-hello/"&gt;ECH&lt;/a&gt; when using QUIC.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;True Async top to bottom&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Will take some time. Not urgent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Advanced scheduling for multiplexed connections&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Support for request priority weight.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Faster! Faster!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll never settle. This is just the starting block.&lt;/p&gt;

&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source:&lt;/em&gt;   &lt;a href="https://github.com/jawah/niquests"&gt;https://github.com/jawah/niquests&lt;/a&gt;&lt;br&gt;
&lt;em&gt;PyPI:&lt;/em&gt;     &lt;a href="https://pepy.tech/project/niquests"&gt;https://pepy.tech/project/niquests&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Docs:&lt;/em&gt;     &lt;a href="https://niquests.readthedocs.io/en/latest/"&gt;https://niquests.readthedocs.io/en/latest/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Thank you for reading!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the end, I got what I wanted, migrated all my projects, enabled multiplexing where it was pertinent, and gained a substantial boost.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Don't screw up reading Headers, Keep It Simple, Stupid ! HTTP or IMAP !</title>
      <dc:creator>TAHRI Ahmed R.</dc:creator>
      <pubDate>Tue, 17 Mar 2020 14:01:04 +0000</pubDate>
      <link>https://forem.com/ousret/don-t-screw-up-reading-headers-keep-it-simple-stupid-http-or-imap-1pje</link>
      <guid>https://forem.com/ousret/don-t-screw-up-reading-headers-keep-it-simple-stupid-http-or-imap-1pje</guid>
      <description>&lt;h3&gt;
  
  
  Why ? Indeed Why ?!
&lt;/h3&gt;

&lt;p&gt;No matters your religion, IMAP4 or HTTP, you should not worries about accessing easily header and associated attributes, adjectives or values. &lt;/p&gt;

&lt;p&gt;But even with modern language like Python we still have those :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;';'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'='&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I have seen so much chunk of code like this, trying to deal with them, often in a risky way to take care of more important part of the code. In face of that I say no more !&lt;/p&gt;

&lt;p&gt;Do not forget that headers are not 1 TO 1. One header can be repeated multiple time and attribute can have multiple value within the same header. So representing them by using a ´dict()´ is not a good idea even if using case insensible dict !&lt;/p&gt;
&lt;h3&gt;
  
  
  Project Kiss-Headers
&lt;/h3&gt;

&lt;p&gt;That is why I created a project that would remove the pain of dealing with them no matter where they came from.&lt;/p&gt;

&lt;p&gt;It was absolutely clear that they was something missing in the python community. From the project ´psf/requests´ to ´tornado´, each one have it own implementation, each one are also incomplete. The best so far I have seen is the one from ´httpx´ project.&lt;/p&gt;

&lt;p&gt;Mine is not perfect, but has its amount of sweetness. Beginning from auto-completion capability in python interpreter or in your beloved IDE. It should dramatically reduce stress and pain when encountering them. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Ousret"&gt;
        Ousret
      &lt;/a&gt; / &lt;a href="https://github.com/Ousret/kiss-headers"&gt;
        kiss-headers
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Python package for HTTP/1.1 style headers. Parse headers to objects. Most advanced available structure for http headers.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1 id="user-content-http-headers-the-complete-toolkit--"&gt;
&lt;a class="heading-link" href="https://github.com/Ousret/kiss-headers#http-headers-the-complete-toolkit--"&gt;HTTP Headers, the Complete Toolkit 🧰 &lt;/a&gt;&lt;a href="https://twitter.com/intent/tweet?text=Python%20library%20for%20oriented%20object%20HTTP%20style%20headers.&amp;amp;url=https://www.github.com/Ousret/kiss-headers&amp;amp;hashtags=python,headers,opensource" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/90bc908826728c0e4261acfff5619fd732c7be2b2a00624fce6363c9a3623c90/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f75726c2f687474702f736869656c64732e696f2e7376673f7374796c653d736f6369616c"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;
  &lt;sup&gt;Object-oriented headers. Kind of structured headers.&lt;/sup&gt;&lt;br&gt;
  &lt;a href="https://pypi.org/project/kiss-headers/" rel="nofollow"&gt;
     &lt;img src="https://camo.githubusercontent.com/3b089d29c02b466ce1be14bdfe02546255582ec755e1d31101a345f8ffadaaba/68747470733a2f2f696d672e736869656c64732e696f2f707970692f707976657273696f6e732f6b6973732d686561646572732e7376673f6f72616e67653d626c7565"&gt;
  &lt;/a&gt;
  &lt;a href="https://codecov.io/gh/Ousret/kiss-headers" rel="nofollow"&gt;
      &lt;img src="https://camo.githubusercontent.com/4375eaa1d7ce421ddc47b18585c61ed731e245ed046469eee5eee0d85bb06f1c/68747470733a2f2f636f6465636f762e696f2f67682f4f75737265742f6b6973732d686561646572732f6272616e63682f6d61737465722f67726170682f62616467652e737667"&gt;
  &lt;/a&gt;
  &lt;a href="https://pepy.tech/project/kiss-headers/" rel="nofollow"&gt;
     &lt;img alt="Download Count Total" src="https://camo.githubusercontent.com/b0840e4540aff6a0c6925dfabada93ef6fba02ff31e289b0ed6c8a06a5639202/68747470733a2f2f706570792e746563682f62616467652f6b6973732d68656164657273"&gt;
  &lt;/a&gt;
&lt;/p&gt;
&lt;h3 id="user-content--why"&gt;&lt;a class="heading-link" href="https://github.com/Ousret/kiss-headers#-why"&gt;❓ Why&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;No matter if you are currently dealing with code using HTTP or IMAP &lt;em&gt;(message, email)&lt;/em&gt;, you should not worry about easily accessing and exploiting headers.&lt;/p&gt;
&lt;p&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/9326700/77257881-55866300-6c77-11ea-820c-7550e6bdeee7.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--feG4-jZt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://user-images.githubusercontent.com/9326700/77257881-55866300-6c77-11ea-820c-7550e6bdeee7.gif" alt="using kiss-headers from python interpreter"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I have seen so many chunks of code trying to deal with these headers; often I saw this implementation:&lt;/p&gt;
&lt;div class="highlight highlight-source-python notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;# No more of that!&lt;/span&gt;
&lt;span class="pl-s1"&gt;charset&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;headers&lt;/span&gt;[&lt;span class="pl-s"&gt;'Content-Type'&lt;/span&gt;].&lt;span class="pl-en"&gt;split&lt;/span&gt;(&lt;span class="pl-s"&gt;';'&lt;/span&gt;)[&lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;1&lt;/span&gt;].&lt;span class="pl-en"&gt;split&lt;/span&gt;(&lt;span class="pl-s"&gt;'='&lt;/span&gt;)[&lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;1&lt;/span&gt;].&lt;span class="pl-en"&gt;replace&lt;/span&gt;(&lt;span class="pl-s"&gt;'"'&lt;/span&gt;, &lt;span class="pl-s"&gt;''&lt;/span&gt;)
&lt;span class="pl-c"&gt;# That too..&lt;/span&gt;
&lt;span class="pl-s1"&gt;response&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;get&lt;/span&gt;(
    &lt;span class="pl-s"&gt;"https://httpbin.org/headers"&lt;/span&gt;,
    &lt;span class="pl-s1"&gt;headers&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;{
        &lt;span class="pl-s"&gt;"user-agent"&lt;/span&gt;: &lt;span class="pl-s"&gt;"custom/2.22"&lt;/span&gt;,
        &lt;span class="pl-s"&gt;"referer"&lt;/span&gt;: &lt;span class="pl-s"&gt;"https://www.google.com"&lt;/span&gt;,
        &lt;span class="pl-s"&gt;"accept"&lt;/span&gt;: &lt;span class="pl-s"&gt;"text/html"&lt;/span&gt;,
        &lt;span class="pl-s"&gt;"accept-language"&lt;/span&gt;: &lt;span class="pl-s"&gt;"en-US"&lt;/span&gt;,
        &lt;span class="pl-s"&gt;"custom-header-xyz"&lt;/span&gt;: &lt;span class="pl-s"&gt;"hello; charset=utf-8"&lt;/span&gt;
    }
)&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Scroll down and see how you could do it from now on.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="user-content--features"&gt;&lt;a class="heading-link" href="https://github.com/Ousret/kiss-headers#-features"&gt;🔪 Features&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;kiss-headers&lt;/code&gt; is a basic library that allow you to handle headers as…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Ousret/kiss-headers"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Imaging being capable of doing those things :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CMUoJfuJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://user-images.githubusercontent.com/9326700/76808050-bf6dbb00-67e6-11ea-9799-d2b20068bbb7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CMUoJfuJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://user-images.githubusercontent.com/9326700/76808050-bf6dbb00-67e6-11ea-9799-d2b20068bbb7.gif" alt="python kiss-headers" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  End note
&lt;/h3&gt;

&lt;p&gt;I am pretty confident that those kind of libraries that serve reducing time spent on the little things would actually help make the life of a programmer easier. And spent a lot more time on something else.&lt;/p&gt;

&lt;p&gt;So, do not hesitate, when you are encountering a case that respect those criteria : (i) stupid (ii) repeatably reinventing the wheel (iii) everyone is facing this issue at least once. (iv) break the loop by contributing.&lt;/p&gt;

&lt;p&gt;Thank you :)&lt;/p&gt;

</description>
      <category>python</category>
      <category>github</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Make ✨ 🍰 ✨ requests act like jQuery when passing nested objects to data/payload</title>
      <dc:creator>TAHRI Ahmed R.</dc:creator>
      <pubDate>Sun, 23 Feb 2020 19:11:37 +0000</pubDate>
      <link>https://forem.com/ousret/make-requests-act-like-jquery-when-passing-nested-objects-to-data-payload-55pa</link>
      <guid>https://forem.com/ousret/make-requests-act-like-jquery-when-passing-nested-objects-to-data-payload-55pa</guid>
      <description>&lt;h2&gt;
  
  
  Once upon a time
&lt;/h2&gt;

&lt;p&gt;I, once was stuck when using python-requests in order to poke a PHP backend.&lt;/p&gt;

&lt;p&gt;You may currently face this issue as Requests does not support dict with depth greater than 1 on parameter data for a x-www-form-urlencoded based request.&lt;/p&gt;

&lt;p&gt;PHP/jQuery $.params support this syntax out of the box. As requests is not about supporting the way PHP/jQuery $.params work internally, this PR maybe doomed.&lt;/p&gt;

&lt;h2&gt;
  
  
  To reproduce
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pprint&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pprint&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'https://api.ipify.org?format=json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;'auth_user'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'auth_pwd'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'json_data'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;'operation'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'core/get'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;'class'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'ServiceChange'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'SELECT ServiceChange WHERE id=429'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;'output_fields'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'request_state'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s"&gt;'t'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What happen now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# auth_user=test&amp;amp;auth_pwd=test&amp;amp;json_data=operation&amp;amp;json_data=class&amp;amp;json_data=key&amp;amp;json_data=output_fields&amp;amp;json_data=h
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something should happen when passing nested dict. This behavior is wrong. We loss data between passing and sending. At least raise a warning or whatnot.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ What should have happened
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# auth_user=test&amp;amp;auth_pwd=test&amp;amp;json_data%5Boperation%5D=core%2Fget&amp;amp;json_data%5Bclass%5D=ServiceChange&amp;amp;json_data%5Bkey%5D=SELECT+ServiceChange+WHERE+id%3D429&amp;amp;json_data%5Boutput_fields%5D=request_state&amp;amp;json_data%5Bh%5D%5Bt%5D=1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why ?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[x] &lt;code&gt;requests&lt;/code&gt; is on a higher level than urllib, this does not make sense in &lt;code&gt;urllib&lt;/code&gt;, but can in &lt;code&gt;requests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[x] PHP and Ruby on Rails support this syntax out of the box, and is used by more than 30 % of the remote backends.&lt;/li&gt;
&lt;li&gt;[x] This PR would make it more beginner friendly, advanced users that are looking for &lt;code&gt;test=foo&amp;amp;test=baz&lt;/code&gt; are more likely to encode their payload manually. Not the other way around.&lt;/li&gt;
&lt;li&gt;[x] This change does not break or change actual behavior expect for the multivalued param case which is a very rare case in my opinion. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use this patch ?
&lt;/h2&gt;

&lt;p&gt;Simply use my fork for the time being, you could support my PR too.&lt;/p&gt;

&lt;p&gt;Patch : &lt;a href="https://github.com/Ousret/requests/tree/patch-1"&gt;tree/patch-1&lt;/a&gt;&lt;br&gt;
PR : &lt;a href="https://github.com/psf/requests/pull/5253"&gt;https://github.com/psf/requests/pull/5253&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>http</category>
      <category>jquery</category>
    </item>
    <item>
      <title>Be more productive 🚀, less robotic 🤖 when inbox need too much attention</title>
      <dc:creator>TAHRI Ahmed R.</dc:creator>
      <pubDate>Sun, 23 Feb 2020 17:14:15 +0000</pubDate>
      <link>https://forem.com/ousret/be-more-productive-less-robotic-when-inbox-need-to-much-attention-3ce4</link>
      <guid>https://forem.com/ousret/be-more-productive-less-robotic-when-inbox-need-to-much-attention-3ce4</guid>
      <description>&lt;p&gt;I worked in a french company that is using iTop internally.&lt;br&gt;
This project was born out of a specific need which hinted at the possibility of a much more open and generic case. &lt;/p&gt;

&lt;p&gt;A company may face this problem:&lt;/p&gt;

&lt;p&gt;How to manage an interoperability of services with others based solely on mails ?&lt;/p&gt;

&lt;p&gt;At first they were using 'incoming mail' embedded module. Defined by &lt;em&gt;Combodo/iTop&lt;/em&gt; as "This extension runs in the background to scan the defined mail inbox(es) and either create or update tickets based on the content of the incoming emails".&lt;/p&gt;

&lt;p&gt;With the old solution (Incoming Mail):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identification is very limited and restricted&lt;/li&gt;
&lt;li&gt;It is mandatory to create IMAP files for each task&lt;/li&gt;
&lt;li&gt;Scanner actions are limited to simple operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hermes offers a complete solution to what iTop/Incoming Mail cannot provide. A better way to identify mail and a more versatile way to react to it.&lt;/p&gt;

&lt;p&gt;There is absolutely no need to know how to code in order to get started.&lt;/p&gt;

&lt;p&gt;Right now, it is available in french only. It is open source and free to use. This project is "i18n ready", that mean it can be translated as easily as iTop was. That does not mean it is not translatable by Google. &lt;/p&gt;

&lt;p&gt;It is as easy as typing &lt;code&gt;docker-compose up&lt;/code&gt; in shell to get started !&lt;/p&gt;

&lt;p&gt;Every contribution and feedbacks are welcome.&lt;br&gt;
Project available on GitHub : &lt;a href="https://github.com/Ousret/hermes"&gt;https://github.com/Ousret/hermes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If needed, "Google-provided" &lt;a href="https://translate.google.fr/translate?sl=fr&amp;amp;tl=en&amp;amp;u=https%3A%2F%2Fgithub.com%2FOusret%2Fhermes"&gt;english translation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QSKR3-iY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/gt7id1pppfeadqm3gl58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QSKR3-iY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/gt7id1pppfeadqm3gl58.png" alt="Web UI" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>management</category>
      <category>python</category>
      <category>imap</category>
    </item>
    <item>
      <title>How I used Python to solve declareless encoding madness</title>
      <dc:creator>TAHRI Ahmed R.</dc:creator>
      <pubDate>Wed, 02 Oct 2019 09:36:12 +0000</pubDate>
      <link>https://forem.com/ousret/how-i-used-python-to-solve-declareless-encoding-madness-30n1</link>
      <guid>https://forem.com/ousret/how-i-used-python-to-solve-declareless-encoding-madness-30n1</guid>
      <description>&lt;p&gt;There is a very old issue regarding "encoding detection" in a text file that has been partially resolved by a program like &lt;a href="https://github.com/chardet/chardet"&gt;Chardet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nowadays, one could argue that this issue is not actually one. Indeed, most standards are providing a way to declare the encoding, like in HTTP specifications.&lt;/p&gt;

&lt;p&gt;But the reality is different, a huge part of the internet still have content with an unknown encoding. One could point out subrip subtitle (SRT) for instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qjotDeeN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/qhq364rdgznev2belwmw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qjotDeeN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/qhq364rdgznev2belwmw.gif" alt="SRT Madness" width="290" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why a popular package like &lt;a href="https://github.com/psf/Requests"&gt;Requests&lt;/a&gt; list Chardet as a requirement to guess apparent encoding on remote resources.&lt;/p&gt;

&lt;p&gt;But there is a catch. First of all, libraries like Chardet are unreliable, unmaintained and sometime even disliked publicly by their owner.&lt;/p&gt;

&lt;p&gt;Nearly all popular libraries are using the same idea, for each code page or encoding they want to detect they create a specific probe. They are trying to identify originating encoding.&lt;/p&gt;

&lt;p&gt;The first thing I did not like is the idea of single prober per encoding table that could lead to hard coding specifications. Secondly, I am convinced that we should not care about the originating encoding, that because two different tables can produce two identical files.&lt;/p&gt;

&lt;p&gt;This is where I came with an alternative. &lt;strong&gt;A crazy one :&lt;/strong&gt; Using brute-force to get sense out of a given content. For each encoding there is.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exclude encoding that does not fit content at all&lt;/li&gt;
&lt;li&gt;Measure observable chaos once opened, identify things like "жГЪСЭ Ян"&lt;/li&gt;
&lt;li&gt;Measure coherence by letter appearances frequencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By not creating specific probe per encoding I was able to provide detection for around 90 encodings ! That's more than twice compared to Chardet and it is actually more reliable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yawq0bpp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/10ummszb2fjzk1w5cwu1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yawq0bpp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/10ummszb2fjzk1w5cwu1.PNG" alt="Features" width="783" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I present to you &lt;a href="https://github.com/Ousret/charset_normalizer"&gt;Charset Normalizer&lt;/a&gt;. The real first universal charset detector. Or if currently busy or whatnot, try it online via &lt;a href="https://charsetnormalizerweb.ousret.now.sh/"&gt;this url&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V6ORKpBV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/54lzk6vbi0u8le6v1aw8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V6ORKpBV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/54lzk6vbi0u8le6v1aw8.gif" alt="Demo" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>github</category>
    </item>
    <item>
      <title>How I used brute-force where I least expected it</title>
      <dc:creator>TAHRI Ahmed R.</dc:creator>
      <pubDate>Tue, 03 Sep 2019 08:08:58 +0000</pubDate>
      <link>https://forem.com/ousret/how-i-used-brute-force-where-i-least-expected-it-3c3l</link>
      <guid>https://forem.com/ousret/how-i-used-brute-force-where-i-least-expected-it-3c3l</guid>
      <description>&lt;p&gt;There is a very old &lt;em&gt;issue&lt;/em&gt; regarding "encoding detection" in a text file that has been partially resolved by a program like &lt;a href="https://github.com/chardet/chardet"&gt;Chardet&lt;/a&gt;. I did not like the idea of single prober per encoding table that could lead to hard coding specifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L7E9j-uy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/abb4qex3neg66gexvj94.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L7E9j-uy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/abb4qex3neg66gexvj94.gif" alt="Alt Text" width="290" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to challenge the existing methods of discovering originating encoding. &lt;/p&gt;

&lt;p&gt;You could consider this issue as obsolete because of current norms :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should indicate used charset encoding as described in standards&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the reality is different, a huge part of the internet still have content with an unknown encoding. (One could point out subrip subtitle (SRT) for instance)&lt;/p&gt;

&lt;p&gt;This is why a popular package like &lt;a href="https://github.com/psf/Requests"&gt;Requests&lt;/a&gt; embed Chardet to guess apparent encoding on remote resources.&lt;/p&gt;

&lt;p&gt;You should know that :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should not care about the originating charset encoding, that because two different table can produce two identical files.&lt;/li&gt;
&lt;li&gt;BOM (&lt;a href="https://en.wikipedia.org/wiki/Byte_order_mark"&gt;byte order mark&lt;/a&gt;) is not universal and concern only a tiny number of encodings and not only Unicode !&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm brute-forcing on three premises (in that order) :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Binaries fit encoding table&lt;/li&gt;
&lt;li&gt;Chaos&lt;/li&gt;
&lt;li&gt;Coherence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Chaos :&lt;/em&gt;  I opened hundred of text files, written by humans, with the wrong encoding table. I observed, then I established some ground rules about what is obvious when it seems like a mess. I know that my interpretation of what is chaotic is very subjective, feel free to contribute to improve or rewrite it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coherence :&lt;/em&gt; For each language there is on earth (the best we can), we have computed letter appearance occurrences ranked. So I thought that those intel are worth something here. So I use those records against the decoded text to check if I can detect intelligent design.&lt;/p&gt;

&lt;p&gt;So I present to you &lt;a href="https://github.com/Ousret/charset_normalizer"&gt;Charset Normalizer&lt;/a&gt;. The Real First Universal Charset Detector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rqu0nWVt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ooz107intsclrpzkkj9h.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rqu0nWVt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ooz107intsclrpzkkj9h.PNG" alt="Alt Text" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to help though testing or contributing.&lt;/p&gt;

</description>
      <category>python</category>
      <category>challenge</category>
    </item>
  </channel>
</rss>
