<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Aki</title>
        <link>https://paragraph.com/@aki</link>
        <description>I ALSO DON'T KNOW</description>
        <lastBuildDate>Tue, 26 May 2026 17:46:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Aki</title>
            <url>https://storage.googleapis.com/papyrus_images/0efd74e8d3997ac45bd4b4ea4ea245a955d6aa944a793f62f9c95f4e68c37149.jpg</url>
            <link>https://paragraph.com/@aki</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Dive into requests]]></title>
            <link>https://paragraph.com/@aki/dive-into-requests</link>
            <guid>5P1QKqEWPK96kFQedOPp</guid>
            <pubDate>Mon, 04 Apr 2022 15:20:02 GMT</pubDate>
            <description><![CDATA[Intro“The requests library is more powerful and easier to use than the urllib.request module from the Python 3 standard library. In fact, requests is considered a model Pythonic API.”As the most well-known Python HTTP library, requests actually emphasizes the major advantage of Python, i.e., Various implementations lead to the same result. However, there’re some tricks, or so called best practices in requests which is hidden under the source codes and manuals.A normal exampleA normal usage ex...]]></description>
            <content:encoded><![CDATA[<h2 id="h-intro" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Intro</h2><blockquote><p>“The requests library is more powerful and easier to use than the urllib.request module from the Python 3 standard library. In fact, requests is considered a model Pythonic API.”</p></blockquote><p>As the most well-known Python HTTP library, <code>requests</code> actually emphasizes the major advantage of Python, i.e., Various implementations lead to the same result.</p><p>However, there’re some tricks, or so called best practices in <code>requests</code> which is hidden under the source codes and manuals.</p><h2 id="h-a-normal-example" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">A normal example</h2><p>A normal usage example of <code>requests</code> may look like below snippets.</p><p>There’re several problems in the snippets:</p><ol><li><p>Use <code>while</code> loop to retry requests. You may have to write those codes for hundreds of times in different functions.</p></li><li><p>There’s no exception catching and throwing. For instance, <code>json.loads</code> may throw an exception if <code>res.text</code> is not an instance of <code>bytes</code> or <code>bytearray</code>.</p></li><li><p>Only use <code>status_code</code> to judge whether the requests is successful, which is hard to handle tricky situations, <code>status_code = 200, code = 400</code> for instance.</p></li><li><p>etc.</p></li></ol><pre data-type="codeBlock" text="import requests
import json

def my_requests(data):
    headers = {&apos;Auth-Pass-Key&apos;: &apos;abc&apos;,
               &apos;Content-Type&apos;: &apos;application/json&apos;}
    retry_index = 0
    while retry_index &lt;= 3:
        res = requests.post(&quot;url&quot;, json=data, headers=headers)
        if res.status_code != 200:
            retry_index += 1
        else:
            return json.loads(res.text)
    return None
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">requests</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">json</span>

<span class="hljs-title">def</span> <span class="hljs-title">my_requests</span>(<span class="hljs-title">data</span>):
    <span class="hljs-title">headers</span> <span class="hljs-operator">=</span> {<span class="hljs-string">'Auth-Pass-Key'</span>: <span class="hljs-string">'abc'</span>,
               <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>}
    <span class="hljs-title">retry_index</span> <span class="hljs-operator">=</span> 0
    <span class="hljs-title"><span class="hljs-keyword">while</span></span> <span class="hljs-title">retry_index</span> <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> 3:
        <span class="hljs-title">res</span> <span class="hljs-operator">=</span> <span class="hljs-title">requests</span>.<span class="hljs-title">post</span>(<span class="hljs-string">"url"</span>, <span class="hljs-title">json</span><span class="hljs-operator">=</span><span class="hljs-title">data</span>, <span class="hljs-title">headers</span><span class="hljs-operator">=</span><span class="hljs-title">headers</span>)
        <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">res</span>.<span class="hljs-title">status_code</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> 200:
            <span class="hljs-title">retry_index</span> <span class="hljs-operator">+</span><span class="hljs-operator">=</span> 1
        <span class="hljs-title"><span class="hljs-keyword">else</span></span>:
            <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">json</span>.<span class="hljs-title">loads</span>(<span class="hljs-title">res</span>.<span class="hljs-title">text</span>)
    <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">None</span>
</code></pre><h2 id="h-unified-calling" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unified calling</h2><p>Actually, some codes in above snippet can be replaced by built-in function/property in <code>requests</code> library.</p><h3 id="h-requests" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">requests</h3><pre data-type="codeBlock" text="# BEFORE
res = requests.post(&quot;url&quot;, json=data, headers=headers)
# AFTER
res = requests.request(&quot;POST&quot;, url=&quot;url&quot;, json=data, headers=headers)
"><code># BEFORE
res <span class="hljs-operator">=</span> requests.post(<span class="hljs-string">"url"</span>, json<span class="hljs-operator">=</span>data, headers<span class="hljs-operator">=</span>headers)
# AFTER
res <span class="hljs-operator">=</span> requests.request(<span class="hljs-string">"POST"</span>, url<span class="hljs-operator">=</span><span class="hljs-string">"url"</span>, json<span class="hljs-operator">=</span>data, headers<span class="hljs-operator">=</span>headers)
</code></pre><p>In this way, you can customize your own common requests, to add logs, add wrappers, etc.</p><h3 id="h-json" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">JSON</h3><pre data-type="codeBlock" text="# BEFORE
return json.loads(res.text)
# AFTER
return res.json()
"><code># BEFORE
<span class="hljs-keyword">return</span> json.loads(res.text)
# AFTER
<span class="hljs-keyword">return</span> res.json()
</code></pre><p>You don’t have to call <code>json.loads</code> yourself, instead, just <code>res.json()</code> is enough, which will also automatically raise JSONDecodeError under scene.</p><pre data-type="codeBlock" text="# BEFORE
if res.status_code != 200:
# AFTER
if res:
# OR:
res.raise_for_status()
"><code><span class="hljs-comment"># BEFORE</span>
<span class="hljs-string">if</span> <span class="hljs-string">res.status_code</span> <span class="hljs-type">!=</span> <span class="hljs-attr">200:</span>
<span class="hljs-comment"># AFTER</span>
<span class="hljs-attr">if res:</span>
<span class="hljs-comment"># OR:</span>
<span class="hljs-string">res.raise_for_status()</span>
</code></pre><p>based on the <code>__bool__</code> overloaded dunder method in Response object. It will return True if status_code is less than 400. The second way will raise a HTTPError with context when status_code is between 400 and 600.</p><h3 id="h-headers" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">headers</h3><pre data-type="codeBlock" text="# BEFORE
headers = {&apos;Auth-Pass-Key&apos;: &apos;abc&apos;,
           &apos;Content-Type&apos;: &apos;application/json&apos;}
res = requests.post(&quot;url&quot;, json=data, headers=headers)
# AFTER
from requests.auth import AuthBase
class AuthPass(AuthBase):
    def __init__(self, pass):
        self.pass = pass

    def __call__(self, r):
        &quot;&quot;&quot;Attach an API token to a custom auth header.&quot;&quot;&quot;
        r.headers[&apos;Auth-Pass-Key&apos;] = f&apos;{self.pass}&apos;
        return r
res = requests.post(&quot;url&quot;, json=data, auth=AuthPass(&quot;abc&quot;))
"><code><span class="hljs-comment"># BEFORE</span>
headers = {<span class="hljs-string">'Auth-Pass-Key'</span>: <span class="hljs-string">'abc'</span>,
           <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>}
res = requests.post(<span class="hljs-string">"url"</span>, json=data, headers=headers)
<span class="hljs-comment"># AFTER</span>
<span class="hljs-keyword">from</span> requests.auth <span class="hljs-keyword">import</span> AuthBase
<span class="hljs-keyword">class</span> <span class="hljs-title class_">AuthPass</span>(<span class="hljs-title class_ inherited__">AuthBase</span>):
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, <span class="hljs-keyword">pass</span></span>):
        self.<span class="hljs-keyword">pass</span> = <span class="hljs-keyword">pass</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params">self, r</span>):
        <span class="hljs-string">"""Attach an API token to a custom auth header."""</span>
        r.headers[<span class="hljs-string">'Auth-Pass-Key'</span>] = <span class="hljs-string">f'<span class="hljs-subst">{self.<span class="hljs-keyword">pass</span>}</span>'</span>
        <span class="hljs-keyword">return</span> r
res = requests.post(<span class="hljs-string">"url"</span>, json=data, auth=AuthPass(<span class="hljs-string">"abc"</span>))
</code></pre><p>use <code>json=data</code> will automatically add <code>&apos;Content-Type&apos;: &apos;application/json&apos;</code> to headers. use <code>AuthBase</code> object is better than use headers variable which contains hard coded <code>Auth-Pass-Key</code></p><h2 id="h-self-defined-requests" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">self defined requests</h2><pre data-type="codeBlock" text="import requests

@auto_retry()
def common_requests(method: str, **kwargs) -&gt; Optional[Dict]:
    resp: Response = requests.request(method, **kwargs)
    logger.info(f&quot;{resp.status_code}, {resp.text}&quot;)
    if is_request_success(resp):
        return resp.json()  # may raise JSONDecodeError
    return None

def my_post_requests(data: Dict, auth: AuthBase) -&gt; Optional[Dict]:
    return common_requests(&quot;post&quot;, json=data, auth=auth)

def my_get_requests(params: Dict) -&gt; Optional[Dict]:
    return common_request(&quot;GET&quot;, params=params)
"><code><span class="hljs-keyword">import</span> requests

<span class="hljs-meta">@auto_retry()</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">common_requests</span>(<span class="hljs-params">method: <span class="hljs-built_in">str</span>, **kwargs</span>) -> <span class="hljs-type">Optional</span>[<span class="hljs-type">Dict</span>]:
    resp: Response = requests.request(method, **kwargs)
    logger.info(<span class="hljs-string">f"<span class="hljs-subst">{resp.status_code}</span>, <span class="hljs-subst">{resp.text}</span>"</span>)
    <span class="hljs-keyword">if</span> is_request_success(resp):
        <span class="hljs-keyword">return</span> resp.json()  <span class="hljs-comment"># may raise JSONDecodeError</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-keyword">def</span> <span class="hljs-title function_">my_post_requests</span>(<span class="hljs-params">data: <span class="hljs-type">Dict</span>, auth: AuthBase</span>) -> <span class="hljs-type">Optional</span>[<span class="hljs-type">Dict</span>]:
    <span class="hljs-keyword">return</span> common_requests(<span class="hljs-string">"post"</span>, json=data, auth=auth)

<span class="hljs-keyword">def</span> <span class="hljs-title function_">my_get_requests</span>(<span class="hljs-params">params: <span class="hljs-type">Dict</span></span>) -> <span class="hljs-type">Optional</span>[<span class="hljs-type">Dict</span>]:
    <span class="hljs-keyword">return</span> common_request(<span class="hljs-string">"GET"</span>, params=params)
</code></pre><p>In above snippets, a self-encapsulated requests functions is built. You can use it for all the normal requests before. In this way, you can write logs, verify the success of requests in just one place. You can even add more features in <code>common_requests</code>.</p><p><code>common_requests</code> takes the advantages of <code>**kwargs</code> and <code>@auto_retry()</code> wrapper (will talk in next section). You can also use <code>partialmethod</code> to create <code>common_post_requests</code> and etc.</p><h2 id="h-self-defined-check" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">self defined check</h2><pre data-type="codeBlock" text="def is_request_success(resp: Response) -&gt; bool:
    resp.raise_for_status()  # may raise HTTPError if 400 &lt;= resp.status_code &lt;= 600 
    if is_customize_success(resp.json()): # may raise JSONDecodeError
        return True
    else:
        raise RequestsFailedException(resp.text)

def is_customize_success(json_resp: Dict) -&gt; bool:
    if json_resp.get(&quot;code&quot;, 999) in [0, &quot;0&quot;]:
        return True
    if json_resp.get(&quot;status_code&quot;, 999) in [0, &quot;0&quot;]:
        return True
    return False
"><code>def is_request_success(resp: Response) <span class="hljs-operator">-</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>:
    resp.raise_for_status()  # may raise HTTPError <span class="hljs-keyword">if</span> <span class="hljs-number">400</span> <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> resp.status_code <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> <span class="hljs-number">600</span> 
    <span class="hljs-keyword">if</span> is_customize_success(resp.json()): # may raise JSONDecodeError
        <span class="hljs-keyword">return</span> True
    <span class="hljs-keyword">else</span>:
        raise RequestsFailedException(resp.text)

def is_customize_success(json_resp: Dict) <span class="hljs-operator">-</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>:
    <span class="hljs-keyword">if</span> json_resp.get(<span class="hljs-string">"code"</span>, <span class="hljs-number">999</span>) in [<span class="hljs-number">0</span>, <span class="hljs-string">"0"</span>]:
        <span class="hljs-keyword">return</span> True
    <span class="hljs-keyword">if</span> json_resp.get(<span class="hljs-string">"status_code"</span>, <span class="hljs-number">999</span>) in [<span class="hljs-number">0</span>, <span class="hljs-string">"0"</span>]:
        <span class="hljs-keyword">return</span> True
    <span class="hljs-keyword">return</span> False
</code></pre><p>A self defined check function is necessary to handle “BAD” API. You can use it all over the project rather than write them in individual requests function.</p><h2 id="h-auto-retry" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">auto retry</h2><pre data-type="codeBlock" text="def auto_retry(times=3, low=1, high=2):
    def outer_wrapper(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            retry_index: int = 0
            while retry_index &lt; times:
                try:
                    return f(*args, **kwargs)
                except (HTTPError, RequestsFailedException, JSONDecodeError) as e:
                    logger.error(e)
                    time.sleep(random.uniform(low, high))
                    retry_index += 1
            return None
        return wrapper
    return outer_wrapper
"><code>def auto_retry(times<span class="hljs-operator">=</span><span class="hljs-number">3</span>, low<span class="hljs-operator">=</span><span class="hljs-number">1</span>, high<span class="hljs-operator">=</span><span class="hljs-number">2</span>):
    def outer_wrapper(f):
        @functools.wraps(f)
        def wrapper(<span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs):
            retry_index: <span class="hljs-keyword">int</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
            <span class="hljs-keyword">while</span> retry_index <span class="hljs-operator">&#x3C;</span> times:
                <span class="hljs-keyword">try</span>:
                    <span class="hljs-keyword">return</span> f(<span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs)
                except (HTTPError, RequestsFailedException, JSONDecodeError) <span class="hljs-keyword">as</span> e:
                    logger.error(e)
                    time.sleep(random.uniform(low, high))
                    retry_index <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>
            <span class="hljs-keyword">return</span> None
        <span class="hljs-keyword">return</span> wrapper
    <span class="hljs-keyword">return</span> outer_wrapper
</code></pre><p>This wrapper will catch errors including <code>HTTPError, throw by res.raise_for_status()</code>, <code>JSONDecodeError, throw by res.json()</code>,<code>RequestsFailedException, self defined error, throw by is_request_success(resp)</code>. It will automatically retry requests for <code>times</code>, between each requests, the wrapper will wait for <code>low-high</code> seconds.</p><p>Or, you can use <code>Session</code> with <code>adapters</code> to retry automatically.</p><pre data-type="codeBlock" text="import requests
from requests.adapters import HTTPAdapter
from requests.exceptions import ConnectionError

my_adapter = HTTPAdapter(max_retries=3)

session = requests.Session()
session.mount(&apos;url&apos;, my_adapter)

try:
    session.get(&apos;url&apos;)
except ConnectionError as e:
    logger.error(e)
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">requests</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">requests</span>.<span class="hljs-title">adapters</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">HTTPAdapter</span>
<span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">requests</span>.<span class="hljs-title">exceptions</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">ConnectionError</span>

<span class="hljs-title">my_adapter</span> <span class="hljs-operator">=</span> <span class="hljs-title">HTTPAdapter</span>(<span class="hljs-title">max_retries</span><span class="hljs-operator">=</span>3)

<span class="hljs-title">session</span> <span class="hljs-operator">=</span> <span class="hljs-title">requests</span>.<span class="hljs-title">Session</span>()
<span class="hljs-title">session</span>.<span class="hljs-title">mount</span>(<span class="hljs-string">'url'</span>, <span class="hljs-title">my_adapter</span>)

<span class="hljs-title"><span class="hljs-keyword">try</span></span>:
    <span class="hljs-title">session</span>.<span class="hljs-title">get</span>(<span class="hljs-string">'url'</span>)
<span class="hljs-title">except</span> <span class="hljs-title">ConnectionError</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">e</span>:
    <span class="hljs-title">logger</span>.<span class="hljs-title"><span class="hljs-keyword">error</span></span>(<span class="hljs-title">e</span>)
</code></pre><h2 id="h-final" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Final</h2><p>Full set of snippet is as below. For co-current scenario, you may refer to <code>aiohttp</code>.</p><p>See requests manual for more information:</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.python-requests.org">https://docs.python-requests.org</a></p><pre data-type="codeBlock" text="import functools
import random
import time

from json import JSONDecodeError
from typing import Dict, List, Optional, Callable

import requests
from requests import Response, HTTPError
from requests.auth import AuthBase

from exceptions import RequestsFailedException

def auto_retry(times=3, low=1, high=2) -&gt; Callable:
    def outer_wrapper(f) -&gt; Callable:
        @functools.wraps(f)
        def wrapper(*args, **kwargs) -&gt; Optional[Dict]:
            retry_index: int = 0
            while retry_index &lt; times:
                try:
                    return f(*args, **kwargs)
                except (HTTPError, RequestsFailedException, JSONDecodeError) as e:
                    logger.error(e)
                    time.sleep(random.uniform(low, high))
                    retry_index += 1
            return None
        return wrapper
    return outer_wrapper


@auto_retry()
def common_requests(method: str, **kwargs) -&gt; Optional[Dict]:
    resp: Response = requests.request(method, **kwargs)
    if is_request_success(resp):
        return resp.json()  # raise JSONDecodeError
    return None


def is_request_success(resp: Response) -&gt; bool:
    resp.raise_for_status()  # will raise HTTPError if 400 &lt;= resp.status_code &lt;= 600
    if is_customize_success(resp.json()):
        return True
    else:
        raise RequestsFailedException(resp.text)


def is_customize_success(json_resp: Dict) -&gt; bool:
    if json_resp.get(&quot;code&quot;, 999) in [0, &quot;0&quot;]:
        return True
    if json_resp.get(&quot;status_code&quot;, 999) in [0, &quot;0&quot;]:
        return True
    return False

class AuthPass(AuthBase):
    def __init__(self, pass):
        self.pass = pass

    def __call__(self, r):
        &quot;&quot;&quot;Attach an API token to a custom auth header.&quot;&quot;&quot;
        r.headers[&apos;Auth-Pass-Key&apos;] = f&apos;{self.pass}&apos;
        return r

def my_post_requests(data: Dict, auth: AuthBase) -&gt; Optional[Dict]:
    return common_requests(&quot;post&quot;, json=data, auth=auth)

def my_get_requests(params: Dict) -&gt; Optional[Dict]:
    return common_request(&quot;GET&quot;, params=params)
"><code><span class="hljs-keyword">import</span> functools
<span class="hljs-keyword">import</span> random
<span class="hljs-keyword">import</span> time

<span class="hljs-keyword">from</span> json <span class="hljs-keyword">import</span> JSONDecodeError
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Dict</span>, <span class="hljs-type">List</span>, <span class="hljs-type">Optional</span>, <span class="hljs-type">Callable</span>

<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> requests <span class="hljs-keyword">import</span> Response, HTTPError
<span class="hljs-keyword">from</span> requests.auth <span class="hljs-keyword">import</span> AuthBase

<span class="hljs-keyword">from</span> exceptions <span class="hljs-keyword">import</span> RequestsFailedException

<span class="hljs-keyword">def</span> <span class="hljs-title function_">auto_retry</span>(<span class="hljs-params">times=<span class="hljs-number">3</span>, low=<span class="hljs-number">1</span>, high=<span class="hljs-number">2</span></span>) -> <span class="hljs-type">Callable</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">outer_wrapper</span>(<span class="hljs-params">f</span>) -> <span class="hljs-type">Callable</span>:
<span class="hljs-meta">        @functools.wraps(<span class="hljs-params">f</span>)</span>
        <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>(<span class="hljs-params">*args, **kwargs</span>) -> <span class="hljs-type">Optional</span>[<span class="hljs-type">Dict</span>]:
            retry_index: <span class="hljs-built_in">int</span> = <span class="hljs-number">0</span>
            <span class="hljs-keyword">while</span> retry_index &#x3C; times:
                <span class="hljs-keyword">try</span>:
                    <span class="hljs-keyword">return</span> f(*args, **kwargs)
                <span class="hljs-keyword">except</span> (HTTPError, RequestsFailedException, JSONDecodeError) <span class="hljs-keyword">as</span> e:
                    logger.error(e)
                    time.sleep(random.uniform(low, high))
                    retry_index += <span class="hljs-number">1</span>
            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
        <span class="hljs-keyword">return</span> wrapper
    <span class="hljs-keyword">return</span> outer_wrapper


<span class="hljs-meta">@auto_retry()</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">common_requests</span>(<span class="hljs-params">method: <span class="hljs-built_in">str</span>, **kwargs</span>) -> <span class="hljs-type">Optional</span>[<span class="hljs-type">Dict</span>]:
    resp: Response = requests.request(method, **kwargs)
    <span class="hljs-keyword">if</span> is_request_success(resp):
        <span class="hljs-keyword">return</span> resp.json()  <span class="hljs-comment"># raise JSONDecodeError</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>


<span class="hljs-keyword">def</span> <span class="hljs-title function_">is_request_success</span>(<span class="hljs-params">resp: Response</span>) -> <span class="hljs-built_in">bool</span>:
    resp.raise_for_status()  <span class="hljs-comment"># will raise HTTPError if 400 &#x3C;= resp.status_code &#x3C;= 600</span>
    <span class="hljs-keyword">if</span> is_customize_success(resp.json()):
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">raise</span> RequestsFailedException(resp.text)


<span class="hljs-keyword">def</span> <span class="hljs-title function_">is_customize_success</span>(<span class="hljs-params">json_resp: <span class="hljs-type">Dict</span></span>) -> <span class="hljs-built_in">bool</span>:
    <span class="hljs-keyword">if</span> json_resp.get(<span class="hljs-string">"code"</span>, <span class="hljs-number">999</span>) <span class="hljs-keyword">in</span> [<span class="hljs-number">0</span>, <span class="hljs-string">"0"</span>]:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
    <span class="hljs-keyword">if</span> json_resp.get(<span class="hljs-string">"status_code"</span>, <span class="hljs-number">999</span>) <span class="hljs-keyword">in</span> [<span class="hljs-number">0</span>, <span class="hljs-string">"0"</span>]:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">AuthPass</span>(<span class="hljs-title class_ inherited__">AuthBase</span>):
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, <span class="hljs-keyword">pass</span></span>):
        self.<span class="hljs-keyword">pass</span> = <span class="hljs-keyword">pass</span>

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params">self, r</span>):
        <span class="hljs-string">"""Attach an API token to a custom auth header."""</span>
        r.headers[<span class="hljs-string">'Auth-Pass-Key'</span>] = <span class="hljs-string">f'<span class="hljs-subst">{self.<span class="hljs-keyword">pass</span>}</span>'</span>
        <span class="hljs-keyword">return</span> r

<span class="hljs-keyword">def</span> <span class="hljs-title function_">my_post_requests</span>(<span class="hljs-params">data: <span class="hljs-type">Dict</span>, auth: AuthBase</span>) -> <span class="hljs-type">Optional</span>[<span class="hljs-type">Dict</span>]:
    <span class="hljs-keyword">return</span> common_requests(<span class="hljs-string">"post"</span>, json=data, auth=auth)

<span class="hljs-keyword">def</span> <span class="hljs-title function_">my_get_requests</span>(<span class="hljs-params">params: <span class="hljs-type">Dict</span></span>) -> <span class="hljs-type">Optional</span>[<span class="hljs-type">Dict</span>]:
    <span class="hljs-keyword">return</span> common_request(<span class="hljs-string">"GET"</span>, params=params)
</code></pre>]]></content:encoded>
            <author>aki@newsletter.paragraph.com (Aki)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/a4af792a6e066f231ef55a9c0a470a48a87ec9443ca45a07da70ebe39c5e9650.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>