<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Blog | Adarsh Divakaran - Python</title>
  <subtitle>Tech blog of Adarsh Divakaran</subtitle>
  <link href="https://blog.adarshd.dev/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="https://blog.adarshd.dev/python/" rel="alternate" type="text/html"/>
  <updated>2025-10-19T09:50:00Z</updated>
  <id>https://blog.adarshd.dev/</id>
  <author>
    <name>Adarsh Divakaran</name>
    <email>adarshdevamritham@gmail.com</email>
  </author>
  <entry>
    <title>Goodbye GIL - Exploring Free Threaded Python 3.14</title>
    <link href="https://blog.adarshd.dev/posts/exploring-free-threaded-python-314/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/exploring-free-threaded-python-314/</id>
    <published>2025-10-19T09:50:00Z</published>
    <updated>2025-10-19T09:50:00Z</updated>
    <content type="html"><![CDATA[<p>I compared execution time of code - which included CPU &amp; I/O bound scripts and a WSGI app using the default and
free-threaded Python 3.14 interpreters. This was for my talk: “Goodbye GIL - Exploring Free Threaded Mode in Python”
at <a href="https://2025.pycon.jp/ja/timetable/talk/KLCT8T">PyCon JP 2025</a>.</p>
<p>The talk involved a demo running CPU &amp; I/O bound workloads (standalone script and flask endpoints) with GIL
enabled and disabled, and comparing the performance.</p>
<blockquote class="prompt-info">
<p>The benchmarking was done using Python 3.14 RC2 (the talk was before Python 3.14 released).</p>
</blockquote>
<p>I originally presented this at PyCascades 2025, using Python 3.13. This time, I updated the benchmarks to Python 3.14
RC2, and added few updates regarding free threaded mode. This post is a recreation of the talk content in text format.</p>
<blockquote class="prompt-tip">
<p><a href="https://speakerdeck.com/adarshd/goodbye-gil-exploring-the-free-threaded-mode-in-python">Access Presentation Slides Here</a> · <a href="https://github.com/adarshdigievo/pycon_jp_goodbye_gil_talk">Benchmarking Scripts</a></p>
</blockquote>
<h2 id="gil" tabindex="-1"><a class="header-anchor" href="#gil">GIL</a></h2>
<p><a href="https://realpython.com/python-gil/">GIL (Global Interpreter Lock)</a> is a mutex that protects access to Python objects,
preventing multiple threads from executing Python bytecodes at once.</p>
<p>GIL simplified CPython’s internals such as memory management. This is because code can be written with the assumption
that at any single point in time, only one thread
will be executing Python bytecode.</p>
<h3 id="the-problem-impact-on-multithreading" tabindex="-1"><a class="header-anchor" href="#the-problem-impact-on-multithreading">The Problem: Impact on Multithreading</a></h3>
<p>The GIL can be a bottleneck in CPU-bound and multithreaded code. In CPU-bound multithreaded programs, threads often
compete for the GIL, leading to context switching and reduced
performance. I/O bounded programs are less affected, as threads can release the GIL during I/O operations.</p>
<p>Historically, multiprocessing was the primary way to achieve parallelism in
Python for CPU-bound tasks, but with inter-process communication overhead.</p>
<h2 id="pep-703-making-the-gil-optional-in-cpython" tabindex="-1"><a class="header-anchor" href="#pep-703-making-the-gil-optional-in-cpython">PEP 703: Making the GIL Optional in CPython</a></h2>
<p>PEP 703 proposed making the Global Interpreter Lock optional in CPython. It introduces “free-threaded build” of Python
where the GIL is disabled. The PEP aims to unlock true parallel execution for Python threads, especially for
CPU-bound workloads.</p>
<p>It was experimental in Python 3.13, eventually evolving into an optional (or possibly
default-disabled) feature in future releases.</p>
<h3 id="challenges-of-free-threaded-python" tabindex="-1"><a class="header-anchor" href="#challenges-of-free-threaded-python">Challenges of Free-Threaded Python</a></h3>
<ul>
<li>Backward Compatibility: Many C extensions are written assuming the GIL will
exist. These extensions may not be thread-safe without the GIL, leading to potential crashes or data corruption.</li>
<li>Performance Overhead: Free-threaded builds may run single-threaded code
slower. This is due to the need for additional locking mechanisms to ensure thread safety (since multiple threads will
be executing at a single point in time).</li>
</ul>
<h2 id="pep-779-criteria-for-supported-status-for-free-threaded-python" tabindex="-1"><a class="header-anchor" href="#pep-779-criteria-for-supported-status-for-free-threaded-python">PEP 779 – Criteria for supported status for free-threaded Python:</a></h2>
<p>The PEP outlines criteria for when free-threaded Python can be considered a supported feature. This included
requirements that the free threaded build needs to meet across performance, stability, etc.</p>
<p>PEP 779 evaluated the free-threaded build against these criteria in Python 3.13 and the criteria were satisfied.</p>
<blockquote>
<p>“The free-threaded build of Python is now supported and no longer
experimental. This is the start of phase II where free-threaded Python is
officially supported but still optional.&quot; - Python 3.14 Release Notes</p>
</blockquote>
<p>The performance penalty on single-threaded code in free-threaded mode is now
roughly 5-10%.</p>
<h2 id="setting-up-free-threaded-python" tabindex="-1"><a class="header-anchor" href="#setting-up-free-threaded-python">Setting Up Free Threaded Python</a></h2>
<p>The official guide to set-up free-threaded Python is
available <a href="https://py-free-threading.github.io/installing_cpython">here</a>.</p>
<p>Windows/macOS users can use the installers from <a href="http://Python.org">Python.org</a> that include free-threaded builds.</p>
<p>For Ubuntu, free-threaded Python 3.14 can be installed from the deadsnakes PPA:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> add-apt-repository ppa:deadsnakes
<span class="token function">sudo</span> <span class="token function">apt-get</span> update
<span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> python3.14-nogil
</code></pre>
<h2 id="benchmarks" tabindex="-1"><a class="header-anchor" href="#benchmarks">Benchmarks</a></h2>
<p>This section presents benchmarks comparing performance of standard Python 3.14 (with GIL) and free-threaded Python
3.14 (without GIL).</p>
<ul>
<li>For benchmarking, I created two virtual environments:
<ul>
<li>One with standard Python 3.14 (with GIL)</li>
<li>Another with free-threaded Python 3.14 (3.14t, without GIL, <code>python3.14t -m venv .venv</code>)</li>
</ul>
</li>
</ul>
<p>Different workloads were benchmarked. When running the benchmarks, the GIL status was checked to ensure the correct
interpreter was used.</p>
<ul>
<li>GIL status can be checked using any of the following methods:</li>
</ul>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> sys

<span class="token keyword">print</span><span class="token punctuation">(</span>sys<span class="token punctuation">.</span>_is_gil_enabled<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># True if GIL is enabled, False otherwise</span>
</code></pre>
<p>OR:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> sysconfig

sysconfig<span class="token punctuation">.</span>get_config_var<span class="token punctuation">(</span><span class="token string">"Py_GIL_DISABLED"</span><span class="token punctuation">)</span>  <span class="token comment"># True if GIL is disabled, False otherwise</span>
</code></pre>
<ul>
<li>The above is wrapped in check_gil.py file (used in benchmarks):</li>
</ul>
<hr>
<details  markdown="1">
<summary><strong>View `check_gil.py` code</strong></summary>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> sys
<span class="token keyword">import</span> sysconfig


<span class="token keyword">def</span> <span class="token function">show_gil_status</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  gil_status <span class="token operator">=</span> sys<span class="token punctuation">.</span>_is_gil_enabled<span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">F"</span><span class="token interpolation"><span class="token punctuation">{</span>sys<span class="token punctuation">.</span>_is_gil_enabled<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">=</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>

  <span class="token keyword">if</span> gil_status<span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"\nRunning in default interpreter - GIL is enabled\n"</span><span class="token punctuation">)</span>
  <span class="token keyword">else</span><span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"\nRunning in Free threaded interpreter - GIL is disabled\n"</span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">check_free_threading_support</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"\n</span><span class="token interpolation"><span class="token punctuation">{</span>sysconfig<span class="token punctuation">.</span>get_config_vars<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'Py_GIL_DISABLED'</span><span class="token punctuation">)</span><span class="token operator">=</span><span class="token punctuation">}</span></span><span class="token string">\n"</span></span><span class="token punctuation">)</span>


<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">'__main__'</span><span class="token punctuation">:</span>
  check_free_threading_support<span class="token punctuation">(</span><span class="token punctuation">)</span>

  show_gil_status<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
</details>
<hr>
<h3 id="first-benchmark-single-threaded-program" tabindex="-1"><a class="header-anchor" href="#first-benchmark-single-threaded-program">First benchmark - Single Threaded Program</a></h3>
<p>The first benchmark involves running a single threaded program that sums numbers from 1 to 1 million, repeated 100 times
and timed using <code>timeit</code> module.</p>
<hr>
<details  markdown="1">
<summary><strong>View benchmarking code</strong></summary>
<pre class="language-python"><code class="language-python">
<span class="token keyword">import</span> timeit

<span class="token keyword">from</span> check_gil <span class="token keyword">import</span> show_gil_status


<span class="token keyword">def</span> <span class="token function">single_threaded_benchmark</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  show_gil_status<span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token comment"># Sum of numbers from 1 to 1 million</span>
  code_to_test <span class="token operator">=</span> <span class="token triple-quoted-string string">"""

total = 0
for i in range(1_000_000):
    total += i

"""</span>

  <span class="token comment"># Use timeit to measure execution time - Run the code 100 times</span>
  execution_time <span class="token operator">=</span> timeit<span class="token punctuation">.</span>timeit<span class="token punctuation">(</span>code_to_test<span class="token punctuation">,</span> number<span class="token operator">=</span><span class="token number">100</span><span class="token punctuation">)</span>

  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Execution time: </span><span class="token interpolation"><span class="token punctuation">{</span>execution_time<span class="token punctuation">:</span><span class="token format-spec">.6f</span><span class="token punctuation">}</span></span><span class="token string"> seconds"</span></span><span class="token punctuation">)</span>


<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">'__main__'</span><span class="token punctuation">:</span>
  single_threaded_benchmark<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
</details>
<hr>
<blockquote class="prompt-info">
<p>Benchmark Results: In my system, the free threaded execution was ~15% slower than default interpreter. Default interpreter completed in ~3s, while free-threaded took ~3.5s.</p>
</blockquote>
<p>Execution time of individual runs are added in the next section, in the <code>Observations</code> table.</p>
<h3 id="io-bound-program" tabindex="-1"><a class="header-anchor" href="#io-bound-program">I/O Bound Program</a></h3>
<p>Next, let’s benchmark an I/O bound program that performs file write and read operations.</p>
<p>We execute the code in single threaded mode as well as multi-threaded mode using <code>ThreadPoolExecutor</code> and repeat both
with GIL enabled and disabled.</p>
<p>The code creates a temporary file, writes data to it in chunks, forces <code>flush</code> and <code>fsync</code> to ensure data is committed
to disk,
then reads the data back in chunks. This simulates a realistic I/O workload.</p>
<hr>
<details  markdown="1">
<summary><strong>View benchmarking code</strong></summary>
<pre class="language-python"><code class="language-python">
<span class="token keyword">import</span> os
<span class="token keyword">import</span> tempfile
<span class="token keyword">import</span> time
<span class="token keyword">from</span> concurrent<span class="token punctuation">.</span>futures <span class="token keyword">import</span> ThreadPoolExecutor<span class="token punctuation">,</span> wait<span class="token punctuation">,</span> ALL_COMPLETED

<span class="token keyword">from</span> check_gil <span class="token keyword">import</span> show_gil_status

FILE_MB <span class="token operator">=</span> <span class="token number">24</span>  <span class="token comment"># total temp file size per op</span>

CHUNK_KB <span class="token operator">=</span> <span class="token number">64</span>  <span class="token comment"># write/read granularity</span>

SINGLE_OPS <span class="token operator">=</span> <span class="token number">8</span>  <span class="token comment"># Single threaded executions</span>

MULTI_OPS <span class="token operator">=</span> <span class="token number">12</span>  <span class="token comment"># Multi-threaded executions</span>
THREADS <span class="token operator">=</span> <span class="token number">6</span>

<span class="token comment"># ===== Derived =====</span>
_CHUNK_BYTES <span class="token operator">=</span> CHUNK_KB <span class="token operator">*</span> <span class="token number">1024</span>
_TOTAL_CHUNKS <span class="token operator">=</span> <span class="token punctuation">(</span>FILE_MB <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">)</span> <span class="token operator">//</span> _CHUNK_BYTES
BUF <span class="token operator">=</span> <span class="token string">b"A"</span> <span class="token operator">*</span> _CHUNK_BYTES


<span class="token keyword">def</span> <span class="token function">_write_pass</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> buf<span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token triple-quoted-string string">"""Write the buffer repeatedly across the file, forcing flush+fsync each time to ensure data is committed to disk."""</span>
  f<span class="token punctuation">.</span>seek<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>

  <span class="token comment"># make sure each write is flushed and synced to disk - to increase IO load.</span>
  <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>_TOTAL_CHUNKS<span class="token punctuation">)</span><span class="token punctuation">:</span>
    f<span class="token punctuation">.</span>write<span class="token punctuation">(</span>buf<span class="token punctuation">)</span>
    f<span class="token punctuation">.</span>flush<span class="token punctuation">(</span><span class="token punctuation">)</span>
    os<span class="token punctuation">.</span>fsync<span class="token punctuation">(</span>f<span class="token punctuation">.</span>fileno<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">_read_pass</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token triple-quoted-string string">"""Read the entire file back chunk by chunk to simulate sequential disk read workload."""</span>
  f<span class="token punctuation">.</span>seek<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
  remaining <span class="token operator">=</span> _TOTAL_CHUNKS
  <span class="token keyword">while</span> remaining <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">:</span>
    data <span class="token operator">=</span> f<span class="token punctuation">.</span>read<span class="token punctuation">(</span>_CHUNK_BYTES<span class="token punctuation">)</span>
    <span class="token keyword">if</span> <span class="token keyword">not</span> data<span class="token punctuation">:</span>
      <span class="token keyword">break</span>
    remaining <span class="token operator">-=</span> <span class="token number">1</span>


<span class="token keyword">def</span> <span class="token function">io_bound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token triple-quoted-string string">"""Perform one full write+read cycle on a temporary file with forced disk syncs, then clean up the file."""</span>
  tmp <span class="token operator">=</span> tempfile<span class="token punctuation">.</span>NamedTemporaryFile<span class="token punctuation">(</span>prefix<span class="token operator">=</span><span class="token string">"io_bench_"</span><span class="token punctuation">,</span> delete<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
  tmp_name <span class="token operator">=</span> tmp<span class="token punctuation">.</span>name
  tmp<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">try</span><span class="token punctuation">:</span>
    <span class="token keyword">with</span> <span class="token builtin">open</span><span class="token punctuation">(</span>tmp_name<span class="token punctuation">,</span> <span class="token string">"wb"</span><span class="token punctuation">,</span> buffering<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f0<span class="token punctuation">:</span>
      f0<span class="token punctuation">.</span>truncate<span class="token punctuation">(</span>FILE_MB <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">)</span>

    flags <span class="token operator">=</span> os<span class="token punctuation">.</span>O_RDWR <span class="token operator">|</span> os<span class="token punctuation">.</span>O_CREAT

    fd <span class="token operator">=</span> os<span class="token punctuation">.</span><span class="token builtin">open</span><span class="token punctuation">(</span>tmp_name<span class="token punctuation">,</span> flags<span class="token punctuation">,</span> <span class="token number">0o600</span><span class="token punctuation">)</span>
    f <span class="token operator">=</span> os<span class="token punctuation">.</span>fdopen<span class="token punctuation">(</span>fd<span class="token punctuation">,</span> <span class="token string">"r+b"</span><span class="token punctuation">,</span> buffering<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span>

    <span class="token keyword">try</span><span class="token punctuation">:</span>
      _write_pass<span class="token punctuation">(</span>f<span class="token punctuation">,</span> BUF<span class="token punctuation">)</span>
      _read_pass<span class="token punctuation">(</span>f<span class="token punctuation">)</span>
    <span class="token keyword">finally</span><span class="token punctuation">:</span>
      f<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword">finally</span><span class="token punctuation">:</span>
    <span class="token keyword">try</span><span class="token punctuation">:</span>
      os<span class="token punctuation">.</span>remove<span class="token punctuation">(</span>tmp_name<span class="token punctuation">)</span>
    <span class="token keyword">except</span> Exception<span class="token punctuation">:</span>
      <span class="token keyword">pass</span>
  <span class="token keyword">return</span> <span class="token string">"IO task done!"</span>


<span class="token keyword">def</span> <span class="token function">single_threaded</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>SINGLE_OPS<span class="token punctuation">)</span><span class="token punctuation">:</span>
    io_bound<span class="token punctuation">(</span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">multi_threaded</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">with</span> ThreadPoolExecutor<span class="token punctuation">(</span>max_workers<span class="token operator">=</span>THREADS<span class="token punctuation">)</span> <span class="token keyword">as</span> executor<span class="token punctuation">:</span>
    futs <span class="token operator">=</span> <span class="token punctuation">[</span>executor<span class="token punctuation">.</span>submit<span class="token punctuation">(</span>io_bound<span class="token punctuation">)</span> <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>MULTI_OPS<span class="token punctuation">)</span><span class="token punctuation">]</span>
    wait<span class="token punctuation">(</span>futs<span class="token punctuation">,</span> return_when<span class="token operator">=</span>ALL_COMPLETED<span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">run_benchmark</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  t0 <span class="token operator">=</span> time<span class="token punctuation">.</span>time<span class="token punctuation">(</span><span class="token punctuation">)</span>
  single_threaded<span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Single thread Exec Time: </span><span class="token interpolation"><span class="token punctuation">{</span>time<span class="token punctuation">.</span>time<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> t0<span class="token punctuation">:</span><span class="token format-spec">.2f</span><span class="token punctuation">}</span></span><span class="token string">s"</span></span><span class="token punctuation">)</span>

  t1 <span class="token operator">=</span> time<span class="token punctuation">.</span>time<span class="token punctuation">(</span><span class="token punctuation">)</span>
  multi_threaded<span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Multi thread Exec Time: </span><span class="token interpolation"><span class="token punctuation">{</span>time<span class="token punctuation">.</span>time<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> t1<span class="token punctuation">:</span><span class="token format-spec">.2f</span><span class="token punctuation">}</span></span><span class="token string">s"</span></span><span class="token punctuation">)</span>


<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">'__main__'</span><span class="token punctuation">:</span>
  show_gil_status<span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"[Config] FILE_MB=</span><span class="token interpolation"><span class="token punctuation">{</span>FILE_MB<span class="token punctuation">}</span></span><span class="token string">, CHUNK_KB=</span><span class="token interpolation"><span class="token punctuation">{</span>CHUNK_KB<span class="token punctuation">}</span></span><span class="token string"> "</span></span>
        <span class="token string-interpolation"><span class="token string">f"SINGLE_OPS=</span><span class="token interpolation"><span class="token punctuation">{</span>SINGLE_OPS<span class="token punctuation">}</span></span><span class="token string">, MULTI_OPS=</span><span class="token interpolation"><span class="token punctuation">{</span>MULTI_OPS<span class="token punctuation">}</span></span><span class="token string">, THREADS=</span><span class="token interpolation"><span class="token punctuation">{</span>THREADS<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
  run_benchmark<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
</details>
<hr>
<blockquote class="prompt-info">
<p>Benchmark Results: In case of this I/O bound operation, both interpreters performed comparably. Single threaded execution took ~2.9s in both interpreters. Multi-threaded execution took ~2.4s in both interpreters.</p>
</blockquote>
<h3 id="cpu-bound-program" tabindex="-1"><a class="header-anchor" href="#cpu-bound-program">CPU Bound Program</a></h3>
<p>The task involves performing a computationally intensive operation. We calculate primes upto the upper limit of 2
million.</p>
<p>The task is done using two modes:</p>
<ol>
<li>Single-threaded mode: The prime counting function is executed in a single thread.</li>
<li>Multi-threaded mode: The prime counting function is divided into chunks, and each chunk is processed in a separate
thread using <code>ThreadPoolExecutor</code>. This way, we can see the multi-threading related performance changes.</li>
</ol>
<hr>
<details  markdown="1">
<summary><strong>View benchmarking code</strong></summary>
<pre class="language-python"><code class="language-python">
<span class="token keyword">import</span> math
<span class="token keyword">import</span> time
<span class="token keyword">import</span> os
<span class="token keyword">from</span> concurrent<span class="token punctuation">.</span>futures <span class="token keyword">import</span> ThreadPoolExecutor<span class="token punctuation">,</span> as_completed

<span class="token comment"># CPU bound task: count primes up to UPPER</span>

<span class="token comment"># Upper limit for prime search - 2 Million</span>
UPPER <span class="token operator">=</span> <span class="token number">2_000_000</span>

<span class="token comment"># Chunk size for multithreading</span>
CHUNK_SIZE <span class="token operator">=</span> <span class="token number">50_000</span>


<span class="token keyword">def</span> <span class="token function">is_prime</span><span class="token punctuation">(</span>n<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">bool</span><span class="token punctuation">:</span>
  <span class="token keyword">if</span> n <span class="token operator">&lt;</span> <span class="token number">2</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token boolean">False</span>
  <span class="token keyword">if</span> n <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> n <span class="token operator">==</span> <span class="token number">2</span>
  r <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>math<span class="token punctuation">.</span>isqrt<span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">)</span>

  <span class="token comment"># check if number is divisible by any odd number up to sqrt(n)</span>
  <span class="token keyword">for</span> f <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> r <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> n <span class="token operator">%</span> f <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span>
      <span class="token keyword">return</span> <span class="token boolean">False</span>
  <span class="token keyword">return</span> <span class="token boolean">True</span>


<span class="token keyword">def</span> <span class="token function">count_primes_range</span><span class="token punctuation">(</span>a<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">,</span> b<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">int</span><span class="token punctuation">:</span>
  <span class="token comment"># Count primes in [a, b)</span>
  cnt <span class="token operator">=</span> <span class="token number">0</span>
  <span class="token comment"># handle 2 explicitly, then iterate odds</span>
  <span class="token keyword">if</span> a <span class="token operator">&lt;=</span> <span class="token number">2</span> <span class="token operator">&lt;</span> b<span class="token punctuation">:</span>
    cnt <span class="token operator">+=</span> <span class="token number">1</span>
  start <span class="token operator">=</span> <span class="token builtin">max</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span>
  <span class="token keyword">if</span> start <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span>
    start <span class="token operator">+=</span> <span class="token number">1</span>
  <span class="token keyword">for</span> n <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>start<span class="token punctuation">,</span> b<span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> is_prime<span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">:</span>
      cnt <span class="token operator">+=</span> <span class="token number">1</span>
  <span class="token keyword">return</span> cnt


<span class="token comment"># Calculate primes in single-threaded mode - 3 - 2 Million in a single thread</span>
<span class="token keyword">def</span> <span class="token function">single_threaded</span><span class="token punctuation">(</span>upper<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">tuple</span><span class="token punctuation">[</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">float</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
  t0 <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>
  cnt <span class="token operator">=</span> count_primes_range<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> upper<span class="token punctuation">)</span>
  dt <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> t0
  <span class="token keyword">return</span> cnt<span class="token punctuation">,</span> dt


<span class="token comment"># Calculate primes in multi-threaded mode using threads. Each thread processes a chunk of 50,000 (CHUNK_SIZE).</span>
<span class="token keyword">def</span> <span class="token function">multithreaded_threads</span><span class="token punctuation">(</span>upper<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">,</span> max_workers<span class="token punctuation">:</span> <span class="token builtin">int</span> <span class="token operator">|</span> <span class="token boolean">None</span> <span class="token operator">=</span> <span class="token boolean">None</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">tuple</span><span class="token punctuation">[</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">float</span><span class="token punctuation">,</span> <span class="token builtin">int</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
  <span class="token comment"># Split [2, upper) into chunks and process with threads.</span>

  <span class="token keyword">if</span> max_workers <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span>
    max_workers <span class="token operator">=</span> <span class="token builtin">max</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> os<span class="token punctuation">.</span>cpu_count<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">or</span> <span class="token number">4</span><span class="token punctuation">)</span>

  <span class="token comment"># Build chunks</span>
  chunks <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
  lo <span class="token operator">=</span> <span class="token number">2</span>
  <span class="token keyword">while</span> lo <span class="token operator">&lt;</span> upper<span class="token punctuation">:</span>
    hi <span class="token operator">=</span> <span class="token builtin">min</span><span class="token punctuation">(</span>lo <span class="token operator">+</span> CHUNK_SIZE<span class="token punctuation">,</span> upper<span class="token punctuation">)</span>
    chunks<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">(</span>lo<span class="token punctuation">,</span> hi<span class="token punctuation">)</span><span class="token punctuation">)</span>
    lo <span class="token operator">=</span> hi

  t0 <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>
  total <span class="token operator">=</span> <span class="token number">0</span>
  <span class="token keyword">with</span> ThreadPoolExecutor<span class="token punctuation">(</span>max_workers<span class="token operator">=</span>max_workers<span class="token punctuation">)</span> <span class="token keyword">as</span> ex<span class="token punctuation">:</span>
    futures <span class="token operator">=</span> <span class="token punctuation">[</span>ex<span class="token punctuation">.</span>submit<span class="token punctuation">(</span>count_primes_range<span class="token punctuation">,</span> a<span class="token punctuation">,</span> b<span class="token punctuation">)</span> <span class="token keyword">for</span> a<span class="token punctuation">,</span> b <span class="token keyword">in</span> chunks<span class="token punctuation">]</span>
    <span class="token keyword">for</span> fut <span class="token keyword">in</span> as_completed<span class="token punctuation">(</span>futures<span class="token punctuation">)</span><span class="token punctuation">:</span>
      total <span class="token operator">+=</span> fut<span class="token punctuation">.</span>result<span class="token punctuation">(</span><span class="token punctuation">)</span>
  dt <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> t0
  <span class="token keyword">return</span> total<span class="token punctuation">,</span> dt<span class="token punctuation">,</span> max_workers


<span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Computing prime count in [2, </span><span class="token interpolation"><span class="token punctuation">{</span>UPPER<span class="token punctuation">}</span></span><span class="token string">)"</span></span><span class="token punctuation">)</span>
  cnt1<span class="token punctuation">,</span> t1 <span class="token operator">=</span> single_threaded<span class="token punctuation">(</span>UPPER<span class="token punctuation">)</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"[Single-threaded] primes=</span><span class="token interpolation"><span class="token punctuation">{</span>cnt1<span class="token punctuation">:</span><span class="token format-spec">,</span><span class="token punctuation">}</span></span><span class="token string">  time=</span><span class="token interpolation"><span class="token punctuation">{</span>t1<span class="token punctuation">:</span><span class="token format-spec">.2f</span><span class="token punctuation">}</span></span><span class="token string">s"</span></span><span class="token punctuation">)</span>

  cnt2<span class="token punctuation">,</span> t2<span class="token punctuation">,</span> k <span class="token operator">=</span> multithreaded_threads<span class="token punctuation">(</span>UPPER<span class="token punctuation">)</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"[Multithreaded (threads=</span><span class="token interpolation"><span class="token punctuation">{</span>k<span class="token punctuation">}</span></span><span class="token string">)] primes=</span><span class="token interpolation"><span class="token punctuation">{</span>cnt2<span class="token punctuation">:</span><span class="token format-spec">,</span><span class="token punctuation">}</span></span><span class="token string">  time=</span><span class="token interpolation"><span class="token punctuation">{</span>t2<span class="token punctuation">:</span><span class="token format-spec">.2f</span><span class="token punctuation">}</span></span><span class="token string">s"</span></span><span class="token punctuation">)</span>

  <span class="token comment"># Sanity check: both modes should count the same</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Results equal:"</span><span class="token punctuation">,</span> cnt1 <span class="token operator">==</span> cnt2<span class="token punctuation">)</span>


<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span>
  main<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
</details>
<hr>
<blockquote class="prompt-info">
<p>Benchmark Results: In single threaded mode, default interpreter completed in ~2.7s, while free-threaded took ~3.1s. For multi-threaded execution, default interpreter took ~2.7s, while free-threaded completed in ~0.8s - a significant improvement.</p>
</blockquote>
<p>When using free-threaded Python, we got performance penalty in single-threaded code, but significant performance in
multi-threaded mode.</p>
<h3 id="wsgi-app-with-flask" tabindex="-1"><a class="header-anchor" href="#wsgi-app-with-flask">WSGI App with Flask</a></h3>
<p>Flask app is run in development mode with Flask’s built-in server (multi-threaded by default). So the WSGI server can be
thought of as a multi-threaded server.</p>
<p>We have two API Endpoints defined - 1 running I/O bound operation &amp; other running CPU bound task.</p>
<p>We benchmark the endpoints using load testing tool <a href="https://k6.io/">k6</a>. k6 creates multiple virtual users (VUs)
simulating real-users of the application and makes requests to the endpoints.</p>
<hr>
<details  markdown="1">
<summary><strong>View Flask `app.py`</strong></summary>
<pre class="language-python"><code class="language-python">
<span class="token keyword">import</span> threading

<span class="token keyword">from</span> flask <span class="token keyword">import</span> Flask

<span class="token keyword">import</span> tempfile
<span class="token keyword">import</span> os

<span class="token keyword">from</span> check_gil <span class="token keyword">import</span> show_gil_status

app <span class="token operator">=</span> Flask<span class="token punctuation">(</span>__name__<span class="token punctuation">)</span>


<span class="token comment"># CPU-bound task</span>
<span class="token keyword">def</span> <span class="token function">heavy_computation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token comment"># sum of squares from 0 to 10 million</span>
  <span class="token keyword">return</span> <span class="token builtin">sum</span><span class="token punctuation">(</span>i <span class="token operator">*</span> i <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">10</span> <span class="token operator">**</span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">)</span>


<span class="token comment"># Routes</span>
<span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>route</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">root</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">return</span> <span class="token string-interpolation"><span class="token string">f"</span><span class="token interpolation"><span class="token punctuation">{</span>threading<span class="token punctuation">.</span>active_count<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">=</span><span class="token punctuation">}</span></span><span class="token string">"</span></span>


<span class="token comment"># Routes</span>
<span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>route</span><span class="token punctuation">(</span><span class="token string">'/cpu'</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">cpu_bound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span>threading<span class="token punctuation">.</span>active_count<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  heavy_computation<span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">return</span> <span class="token string">"CPU task done!"</span>


<span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>route</span><span class="token punctuation">(</span><span class="token string">'/io'</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">io_bound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">print</span><span class="token punctuation">(</span>threading<span class="token punctuation">.</span>active_count<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

  <span class="token keyword">with</span> tempfile<span class="token punctuation">.</span>TemporaryFile<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
    f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">b"Hello, World!"</span> <span class="token operator">*</span> <span class="token number">10000000</span><span class="token punctuation">)</span>
    f<span class="token punctuation">.</span>seek<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
    f<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
    os<span class="token punctuation">.</span>remove<span class="token punctuation">(</span>f<span class="token punctuation">.</span>name<span class="token punctuation">)</span>  <span class="token comment"># Added for more IO work</span>

  <span class="token keyword">with</span> tempfile<span class="token punctuation">.</span>TemporaryFile<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
    f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">b"Hello, World!"</span> <span class="token operator">*</span> <span class="token number">10000000</span><span class="token punctuation">)</span>
    f<span class="token punctuation">.</span>seek<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
    f<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
    os<span class="token punctuation">.</span>remove<span class="token punctuation">(</span>f<span class="token punctuation">.</span>name<span class="token punctuation">)</span>  <span class="token comment"># Added for more IO work</span>

  <span class="token keyword">with</span> tempfile<span class="token punctuation">.</span>TemporaryFile<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
    f<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">b"Hello, World!"</span> <span class="token operator">*</span> <span class="token number">10000000</span><span class="token punctuation">)</span>
    f<span class="token punctuation">.</span>seek<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
    f<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
    os<span class="token punctuation">.</span>remove<span class="token punctuation">(</span>f<span class="token punctuation">.</span>name<span class="token punctuation">)</span>  <span class="token comment"># Added for more IO work</span>

  <span class="token keyword">return</span> <span class="token string">"IO task done!"</span>


<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">'__main__'</span><span class="token punctuation">:</span>
  show_gil_status<span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token comment"># By default, Flask's built-in server is multi-threaded.</span>
  <span class="token comment"># This can be verified by visiting the root endpoint (/).</span>
  app<span class="token punctuation">.</span>run<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
</details>
<hr>
<p>Benchmarks were done using the k6. The benchmark scripts used are
available <a href="https://github.com/adarshdigievo/pycon_jp_goodbye_gil_talk/tree/master/flask_app/benchmark">here</a></p>
<p>I configured k6 to run for 20 seconds with 15 virtual users (VUs) making requests to the Flask app endpoints. The
benchmark was done using <code>k6 run benchmark_io.js</code> and <code>k6 run benchmark_cpu.js</code> commands.</p>
<blockquote class="prompt-info">
<p>Results: For I/O bound endpoint, both interpreters performed comparably - ~31-32 requests completed in 20 seconds. For CPU bound endpoint, free-threaded Python showed significant improvement - ~91 requests completed in 20 seconds, compared to ~47 requests with GIL enabled.</p>
</blockquote>
<p>The results indicate that in our WSGI app, free-threaded Python can handle CPU-bound endpoints more efficiently in a
multi-threaded
environment, while I/O-bound workloads remain largely unaffected by the presence or absence of the GIL.</p>
<h2 id="observations" tabindex="-1"><a class="header-anchor" href="#observations">Observations</a></h2>
<p>The below table shows the consolidated benchmark results:</p>
<table>
<thead>
<tr>
<th>File</th>
<th>GIL Enabled</th>
<th>Free Threaded</th>
</tr>
</thead>
<tbody>
<tr>
<td>1_single_threaded.py</td>
<td>~3.0s</td>
<td>~3.5s</td>
</tr>
<tr>
<td>2_io_bound_single_thread</td>
<td>~2.9s</td>
<td>~2.9s</td>
</tr>
<tr>
<td>2_io_bound_multithread</td>
<td>~2.4s</td>
<td>~2.4s</td>
</tr>
<tr>
<td>3_cpu_bound_single_thread</td>
<td>~2.7s</td>
<td>~3.1s</td>
</tr>
<tr>
<td>3_cpu_bound_multithread</td>
<td>~2.7s</td>
<td>~0.8s</td>
</tr>
<tr>
<td>flask_app_io_bound</td>
<td>31 req/20s</td>
<td>32 reqs/20s</td>
</tr>
<tr>
<td>flask_app_cpu_bound</td>
<td>47 req/20s</td>
<td>91 req/20s</td>
</tr>
</tbody>
</table>
<p>For flask app, the benchmark lists the number of requests completed in 20 seconds (higher the better).</p>
<p>Summary of benchmarks:</p>
<ul>
<li>Single threaded - Worse performance in free-threaded mode.</li>
<li>I/O Bound programs - Minimal/no impact. Both interpreters perform comparably.</li>
<li>For <strong>CPU Bound, Multi-threaded</strong> Programs, free-threaded Python shows significant performance improvements.</li>
</ul>
<h2 id="the-future-of-free-threaded-python" tabindex="-1"><a class="header-anchor" href="#the-future-of-free-threaded-python">The future of Free Threaded Python</a></h2>
<ol>
<li>In 2024, CPython 3.13 was released with support for a --disable-gil build time flag.
There are two ABIs for CPython, one with the GIL and one without. Extension authors
target both ABIs.</li>
<li>We are here now (PEP 779 Acceptance): “The free-threaded build of Python is now supported
and no longer experimental&quot;.</li>
<li>After 2–3 releases, (i.e., in 2026–2027), CPython is released with the GIL controlled by a
runtime environment variable or flag. The GIL is enabled by default. There is only a single
ABI.</li>
<li>After another 2–3 release (i.e., 2028–2030), CPython switches to the GIL being disabled
by default. The GIL can still be enabled at runtime via an environment variable or
command line flag.</li>
</ol>
<h2 id="migration-checklist" tabindex="-1"><a class="header-anchor" href="#migration-checklist">Migration Checklist</a></h2>
<p>Free-threaded Python:</p>
<ul>
<li>Great for CPU bound Tasks + Threading</li>
<li>Great for scenarios where parallelisation is needed - but multiprocessing is too
complex / not suitable</li>
</ul>
<p>Precautions:</p>
<ul>
<li>Always benchmark using your own workload before making a switch.</li>
<li>Watch out for library compatibility issues/bugs. Proper testing is needed before production adoption with your
workloads.</li>
</ul>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="#conclusion">Conclusion</a></h2>
<ul>
<li>
<p>Free-threaded Python in 3.14 shows significant performance improvements for CPU-bound multithreaded workloads,
while maintaining comparable performance for I/O-bound tasks. A new
concurrency mode is unlocked for CPU bound programs without overhead
of inter-process communication</p>
</li>
<li>
<p>Single-threaded performance sees a slight decrease in performance. The default interpreter works best for
single-threaded programs. The performance penalty will be reduced further in future releases.</p>
</li>
<li>
<p>Performance is comparable for I/O-bound workloads in both modes.</p>
</li>
</ul>
<hr>
<p>PS: I published my book “Deep Dive Python” last month. <a href="https://deepdivepython.com">Get the book</a> to learn Python
concepts straight from real open-source code.</p>
]]></content>
  </entry>
  <entry>
    <title>Vibe Coding with Junie - JetBrains AI Coding Agent</title>
    <link href="https://blog.adarshd.dev/posts/vibe-coding-with-jetbrains-junie/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/vibe-coding-with-jetbrains-junie/</id>
    <published>2025-08-05T12:50:00Z</published>
    <updated>2025-08-05T12:50:00Z</updated>
    <content type="html"><![CDATA[<p>I created a Python project recently to test out Junie - the new AI coding agent from JetBrains. This led to the creation
of <a href="https://github.com/adarshdigievo/pyzlides">Pyzlides</a>, a Python library that lets us build presentation slides
entirely as code. Interestingly, there are zero lines of code written by me; the entire codebase was generated by Junie.</p>
<p>I started using PyCharm IDE back in 2019 for my work, and I’ve been hooked on it ever since. Since then, I’ve explored
other AI-powered IDEs like Cursor and Windsurf, actively experimenting with each new release to see what they offer.</p>
<p>At PyCon US this year, I got to meet the JetBrains PyCharm team in person. They gave me a quick demo of Junie, and I
knew immediately I wanted to give it a shot.</p>
<p>Below, I’ll share the detailed steps I took while developing Pyzlides, along with a few tips on making the most of
AI-assisted coding.</p>
<h2 id="initial-setup" tabindex="-1"><a class="header-anchor" href="#initial-setup">Initial Setup</a></h2>
<p>For AI coding tools, context is the most important. I always spend lot of time to create the initial prompt which will
be given to the agent. I try to make it as detailed as possible with examples and intended usage.
Initially, I bootstrapped the environment using <code>uv</code> and installed the dependencies in the environment. After a bit of
brainstorming and help from ChatGPT to decide the project dependencies, I decided to use the below dependencies:</p>
<ul>
<li><strong>ReportLab</strong>: For generating the PDF slides.</li>
<li><strong>Pillow</strong>: For handling images in slides.</li>
<li><strong>PyYAML</strong>: For parsing the configuration file.</li>
</ul>
<p>I installed these libraries in the environment and this was my major point of contribution. After this point, the entire
building was done by Junie and I was only involved with writing prompts.</p>
<h2 id="getting-started-with-junie" tabindex="-1"><a class="header-anchor" href="#getting-started-with-junie">Getting started with Junie</a></h2>
<p>I used the Junie premium subscription for this project. Below is a screenshot of Junie’s chat interface:</p>
<p><img src="/assets/img/posts/vibe-coding-with-jetbrains-junie/junie_interface.png" alt="image"></p>
<p>Junie provides a simple, familiar chat interface to interact with. From the interface, we have two conversation modes:</p>
<ol>
<li>
<p><strong>Code mode</strong>: Junie directly generates or edits code.</p>
</li>
<li>
<p><strong>Ask mode</strong>: Useful for discussing the codebase, understanding unfamiliar code structure, or clarifying doubts
without making direct code changes.</p>
</li>
</ol>
<p>Additionally, using the <code>+</code> button, we can attach relevant files to give Junie more context about the project.</p>
<p>Junie includes a <strong>Brave Mode</strong> toggle. When enabled, Junie executes terminal commands (such as creating directories,
running the project, etc.) without asking for user confirmation. I recommend using Brave Mode only with simpler projects
where you fully understand and trust the codebase. Otherwise, it poses a risk of unintended actions or prompt injection.</p>
<p>Recent Junie versions introduced another toggle: <strong>Think More</strong>, prompting the AI model to reflect more carefully during
code
generation, improving the overall quality of its responses.</p>
<h3 id="setting-context" tabindex="-1"><a class="header-anchor" href="#setting-context">Setting context</a></h3>
<p>As we’ve previously discussed, AI coding assistants perform best when given ample context. Junie provides helpful ways
to ensure it understands our project’s codebase clearly:</p>
<ul>
<li>
<p><strong><code>.aiignore</code></strong>: From the <code>+</code> menu, we can generate an AI-ignore file, similar to <code>.gitignore</code>. Files added here won’t
be modified by Junie.</p>
</li>
<li>
<p><strong>Guidelines</strong>: Also available through the <code>+</code> button and example prompts are project-specific instructions. Junie
uses these as guidelines to stay aligned with project conventions. I will share the guidelines I created later in this
post.</p>
</li>
</ul>
<h3 id="junie-settings" tabindex="-1"><a class="header-anchor" href="#junie-settings">Junie Settings</a></h3>
<p>Before we dive deeper into Pyzlides, let’s quickly look at Junie’s settings:</p>
<p><img src="/assets/img/posts/vibe-coding-with-jetbrains-junie/junie_settings.png" alt="image"></p>
<ul>
<li>
<p><strong>Models</strong>:<br>
I chose <strong>Claude Sonnet 4</strong> as my model, as it’s currently one of the best for coding tasks.</p>
</li>
<li>
<p><strong>Connect MCP Servers</strong>:<br>
MCP servers allow Junie to interact with external resources. They can be configured globally in settings or
specifically for a project by adding them to the <code>/.junie/mcp</code> directory in the project’s root folder.<br>
For instance, if we integrate Stripe, connecting the Stripe MCP server ensures Junie understands and accurately
utilizes Stripe’s latest API operations, significantly improving the quality of generated code.</p>
</li>
<li>
<p><strong>Action Allowlist</strong>:<br>
Here, we can specify terminal commands Junie may run directly without asking for our confirmation.</p>
</li>
</ul>
<h3 id="init-pyzlides" tabindex="-1"><a class="header-anchor" href="#init-pyzlides">Init Pyzlides</a></h3>
<p>To help Junie understand the project clearly, I prepared an initial prompt using ChatGPT. The complete prompt is
available in the Pyzlides repo <a href="https://github.com/adarshdigievo/pyzlides/blob/main/original_instructions.md">here</a>.</p>
<p>Here’s a brief version of the initial prompt:</p>
<blockquote>
<p>Pyzlides: Python Presentation Creator</p>
<p>Overview</p>
<p>Pyzlides is a Python library that lets us create PDF presentation slides directly from Python code. Slides are
generated in standard presentation resolution, and elements like headers, body text, images, and layouts can be
defined
using simple Python classes. Pyzlides supports custom themes, layouts, and ordering slides through a configuration
file.</p>
<p>Features</p>
<ol>
<li><strong>Declarative Slide Creation</strong>: Define slides using Python classes like <code>Head1</code>, <code>BodyText</code>, <code>Img</code>, etc.</li>
<li><strong>Custom Layouts</strong>: Use custom layouts like <code>Center</code> or <code>Bottom</code> for positioning elements.</li>
<li><strong>Configurable Themes</strong>: Define themes such as colors, fonts, and backgrounds via a config file.</li>
<li><strong>Slide Ordering</strong>: Specify slide sequence in the configuration file.</li>
<li><strong>PDF Output</strong>: Generate presentations as PDF files.</li>
<li><strong>Extendable</strong>: Easily extend slide functionality by creating new element types or layouts.</li>
</ol>
<hr>
<p>Dependencies</p>
<ul>
<li><strong>ReportLab</strong>: PDF slide generation.</li>
<li><strong>Pillow</strong>: Image handling.</li>
<li><strong>PyYAML</strong>: Config file parsing.</li>
</ul>
<p>Core Classes</p>
<ol>
<li>
<p><strong>Base Slide Element Classes</strong></p>
<ul>
<li>
<p><code>Head1(text: str)</code>: Large header text.</p>
</li>
<li>
<p><code>BodyText(text: str)</code>: Paragraph-style text.</p>
</li>
<li>
<p><code>Img(path: str, caption: str)</code>: Image with optional caption.</p>
</li>
<li>
<p><code>Bold(text: str)</code>: Bold formatting.</p>
</li>
<li>
<p><code>Center(element)</code>: Center-align element.</p>
</li>
<li>
<p><code>Bottom(element)</code>: Bottom-align element.</p>
</li>
</ul>
</li>
</ol>
</blockquote>
<p>To begin, I clicked on the example prompt button in Junie to generate project instructions. Junie created detailed
guidelines automatically and stored them in the <code>.junie</code> directory. This file includes key features, project structure,
and expectations. You can view it <a href="https://github.com/adarshdigievo/pyzlides/blob/main/.junie/guidelines.md">here</a>.</p>
<h2 id="start-coding" tabindex="-1"><a class="header-anchor" href="#start-coding">Start Coding</a></h2>
<p>One aspect I liked about Junie was its ability to first generate a clear implementation plan before writing any code.
This made complex tasks manageable and easy to review.</p>
<p>Once Junie starts, it updates progress as each step completes. Below you can see Junie’s initial implementation plan for
Pyzlides.</p>
<p><img src="/assets/img/posts/vibe-coding-with-jetbrains-junie/initial_plan.png" alt="image"></p>
<p>Below is the expanded interface when the task was completed by Junie. The pane on the right shows the steps taken for
the implementation.</p>
<p><img src="/assets/img/posts/vibe-coding-with-jetbrains-junie/initial_plan_done.png" alt="image">
After finishing a task, Junie suggests helpful follow-up actions like adding files to Git or reverting unwanted changes.</p>
<p><img src="/assets/img/posts/vibe-coding-with-jetbrains-junie/done_actions.png" alt="image"></p>
<p>The initial implementation by Junie was already impressive. There were minor alignment issues, so I asked Junie to fix
these in the next prompt:</p>
<blockquote>
<p>the pages are made in portrait mode - change it to landscape mode.</p>
<p>also, bolding does not seem to work.</p>
<p>increase the text size - it is a presentation, not just a simple pdf file.</p>
<p>also make necessary changes so that this can be published as a library.
people can install this library and then add config and slide files and use the lib to generate pdf. sample slide
files and config can be moved to examples folder if needed. keep the library source directory clean</p>
</blockquote>
<p>Once fixed, I asked Junie to add more advanced features and create example slides to showcase Pyzlides capabilities
clearly:</p>
<blockquote>
<p>create multiple elements to support:</p>
<p>title slide - just a title in the centre - horizontally and vertically</p>
<p>image backgrounds - support images as slide backgrounds.</p>
<p>fix text getting cut out to the next sections.</p>
<p>add H1 to h3 tags - different type of sections.</p>
<p>in the example presentation, add slides to illustrate various types - title only, title with text, title with image,
title with image and text, dark mode presentation, white mode presentation, custom background image presentation.</p>
<p>make folders inside example folder for each example. add more elements to the definition as needed - to support a
fully featured slides functionality.</p>
<p>also, inline text colours can be specified to text elements.</p>
</blockquote>
<p>After this, I asked Junie to add features like code snippets and grid layouts. After these enhancements,
the library was nearly complete.</p>
<h2 id="finishing-up" tabindex="-1"><a class="header-anchor" href="#finishing-up">Finishing Up</a></h2>
<p>There were a few remaining issues, primarily with text overflowing slides. I prompted Junie to handle these scenarios
clearly:</p>
<blockquote>
<ol>
<li>
<p>detect when texts flow out of the view - and then fix it to go to next line. regenerate all examples after this
change. also test this with long headings, text, etc. there should be a bit of default margins on top botoom left
and right.</p>
</li>
<li>
<p>also, add a code component - that accept a code in markdown block and provide syntax highlighted output in text.
you can use the already installed pygments library for the same.</p>
</li>
</ol>
</blockquote>
<p>Next, I asked Junie to address a few bugs from previous implementations:</p>
<blockquote>
<ul>
<li>syntax highlight is not working.</li>
<li>when there is too much text, split it into multiple slides automatically. currently the contents are being cut out -
for example in the text and code example.</li>
<li>also the newline feature is not working as expected. since title texts are starting from centre of the page, some
text is still getting cut out horizontally - relook the calculation part</li>
</ul>
</blockquote>
<p>Throughout this project, I enabled Brave Mode. This allowed Junie to automatically run example code, generate PDFs, and
proactively identify and fix issues.</p>
<p>To make Pyzlides easy for new users, I also had Junie add a README to guide them through using the library. At this
stage, I noticed an unused <code>layout</code> section in the config files. I asked Junie to remove this:</p>
<blockquote>
<p>i can see that the layouts section in config is unused. remove this from example config files and Config class defined
in core</p>
</blockquote>
<p>Junie’s response was prompt and accurate:</p>
<blockquote>
<p>The <code>layouts</code> section was removed from the Config class in <a href="http://core.py">core.py</a> and from all example config files, confirming it
was unused. Functionality was verified by successfully generating presentations from both the basic-presentation and
grid-layout examples without any errors.</p>
</blockquote>
<h3 id="summary" tabindex="-1"><a class="header-anchor" href="#summary">Summary</a></h3>
<p>The entire Pyzlides project—including fixes and improvements—was completed with fewer than 10 prompts. Below is the
final file structure:</p>
<pre class="language-text"><code class="language-text">project_root/
├── .gitignore
├── pyproject.toml
├── pyzlides.py
├── readme.md
├── uv.lock
│
├── .junie/
│   └── guidelines.md
│
├── pyzlides/
│   ├── __init__.py
│   ├── __main__.py
│   ├── cli.py
│   └── core.py   # Core library functionality (1010 lines)
│
└── examples/
    ├── README.md
    │
    ├── images/
    │   ├── bg_black1.jpg
    │   ...
    │
    ├── basic-presentation/
    │   ...
</code></pre>
<p>These are the core files of the library:</p>
<ul>
<li>
<p><code>pyzlides.py</code>: Main command-line entry point for generating presentations.</p>
</li>
<li>
<p><code>pyzlides/</code></p>
<ul>
<li><code>cli.py</code>: Handles command-line interface functionality.</li>
<li><code>core.py</code>: Contains the core implementation and logic of Pyzlides. Includes the component definitions, parsing
logic and pdf generation code.</li>
</ul>
</li>
</ul>
<p><strong>My conclusion</strong>: I’m impressed with Junie - especially loved the planning mode. Few would guess an AI coding agent
could accomplish such a detailed
task within minutes.</p>
<blockquote>
<p>I haven’t fully refined or battle-tested Pyzlides yet, but it’s already an excellent starting point. You can review
the generated PDFs in the <code>examples</code> folder, and see for yourself :)</p>
</blockquote>
<h3 id="notes-for-ai-assisted-coding" tabindex="-1"><a class="header-anchor" href="#notes-for-ai-assisted-coding">Notes for AI-Assisted Coding</a></h3>
<ul>
<li>Use Brave Mode cautiously, especially in complex or sensitive projects.</li>
<li>Provide clear, detailed context for the best results.</li>
<li>Always verify the plan generated by the agent and course correct whenever needed.</li>
<li>Consider tools like <a href="https://gitingest.com/">GitIngest</a> to enhance context. Also, connect MCP servers when available
for deeper integrations.</li>
</ul>
<h3 id="links" tabindex="-1"><a class="header-anchor" href="#links">Links</a></h3>
<ul>
<li>
<p>Pyzlides GitHub project: <a href="https://github.com/adarshdigievo/pyzlides/">GitHub - adarshdigievo/pyzlides</a></p>
</li>
<li>
<p>Junie Docs: <a href="https://www.jetbrains.com/junie/">Junie, the AI coding agent by JetBrains</a></p>
</li>
<li>
<p>Junie Playbook: <a href="https://www.jetbrains.com/guide/ai/article/junie/pycharm/">PyCharm Junie Playbook - JetBrains Guide</a></p>
</li>
<li>
<p>Download Pycharm  : <a href="https://jb.gg/pcusad">PyCharm: The only Python IDE you need</a></p>
</li>
</ul>
<p>I’d like to thank the JetBrains team for collaborating on this project. I look forward to using Junie in future
experiments.</p>
]]></content>
  </entry>
  <entry>
    <title>Will AI Replace Junior Developers? I Asked Experts at Pycon US</title>
    <link href="https://blog.adarshd.dev/posts/pycon-us-ai-and-future-of-programming/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/pycon-us-ai-and-future-of-programming/</id>
    <published>2025-06-16T13:50:00Z</published>
    <updated>2025-06-16T13:50:00Z</updated>
    <content type="html"><![CDATA[<p>I attended <em>Pycon US in Pittsburgh</em> - This was my first in-person Pycon US.</p>
<p>At this PyCon, I decided to try something different. I skipped most of the talks and focused on the hallway track - just
walking around, meeting people, and asking questions.</p>
<p>I was also presenting a poster at PyCon - on using Python to improve at
chess: <a href="https://github.com/adarshdigievo/talks/tree/main/Pycon%20US%2025%20-%20Improving%20in%20Chess">presentation materials here.</a></p>
<p>Before the conference, I had prepared a few questions I wanted to ask the Python people:</p>
<ul>
<li>
<p>What do you think is the future of programming?</p>
</li>
<li>
<p>Will AI take over programming jobs in the next 2–3 years?</p>
</li>
<li>
<p>What AI tools do you use in your daily work?</p>
</li>
<li>
<p>What advice do you have for Python developers?</p>
</li>
</ul>
<p>Most of the people I talked to are familiar names in the Python community. I’ve read their blog posts, watched their
talks, or used their open-source tools. There were many more people I wish I had a chance to talk to, but I’m glad I got
to speak with the ones I did.</p>
<p>One thing surprised me - very few people said AI will replace developers. I expected more.</p>
<p>Later I realized why. Most of the folks I spoke to are working on deep, core programming problems - like optimizing
CPython, or building dev tools. These are areas where current AI tools aren’t that useful. LLMs haven’t seen much of
that code. So maybe it’s not that surprising after all.</p>
<p>Here’s what they said:</p>
<hr>
<h2 id="guido-van-rossum-dutch-programmer" tabindex="-1"><a class="header-anchor" href="#guido-van-rossum-dutch-programmer">Guido van Rossum — Dutch programmer</a></h2>
<p>Guido said AI won’t replace programmers.</p>
<p>He’s not a fan of “vibe coding” (letting AI write code you don’t understand). He is not impressed with the quality of
the code generated by current tools.
But he does use GitHub Copilot (for autocomplete) and he expects AI assistants to get much better in the future -
something like a
supercharged Copilot.</p>
<hr>
<h2 id="anthony-shaw-psf-fellow-author-of-cpython-internals" tabindex="-1"><a class="header-anchor" href="#anthony-shaw-psf-fellow-author-of-cpython-internals">Anthony Shaw — PSF Fellow, Author of CPython Internals</a></h2>
<p>Anthony said AI is like the new Stack Overflow.</p>
<p>Earlier, people copied code from blogs or seniors. Now it’s from ChatGPT.<br>
AI is useful for boilerplate or things you already know. He uses <strong>Copilot Chat</strong> for regular stuff and <strong>agent mode</strong>
when he’s working on something unfamiliar (like frontend work).</p>
<p>His advice: <em>Don’t blindly rely on AI</em>. If you don’t keep learning, the gap between juniors and seniors will grow fast.</p>
<blockquote class="prompt-tip">
<p><a href="https://tonybaloney.github.io/">Anthony's Website</a> · <a href="https://tonybaloney.github.io/vscode-pets/">My favorite project – VSCode Pets</a></p>
</blockquote>
<hr>
<h2 id="anthony-sottile-maintainer-of-pre-commit-flake8-and-pytest" tabindex="-1"><a class="header-anchor" href="#anthony-sottile-maintainer-of-pre-commit-flake8-and-pytest">Anthony Sottile - Maintainer of pre-commit, flake8, and pytest</a></h2>
<p>He doesn’t use AI tools.</p>
<p>He said his problems are too complex for AI. It’s easier for him to just write the code than explain it to a chatbot.</p>
<p>He does agree that AI is good for boring or repetitive code.</p>
<blockquote class="prompt-tip">
<p><a href="https://github.com/asottile">Anthony's GitHub</a> · <a href="https://www.youtube.com/c/anthonywritescode">YouTube – Anthony Writes Code</a></p>
</blockquote>
<hr>
<h2 id="reuven-m-lerner-python-author-trainer" tabindex="-1"><a class="header-anchor" href="#reuven-m-lerner-python-author-trainer">Reuven M. Lerner - Python Author &amp; Trainer</a></h2>
<p>Reuven said companies should <em>not</em> replace junior devs.</p>
<p>He said juniors are like seeds. If companies don’t invest in them now, they’ll struggle later when they only have senior
devs and AI bots.</p>
<p>He uses AI mostly for writing—like reviewing posts or generating images. He rarely uses it for coding.</p>
<blockquote class="prompt-tip">
<p><a href="https://lerner.co.il/">Reuven's Website – lerner.co.il</a> · Great courses and newsletters for Python devs</p>
</blockquote>
<hr>
<h2 id="trey-hunner-python-morsels" tabindex="-1"><a class="header-anchor" href="#trey-hunner-python-morsels">Trey Hunner — Python Morsels</a></h2>
<p>Trey said AI won’t replace developers.</p>
<p>But it might change how we work.<br>
Developers might end up reading and reviewing more code instead of writing everything from scratch.</p>
<p>He doesn’t use Copilot or coding tools. He copies from ChatGPT when needed and uses a tool called Typingmind to talk to
AI.</p>
<blockquote class="prompt-tip">
<p><a href="https://treyhunner.com/">Trey's Website</a> · <a href="https://www.pythonmorsels.com/">Python Morsels</a></p>
</blockquote>
<p>It is also worth a mention that Trey taught us the <a href="https://treyhunner.com/2015/06/cabo-card-game/">Cabo card game</a>
during the evenings after PyCon.</p>
<hr>
<h2 id="samuel-colvin-founder-of-pydantic" tabindex="-1"><a class="header-anchor" href="#samuel-colvin-founder-of-pydantic">Samuel Colvin — Founder of Pydantic</a></h2>
<p>Samuel had a different view.<br>
He said AI <em>will</em> replace some programmers.</p>
<p>Companies will use AI to do more with fewer people.<br>
He sees AI as a way to speed things up and make work more efficient.</p>
<p>He uses tools like <code>trae</code>, <code>cline</code>, and others to boost his workflow.</p>
<blockquote class="prompt-tip">
<p><a href="https://scolvin.com/">Samuel's Website</a> · <a href="https://github.com/samuelcolvin">GitHub</a></p>
</blockquote>
<hr>
<h2 id="simon-willison-django-co-creator-creator-of-datasette" tabindex="-1"><a class="header-anchor" href="#simon-willison-django-co-creator-creator-of-datasette">Simon Willison — Django co-creator, Creator of Datasette</a></h2>
<p>Simon said AI won’t replace developers. But it will create new types of work and improve productivity.</p>
<p>He uses Claude, ChatGPT, and his own LLM CLI tool.<br>
For personal scripts, he uses AI to get things done quickly, and reviews the code properly when it matters.</p>
<blockquote class="prompt-tip">
<p><a href="https://simonwillison.net/">Simon's Website</a> · <a href="https://github.com/simonw">GitHub</a></p>
</blockquote>
<hr>
<h2 id="mariatta-wijaya-python-core-developer" tabindex="-1"><a class="header-anchor" href="#mariatta-wijaya-python-core-developer">Mariatta Wijaya — Python Core Developer</a></h2>
<p>Mariatta said AI is more helpful for senior developers.</p>
<p>Seniors can use AI to write code quickly and then fix what’s wrong.<br>
For juniors, it depends. If the task is small, AI can help. But juniors still need to understand what the code does.</p>
<p>She uses Copilot in her daily work.</p>
<p>She also recommends this talk from PyTexas:<br>
<a href="https://www.youtube.com/watch?v=FP9VoFPJsiQ">Heather’s Talk – How to Think Like a Senior Engineer</a></p>
<blockquote class="prompt-tip">
<p><a href="https://mariatta.ca/">Mariatta's Website</a> · <a href="https://github.com/mariatta">GitHub</a></p>
</blockquote>
<hr>
<h2 id="final-thoughts" tabindex="-1"><a class="header-anchor" href="#final-thoughts">Final Thoughts</a></h2>
<p>Most Python people agree: AI won’t replace developers anytime soon.</p>
<p>My thoughts: it <em>will</em> definitely change how we work.</p>
<p>If you’re using AI without understanding, you’ll likely fall behind.<br>
If you keep learning and adapt, you’ll do fine.</p>
<hr>
<h3 id="my-personal-favorite-moment" tabindex="-1"><a class="header-anchor" href="#my-personal-favorite-moment">My personal favorite moment</a></h3>
<p>One of the highlights of PyCon US for me was meeting <strong>Paul Everitt</strong>.</p>
<p>He gave me a sneak peek of the Python documentary trailer <em>before</em> it was released on YouTube. And even better—he handed
me a <strong>chocolate Cornetto</strong> from the PyCharm booth while telling stories. Instant core memory.</p>
<p>Paul is one of the most enthusiastic and genuine people I met at the conference. A natural storyteller.</p>
<p>A few things I didn’t know about him until then:</p>
<ul>
<li>
<p>He managed <a href="https://www.navy.mil">navy.mil</a> while serving in the U.S. military—one of the earliest websites on the
internet.</p>
</li>
<li>
<p>He was a keynote speaker at the <strong>first-ever Python conference</strong> in 1994.</p>
</li>
<li>
<p>He helped <strong>incorporate the Python Software Foundation</strong>.</p>
</li>
<li>
<p>He was Guido’s manager during his Plone days.</p>
</li>
</ul>
<p>The kind of person you meet once and never forget.</p>
<hr>
<p>That’s all from my hallway track this year.</p>
]]></content>
  </entry>
  <entry>
    <title>Thread Safety in Python - My first tutorial for Real Python</title>
    <link href="https://blog.adarshd.dev/posts/thread-safety-real-python-tutorial/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/thread-safety-real-python-tutorial/</id>
    <published>2024-10-24T12:30:00Z</published>
    <updated>2024-10-24T12:30:00Z</updated>
    <content type="html"><![CDATA[<p>My first tutorial for Real Python got published 🎉</p>
<p>“Python Thread Safety: Using a Lock and Other Techniques”: <a href="https://realpython.com/python-thread-lock/">https://realpython.com/python-thread-lock/</a></p>
<h3 id="why-thread-safety" tabindex="-1"><a class="header-anchor" href="#why-thread-safety">Why thread safety?</a></h3>
<p>Below is a classic example of creating a singleton class is Python:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">SingletonClass</span><span class="token punctuation">(</span><span class="token builtin">object</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">def</span> <span class="token function">__new__</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> <span class="token keyword">not</span> <span class="token builtin">hasattr</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> <span class="token string">'instance'</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
      cls<span class="token punctuation">.</span>instance <span class="token operator">=</span> <span class="token builtin">super</span><span class="token punctuation">(</span>SingletonClass<span class="token punctuation">,</span> cls<span class="token punctuation">)</span><span class="token punctuation">.</span>__new__<span class="token punctuation">(</span>cls<span class="token punctuation">)</span>
    <span class="token keyword">return</span> cls<span class="token punctuation">.</span>instance
</code></pre>
<p>The <code>SingletonClass</code> is supposed to have only 1 object, but the code can lead to the creation of more than one instances
when executed in a multithreaded environment.</p>
<p>Read the tutorial to spot such race conditions and learn to fix them using Python’s synchronization primitives.</p>
<p>Read now at Real Python Website: <a href="https://realpython.com/python-thread-lock/">https://realpython.com/python-thread-lock/</a></p>
<hr>
<hr>
<p>I share interesting Python snippets from open-source projects illustrating Python language features in my
newsletter, “Python in the Wild”.</p>
<p>Subscribe to the newsletter on <a href="https://adarshd.substack.com/">Substack</a>
or <a href="https://www.linkedin.com/newsletters/python-in-the-wild-7155981512197181440/">Linkedin</a> to receive new Pythonic
posts to your email 💌🚀.</p>
]]></content>
  </entry>
  <entry>
    <title>Thread Local Data in Python</title>
    <link href="https://blog.adarshd.dev/posts/thread-local-storage-in-python/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/thread-local-storage-in-python/</id>
    <published>2024-02-18T11:29:00Z</published>
    <updated>2024-02-18T11:29:00Z</updated>
    <content type="html"><![CDATA[<p>Since threads in Python share the memory space of their parent process, we might need to define thread-specific variables for specific use cases to avoid unintended side effects.</p>
<p>In this article, we will:</p>
<ol>
<li>Explore Thread local storage: Python threading module’s solution for thread-specific/thread-private values.</li>
<li>See its real-world example from Open-Source ( usage in the Peewee ORM library).</li>
<li>Look at the CPython source code to see how thread local storage is implemented under the hood.</li>
</ol>
<h2 id="background" tabindex="-1"><a class="header-anchor" href="#background">Background</a></h2>
<p>Threads share the memory space of their parent process.
This will allow us to seamlessly access and share variables, data structures, etc., across threads.
But this comes with its own challenges.
There may be scenarios where we need to isolate variables and might need to store data specific to each thread.
Thread local storage can be leveraged in this case.</p>
<p>We can use <a href="https://docs.python.org/3/library/threading.html#thread-local-data"><code>local()</code> found in the <code>threading</code> module</a> to define thread-local variables.</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> threading
<span class="token keyword">import</span> time

<span class="token comment"># Create a thread-local storage object (1)</span>
thread_local <span class="token operator">=</span> threading<span class="token punctuation">.</span>local<span class="token punctuation">(</span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">init_data</span><span class="token punctuation">(</span>number<span class="token punctuation">)</span><span class="token punctuation">:</span>
    thread_local<span class="token punctuation">.</span>number <span class="token operator">=</span> number <span class="token operator">*</span> <span class="token number">100</span>


<span class="token keyword">def</span> <span class="token function">show_data</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Thread </span><span class="token interpolation"><span class="token punctuation">{</span>threading<span class="token punctuation">.</span>current_thread<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>name<span class="token punctuation">}</span></span><span class="token string"> has number </span><span class="token interpolation"><span class="token punctuation">{</span>thread_local<span class="token punctuation">.</span>number<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">worker</span><span class="token punctuation">(</span>number<span class="token punctuation">)</span><span class="token punctuation">:</span>
    init_data<span class="token punctuation">(</span>number<span class="token punctuation">)</span>

    <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
        show_data<span class="token punctuation">(</span><span class="token punctuation">)</span>


thread1 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>worker<span class="token punctuation">,</span> name<span class="token operator">=</span><span class="token string">"A"</span><span class="token punctuation">,</span> kwargs<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">"number"</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
thread2 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>worker<span class="token punctuation">,</span> name<span class="token operator">=</span><span class="token string">"B"</span><span class="token punctuation">,</span> kwargs<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">"number"</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">}</span><span class="token punctuation">)</span>

thread1<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>
thread2<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

thread1<span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>
thread2<span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>

</code></pre>
<p>The program output will be:</p>
<pre class="language-text"><code class="language-text">Thread A has number 100
Thread B has number 200
Thread A has number 100
Thread B has number 200
Thread A has number 100
Thread B has number 200
</code></pre>
<p>As seen in <code>comment #(1)</code>, a thread-local storage object was created and assigned to the variable <code>thread_local</code>.
Arbitrary attributes can be assigned to this variable, which are specific to the thread that performs the assignment and is isolated from others.</p>
<p>In the example, each thread stores its own <code>number</code> attribute to the thread_local object and accesses the thread-specific value during their concurrent execution.</p>
<h2 id="usage-in-the-wild" tabindex="-1"><a class="header-anchor" href="#usage-in-the-wild">Usage in the Wild</a></h2>
<p>Peewee, a Python ORM, utilizes thread-local data in its <code>ThreadSafeDatabaseMetadata</code> to support dynamic database switches at runtime in multithreaded applications.</p>
<p>The source code can be found <a href="https://github.com/coleifer/peewee/blob/01b2d94a4029858b9d2b1bee6fac40eed27ad9ad/playhouse/shortcuts.py#L322">here.</a></p>
<pre class="language-python"><code class="language-python"><span class="token comment"># File: peewee/playhouse/shortcuts.py</span>

<span class="token keyword">class</span> <span class="token class-name">ThreadSafeDatabaseMetadata</span><span class="token punctuation">(</span>Metadata<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Metadata class to allow swapping database at run-time in a multi-threaded
    application. To use:

    class Base(Model):
        class Meta:
            model_metadata_class = ThreadSafeDatabaseMetadata
    """</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">**</span>kwargs<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment"># The database attribute is stored in a thread-local.</span>
        self<span class="token punctuation">.</span>_database <span class="token operator">=</span> <span class="token boolean">None</span>
        self<span class="token punctuation">.</span>_local <span class="token operator">=</span> threading<span class="token punctuation">.</span>local<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token builtin">super</span><span class="token punctuation">(</span>ThreadSafeDatabaseMetadata<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">.</span>__init__<span class="token punctuation">(</span><span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">**</span>kwargs<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">_get_db</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token builtin">getattr</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>_local<span class="token punctuation">,</span> <span class="token string">"database"</span><span class="token punctuation">,</span> self<span class="token punctuation">.</span>_database<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">_set_db</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> db<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">if</span> self<span class="token punctuation">.</span>_database <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>_database <span class="token operator">=</span> db
        self<span class="token punctuation">.</span>_local<span class="token punctuation">.</span>database <span class="token operator">=</span> db

    database <span class="token operator">=</span> <span class="token builtin">property</span><span class="token punctuation">(</span>_get_db<span class="token punctuation">,</span> _set_db<span class="token punctuation">)</span>
</code></pre>
<p>In multithreaded applications using <code>peewee</code> ORM, database switching at runtime without using <code>ThreadSafeDatabaseMetada</code> can lead to errors.
If multiple threads work in parallel and one thread changes the connection parameters, this can lead to errors such as writing to a wrong DB, inconsistent writes (in case of non-atomic DB operations), etc.</p>
<p><code>ThreadSafeDatabaseMetada</code> solves this by keeping the database attributes in a thread-local object (<code>self._local</code>). In this way, dynamic changes to the database will only affect the thread that made the change. Other threads will keep working with their existing databases.</p>
<h2 id="should-i-use-it" tabindex="-1"><a class="header-anchor" href="#should-i-use-it">Should I use it?</a></h2>
<p>Thread-local storage should be used when:</p>
<p>You are writing a multi-threaded application (obviously) and:</p>
<ul>
<li>If some variables are used by the current thread only and are not relevant to the main thread/other threads.</li>
<li>You find out that changes made by one thread can lead to unintended side effects in other concurrent threads.</li>
</ul>
<p>Generally, if you are working with multiple threads and there is shared mutable data:</p>
<p>You should check if sharing these data across threads is actually needed.</p>
<ol>
<li>
<p>If sharing can be avoided, use thread-local storage to make the data specific to each thread.</p>
</li>
<li>
<p>Otherwise, implement locks or other synchronization primitives to enforce thread safety.</p>
</li>
</ol>
<p><strong>Note</strong>: Context variables from the <a href="https://docs.python.org/3.12/library/contextvars.html#module-contextvars">contextvars standard library module</a> can be used as an alternative to <code>threading.local()</code>. They work with multithreading as well as asyncio to store context-specific information. In the case of asyncio programs, context variables allow each coroutine task to have its own set of variables isolated from other (asyncio) tasks.</p>
<h2 id="behind-the-scenes" tabindex="-1"><a class="header-anchor" href="#behind-the-scenes">Behind the scenes</a></h2>
<p>The Python implementation of threading.local can be found in the <a href="https://github.com/python/cpython/blob/3.12/Lib/_threading_local.py"><code>/Lib/_threading_local.py</code> path in CPython source code.</a></p>
<p>The <code>_localimpl</code> class is used to store thread-local values.</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">_localimpl</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""A class managing thread-local dicts"""</span>
    __slots__ <span class="token operator">=</span> <span class="token string">'key'</span><span class="token punctuation">,</span> <span class="token string">'dicts'</span><span class="token punctuation">,</span> <span class="token string">'localargs'</span><span class="token punctuation">,</span> <span class="token string">'locallock'</span><span class="token punctuation">,</span> <span class="token string">'__weakref__'</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment"># The key used in the Thread objects' attribute dicts.</span>
        <span class="token comment"># We keep it a string for speed but make it unlikely to clash with</span>
        <span class="token comment"># a "real" attribute.</span>
        self<span class="token punctuation">.</span>key <span class="token operator">=</span> <span class="token string">'_threading_local._localimpl.'</span> <span class="token operator">+</span> <span class="token builtin">str</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token comment"># { id(Thread) -> (ref(Thread), thread-local dict) }</span>
        self<span class="token punctuation">.</span>dicts <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre>
<p>The <code>dicts</code> attribute maps the id of a thread to a tuple. The tuple is two-membered, containing a reference to the thread and the actual dictionary storing thread local values (<code># { id(Thread) -&gt; (ref(Thread), thread-local dict) }</code>).</p>
<p>Looking at a few other methods of the class:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">_localimpl</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""A class managing thread-local dicts"""</span>
    <span class="token keyword">def</span> <span class="token function">get_dict</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Return the dict for the current thread. Raises KeyError if none
        defined."""</span>
        thread <span class="token operator">=</span> current_thread<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>dicts<span class="token punctuation">[</span><span class="token builtin">id</span><span class="token punctuation">(</span>thread<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token comment"># (1) returning the local dict of current thread</span>

    <span class="token keyword">def</span> <span class="token function">create_dict</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Create a new dict for the current thread, and return it."""</span>
        localdict <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
        key <span class="token operator">=</span> self<span class="token punctuation">.</span>key
        thread <span class="token operator">=</span> current_thread<span class="token punctuation">(</span><span class="token punctuation">)</span>
        idt <span class="token operator">=</span> <span class="token builtin">id</span><span class="token punctuation">(</span>thread<span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
        wrthread <span class="token operator">=</span> ref<span class="token punctuation">(</span>thread<span class="token punctuation">,</span> thread_deleted<span class="token punctuation">)</span>
        thread<span class="token punctuation">.</span>__dict__<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> wrlocal
        self<span class="token punctuation">.</span>dicts<span class="token punctuation">[</span>idt<span class="token punctuation">]</span> <span class="token operator">=</span> wrthread<span class="token punctuation">,</span> localdict  <span class="token comment"># (2) Populating the `dicts` with a new thread</span>
        <span class="token keyword">return</span> localdict
</code></pre>
<p>The comments <code>#(1)</code> &amp; <code>#(2)</code> illustrate operations on the <code>dicts</code> attribute discussed previously.</p>
<p><code>(1)</code>: Returns the local data dictionary corresponding to the current thread accessing it.</p>
<p><code>(2)</code>: This part initializes the local dict for a new thread.</p>
<p>Then, we have the actual <code>local</code> callable, which we call as <code>threading.local()</code> to initialize a thread-local object.</p>
<pre class="language-python"><code class="language-python">
<span class="token decorator annotation punctuation">@contextmanager</span>
<span class="token keyword">def</span> <span class="token function">_patch</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    impl <span class="token operator">=</span> <span class="token builtin">object</span><span class="token punctuation">.</span>__getattribute__<span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token string">'_local__impl'</span><span class="token punctuation">)</span>
    <span class="token keyword">try</span><span class="token punctuation">:</span>
        dct <span class="token operator">=</span> impl<span class="token punctuation">.</span>get_dict<span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment"># (3) this will return local dict of current thread -/</span>
                               <span class="token comment"># see its definition in the above snippet</span>
    <span class="token keyword">except</span> KeyError<span class="token punctuation">:</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
        <span class="token comment"># calls _localimpl`s create_dict to create &amp; init a new dict</span>
    <span class="token keyword">with</span> impl<span class="token punctuation">.</span>locallock<span class="token punctuation">:</span>
        <span class="token builtin">object</span><span class="token punctuation">.</span>__setattr__<span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token string">'__dict__'</span><span class="token punctuation">,</span> dct<span class="token punctuation">)</span> <span class="token comment"># (4) &lt;The magic> Temporarily replaces -/</span>
        <span class="token comment"># the instance's __dict__ attribute with the thread-specific dictionary.</span>
        
        <span class="token keyword">yield</span>


<span class="token keyword">class</span> <span class="token class-name">local</span><span class="token punctuation">:</span>
    __slots__ <span class="token operator">=</span> <span class="token string">'_local__impl'</span><span class="token punctuation">,</span> <span class="token string">'__dict__'</span>

    <span class="token keyword">def</span> <span class="token function">__new__</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> <span class="token operator">/</span><span class="token punctuation">,</span> <span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">**</span>kw<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

        impl <span class="token operator">=</span> _localimpl<span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment"># (1) - wraps the _localimpl object in an attribute</span>
        impl<span class="token punctuation">.</span>localargs <span class="token operator">=</span> <span class="token punctuation">(</span>args<span class="token punctuation">,</span> kw<span class="token punctuation">)</span>
        impl<span class="token punctuation">.</span>locallock <span class="token operator">=</span> RLock<span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment"># (2) - Lock for thread safety</span>
        <span class="token builtin">object</span><span class="token punctuation">.</span>__setattr__<span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token string">'_local__impl'</span><span class="token punctuation">,</span> impl<span class="token punctuation">)</span>
        <span class="token comment"># We need to create the thread dict in anticipation of</span>
        <span class="token comment"># __init__ being called, to make sure we don't call it</span>
        <span class="token comment"># again ourselves.</span>
        impl<span class="token punctuation">.</span>create_dict<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> self

    <span class="token keyword">def</span> <span class="token function">__getattribute__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">with</span> _patch<span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> <span class="token builtin">object</span><span class="token punctuation">.</span>__getattribute__<span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__setattr__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">if</span> name <span class="token operator">==</span> <span class="token string">'__dict__'</span><span class="token punctuation">:</span>
            <span class="token keyword">raise</span> AttributeError<span class="token punctuation">(</span>
                <span class="token string">"%r object attribute '__dict__' is read-only"</span>
                <span class="token operator">%</span> self<span class="token punctuation">.</span>__class__<span class="token punctuation">.</span>__name__<span class="token punctuation">)</span>
        <span class="token keyword">with</span> _patch<span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> <span class="token builtin">object</span><span class="token punctuation">.</span>__setattr__<span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">,</span> value<span class="token punctuation">)</span>

    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
</code></pre>
<p>Let’s look at various parts of the code one by one.</p>
<p><code># (1)</code>:  An object of <code>_localimpl</code> class is created and later stored in <code>_local__impl</code> attribute</p>
<p><code># (2)</code>: RLock() is used on dict operations - Concurrent writes by multiple threads may cause ‘lost writes’ since dict is not thread-safe.</p>
<p><code># (3)</code>: In the try/except block, the current thread’s local data is fetched and assigned to the variable named <code>dct</code>.</p>
<p><code># (4)</code>: The magic happens here. Here is a quick refresher on class attributes before we delve further:</p>
<blockquote>
<p>A class has a namespace implemented by a dictionary object. Class attribute references are translated to lookups in this dictionary, e.g., C.x is translated to C.<strong>dict</strong>[“x”] (although there are a number of hooks which allow for other means of locating attributes).</p>
</blockquote>
<p>The <code>_patch</code> function we are currently in is a context manager. Using the line marked as <code># (4)</code>, it patches the namespace dictionary of the local class with the <code>dot</code> variable (which stores the current-thread specific data).</p>
<p>Since the <code>__getattribute__</code> and <code>__setattr__</code> dunders of the class <code>local</code> use the <code>_patch</code> context manager, attribute access performed inside the context will use the thread-local dictionary (<code>dct</code>) replacing the class’s actual namespace dictionary.</p>
<p><strong>Note</strong>: The <code>Lib/_threading_local.py</code> starts with the below note:</p>
<blockquote>
<p>Note that this module provides a Python version of the threading.local
class. Depending on the version of Python you’re using, there may be a
faster one available. You should always import the <code>local</code> class from
<code>threading</code>.</p>
</blockquote>
<p>The code we looked at might not be the one running in our Python installations. I think newer Pythons versions are using <code>C</code> implementations of the thread local functionality for efficiency.</p>
<hr>
<p>I share interesting Python snippets 🐍 like this from open-source projects illustrating Python language features in my newsletter, “Python in the Wild”.</p>
<p>Subscribe to the newsletter on <a href="https://adarshd.substack.com/">Substack</a> or <a href="https://www.linkedin.com/newsletters/python-in-the-wild-7155981512197181440/">Linkedin</a> to receive new Pythonic posts to your email 💌🚀.</p>
]]></content>
  </entry>
  <entry>
    <title>Evolution of the `sort` in Python and the Role of `functools.cmp_to_key`</title>
    <link href="https://blog.adarshd.dev/posts/evolution-of-python-sort-and-cmp_to_key/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/evolution-of-python-sort-and-cmp_to_key/</id>
    <published>2024-02-05T13:29:00Z</published>
    <updated>2024-02-05T13:29:00Z</updated>
    <content type="html"><![CDATA[<p>In Python, the <code>sort</code> method and the <code>sorted</code> callable are commonly used for sorting operations.
<code>sort</code> is a list method which modifies the list in-place, whereas <code>sorted</code> takes an iterable as its first argument and returns a sorted list containing the iterables elements.</p>
<p>Both of these use <a href="https://en.wikipedia.org/wiki/Timsort">Timsort algorithm</a> under the hood, to perform the sorting operation.</p>
<p>In this article, we will explore the evolution of sorting in Python from Python 2 to 3 and will look into the <code>cmp_to_key</code> function from <code>functools</code>.</p>
<h2 id="background" tabindex="-1"><a class="header-anchor" href="#background">Background</a></h2>
<p>Below are the type annotations for <code>sort</code> and <code>sorted</code>, from the <a href="https://github.com/python/typeshed/blob/2c295057659d2b8bdfe632012c04497273b002b6/stdlib/builtins.pyi#L1665">Python typeshed library</a>.</p>
<pre class="language-python"><code class="language-python"><span class="token decorator annotation punctuation">@overload</span>
<span class="token keyword">def</span> <span class="token function">sorted</span><span class="token punctuation">(</span>
    __iterable<span class="token punctuation">:</span> Iterable<span class="token punctuation">[</span>SupportsRichComparisonT<span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token operator">*</span><span class="token punctuation">,</span>
    key<span class="token punctuation">:</span> <span class="token boolean">None</span> <span class="token operator">=</span> <span class="token boolean">None</span><span class="token punctuation">,</span>
    reverse<span class="token punctuation">:</span> <span class="token builtin">bool</span> <span class="token operator">=</span> <span class="token boolean">False</span>
<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">list</span><span class="token punctuation">[</span>SupportsRichComparisonT<span class="token punctuation">]</span><span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>


<span class="token decorator annotation punctuation">@overload</span>
<span class="token keyword">def</span> <span class="token function">sorted</span><span class="token punctuation">(</span>
    __iterable<span class="token punctuation">:</span> Iterable<span class="token punctuation">[</span>_T<span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token operator">*</span><span class="token punctuation">,</span>
    key<span class="token punctuation">:</span> Callable<span class="token punctuation">[</span><span class="token punctuation">[</span>_T<span class="token punctuation">]</span><span class="token punctuation">,</span> SupportsRichComparison<span class="token punctuation">]</span><span class="token punctuation">,</span>
    reverse<span class="token punctuation">:</span> <span class="token builtin">bool</span> <span class="token operator">=</span> <span class="token boolean">False</span>
<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">list</span><span class="token punctuation">[</span>_T<span class="token punctuation">]</span><span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>


<span class="token keyword">class</span> <span class="token class-name">list</span><span class="token punctuation">(</span>MutableSequence<span class="token punctuation">[</span>_T<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token decorator annotation punctuation">@overload</span>
    <span class="token keyword">def</span> <span class="token function">sort</span><span class="token punctuation">(</span>
        self<span class="token punctuation">:</span> <span class="token builtin">list</span><span class="token punctuation">[</span>SupportsRichComparisonT<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">*</span><span class="token punctuation">,</span> key<span class="token punctuation">:</span> <span class="token boolean">None</span> <span class="token operator">=</span> <span class="token boolean">None</span><span class="token punctuation">,</span> reverse<span class="token punctuation">:</span> <span class="token builtin">bool</span> <span class="token operator">=</span> <span class="token boolean">False</span>
    <span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

    <span class="token decorator annotation punctuation">@overload</span>
    <span class="token keyword">def</span> <span class="token function">sort</span><span class="token punctuation">(</span>
        self<span class="token punctuation">,</span> <span class="token operator">*</span><span class="token punctuation">,</span> key<span class="token punctuation">:</span> Callable<span class="token punctuation">[</span><span class="token punctuation">[</span>_T<span class="token punctuation">]</span><span class="token punctuation">,</span> SupportsRichComparison<span class="token punctuation">]</span><span class="token punctuation">,</span> reverse<span class="token punctuation">:</span> <span class="token builtin">bool</span> <span class="token operator">=</span> <span class="token boolean">False</span>
    <span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
</code></pre>
<p>Both <code>sort</code> and <code>sorted</code> accept an optional keyword-only argument <code>key</code>. It should be a callable and if supplied, will be used to calculate the comparison key.</p>
<blockquote>
<p>From Python docs:</p>
<p><em>key</em> specifies a function of one argument that is used to extract a comparison key from each list element (for example, <code>key=str.lower</code>).
The key corresponding to each item in the list is calculated once and then used for the entire sorting process.</p>
</blockquote>
<p>Now lets take a look at the <a href="https://docs.python.org/2.7/library/functions.html?highlight=sort#sorted">Python 2 docs for the sorted built-in</a>:</p>
<pre class="language-text"><code class="language-text"> sorted(iterable[, cmp[, key[, reverse]]])

    Return a new sorted list from the items in iterable.

    The optional arguments cmp, key, and reverse have the same meaning as those for the list.sort() method (described in section Mutable Sequence Types).

    cmp specifies a custom comparison function of two arguments (iterable elements), 
    which should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument.
    
    ...
</code></pre>
<p>There is an additional argument <code>cmp</code> compared to what we have seen in Python 3.</p>
<p>The docs also mentions:</p>
<blockquote>
<p>In general, the key and reverse conversion processes are much faster than specifying an equivalent cmp function. This is because cmp is called multiple times for each list element while key and reverse touch each element only once. Use functools.cmp_to_key() to convert an old-style cmp function to a key function.</p>
</blockquote>
<p>The <code>cmp</code> argument was removed in Python 3 and the <a href="https://docs.python.org/3/library/functools.html#functools.cmp_to_key">cmp_to_key helper</a> to convert comparison functions to new-style key functions has been added to the functools module.</p>
<h2 id="usage-in-the-wild" tabindex="-1"><a class="header-anchor" href="#usage-in-the-wild">Usage in the Wild</a></h2>
<p>If we have a comparison function (which takes two arguments and returns their comparison result), this can be converted to a sort key by using <code>cmp_to_key</code> function, provided in the <code>functools</code> module.</p>
<p>The below example from the <a href="https://github.com/craffel/pretty-midi">pretty_midi</a>, a MIDI processing library illustrates the use of <code>cmp_to_key</code>.</p>
<p><a href="https://github.com/craffel/pretty-midi/blob/07f4174ef701c3355fb6d4aa72ae968026d5df10/pretty_midi/pretty_midi.py#L1473">Link to source code</a></p>
<pre class="language-python"><code class="language-python">
<span class="token keyword">class</span> <span class="token class-name">PrettyMIDI</span><span class="token punctuation">(</span><span class="token builtin">object</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""A container for MIDI data in an easily-manipulable format."""</span>

    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

    <span class="token keyword">def</span> <span class="token function">write</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> filename<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Write the MIDI data out to a .mid file."""</span>

        <span class="token keyword">def</span> <span class="token function">event_compare</span><span class="token punctuation">(</span>event1<span class="token punctuation">,</span> event2<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment"># &lt;--- (1)</span>
            <span class="token triple-quoted-string string">"""Compares two events for sorting.

            Events are sorted by tick time ascending. Events with the same tick
            time ares sorted by event type. Some events are sorted by
            additional values. For example, Note On events are sorted by pitch
            then velocity, ensuring that a Note Off (Note On with velocity 0)
            will never follow a Note On with the same pitch.

            Parameters
            ----------
            event1, event2 : mido.Message
               Two events to be compared.
            """</span>

            secondary_sort <span class="token operator">=</span> <span class="token punctuation">{</span>
                <span class="token string">"set_tempo"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"time_signature"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"key_signature"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">3</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"lyrics"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">4</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"text_events"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">5</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"program_change"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">6</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"pitchwheel"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">7</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span> <span class="token operator">+</span> e<span class="token punctuation">.</span>pitch<span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"control_change"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span>
                    <span class="token punctuation">(</span><span class="token number">8</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>control <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span> <span class="token operator">+</span> e<span class="token punctuation">.</span>value
                <span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"note_off"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">9</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>note <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"note_on"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">10</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>note <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span> <span class="token operator">+</span> e<span class="token punctuation">.</span>velocity<span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">"end_of_track"</span><span class="token punctuation">:</span> <span class="token keyword">lambda</span> e<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">11</span> <span class="token operator">*</span> <span class="token number">256</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span>
            <span class="token comment"># If the events have the same tick, and both events have types</span>
            <span class="token comment"># which appear in the secondary_sort dictionary, use the dictionary</span>
            <span class="token comment"># to determine their ordering.</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>
                event1<span class="token punctuation">.</span>time <span class="token operator">==</span> event2<span class="token punctuation">.</span>time
                <span class="token keyword">and</span> event1<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token keyword">in</span> secondary_sort
                <span class="token keyword">and</span> event2<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token keyword">in</span> secondary_sort
            <span class="token punctuation">)</span><span class="token punctuation">:</span>
                <span class="token keyword">return</span> <span class="token punctuation">(</span>secondary_sort<span class="token punctuation">[</span>event1<span class="token punctuation">.</span><span class="token builtin">type</span><span class="token punctuation">]</span><span class="token punctuation">(</span>event1<span class="token punctuation">)</span> <span class="token operator">-</span> 
                        secondary_sort<span class="token punctuation">[</span>event2<span class="token punctuation">.</span><span class="token builtin">type</span><span class="token punctuation">]</span><span class="token punctuation">(</span>event2<span class="token punctuation">)</span><span class="token punctuation">)</span>

            <span class="token comment"># Otherwise, just return the difference of their ticks.</span>
            <span class="token keyword">return</span> event1<span class="token punctuation">.</span>time <span class="token operator">-</span> event2<span class="token punctuation">.</span>time

        <span class="token comment"># Create track 0 with timing information</span>
        timing_track <span class="token operator">=</span> mido<span class="token punctuation">.</span>MidiTrack<span class="token punctuation">(</span><span class="token punctuation">)</span>

        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>  <span class="token comment"># Code processing the timing_track</span>

        <span class="token comment"># Sort the (absolute-tick-timed) events.</span>
        timing_track<span class="token punctuation">.</span>sort<span class="token punctuation">(</span>key<span class="token operator">=</span>functools<span class="token punctuation">.</span>cmp_to_key<span class="token punctuation">(</span>event_compare<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># &lt;--- (2)</span>

        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
</code></pre>
<p>The <code>event_compare</code> function (pointed in comment (1)) provides a specific comparison logic for MIDI events, ensuring they are sorted correctly by their tick time and type, along with additional criteria for certain event types.
Then, <code>functools.cmp_to_key</code> is used to convert this comparison function into a key function ( (pointed in comment (2))).</p>
<h2 id="should-you-use-it" tabindex="-1"><a class="header-anchor" href="#should-you-use-it">Should You Use It</a></h2>
<p>As <code>cmp_to_key</code> was aimed to help transition from Python 2 style <code>cmp</code> functions to <code>key</code> functions, its usage is not very common in newer projects. However, it can come in handy when we already have a comparison function defined in our code, to compare two objects and need to sort an iterable of these objects. In such a case, cmp_to_key will allow us to reuse our comparison logic (defined in our comparison function) without defining a new key function.</p>
<h2 id="behind-the-scenes" tabindex="-1"><a class="header-anchor" href="#behind-the-scenes">Behind the Scenes</a></h2>
<p>Below is the <a href="https://github.com/python/cpython/blob/87cd20a567aca56369010689e22a524bc1f1ac03/Lib/functools.py#L207">source code of <code>cmp_to_key</code></a> from the functools module</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">cmp_to_key</span><span class="token punctuation">(</span>mycmp<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Convert a cmp= function into a key= function"""</span>
    <span class="token keyword">class</span> <span class="token class-name">K</span><span class="token punctuation">(</span><span class="token builtin">object</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        __slots__ <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'obj'</span><span class="token punctuation">]</span>
        <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> obj<span class="token punctuation">)</span><span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>obj <span class="token operator">=</span> obj
        <span class="token keyword">def</span> <span class="token function">__lt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> mycmp<span class="token punctuation">(</span>self<span class="token punctuation">.</span>obj<span class="token punctuation">,</span> other<span class="token punctuation">.</span>obj<span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">0</span>
        <span class="token keyword">def</span> <span class="token function">__gt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> mycmp<span class="token punctuation">(</span>self<span class="token punctuation">.</span>obj<span class="token punctuation">,</span> other<span class="token punctuation">.</span>obj<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span>
        <span class="token keyword">def</span> <span class="token function">__eq__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> mycmp<span class="token punctuation">(</span>self<span class="token punctuation">.</span>obj<span class="token punctuation">,</span> other<span class="token punctuation">.</span>obj<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span>
        <span class="token keyword">def</span> <span class="token function">__le__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> mycmp<span class="token punctuation">(</span>self<span class="token punctuation">.</span>obj<span class="token punctuation">,</span> other<span class="token punctuation">.</span>obj<span class="token punctuation">)</span> <span class="token operator">&lt;=</span> <span class="token number">0</span>
        <span class="token keyword">def</span> <span class="token function">__ge__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> mycmp<span class="token punctuation">(</span>self<span class="token punctuation">.</span>obj<span class="token punctuation">,</span> other<span class="token punctuation">.</span>obj<span class="token punctuation">)</span> <span class="token operator">>=</span> <span class="token number">0</span>
        __hash__ <span class="token operator">=</span> <span class="token boolean">None</span>
    <span class="token keyword">return</span> K

</code></pre>
<p><code>cmp_to_key</code> intelligently maps the original comparison function to a key function suitable for sorting, by implementing all the comparsion dunders.</p>
<p>It returns a new callable (class K) with all the comparison dunders which will be called during the sorting process to calculate the sort key. Since the sorting process compare each of the calculated keys, the comparison dunders will get invoked, and they will return based on the result from the original comparison function.</p>
<p>For example, during sorting, when <code>K object</code> &lt; <code>another K object</code> needs to be checked, Python will actually call the <code>lt</code> dunder method on K. This method simply calls the original cmp function to compare the <code>obj</code> attributes of the two K instances.</p>
<hr>
<p>I share interesting Python snippets 🐍 like this from open-source projects illustrating Python language features in my newsletter, “Python in the Wild”.</p>
<p>Subscribe to the newsletter on <a href="https://adarshd.substack.com/">Substack</a> or <a href="https://www.linkedin.com/newsletters/python-in-the-wild-7155981512197181440/">Linkedin</a> to receive new Pythonic posts to your email 💌🚀.</p>
]]></content>
  </entry>
  <entry>
    <title>Partition method of Python strings</title>
    <link href="https://blog.adarshd.dev/posts/partition-method-of-python-str/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/partition-method-of-python-str/</id>
    <published>2024-01-22T11:25:00Z</published>
    <updated>2024-01-22T11:25:00Z</updated>
    <content type="html"><![CDATA[<p>The partition method of str covers a niche use case - It is useful when a string needs to be split into exactly two parts based on a separator. It returns a three-membered tuple containing the part before, the separator itself, and the part after the separator.
The split method is commonly used for splitting strings, which returns a list whose length can change based on the number of times the separator is present.</p>
<pre class="language-python"><code class="language-python">host <span class="token operator">=</span> <span class="token string">"localhost:8000"</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>host<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># Output: ['localhost', '8000']</span>

host <span class="token operator">=</span> <span class="token string">"localhost"</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>host<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># Output:['localhost']</span>

host <span class="token operator">=</span> <span class="token string">""</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>host<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># Output: ['']</span>

</code></pre>
<p><code>partition</code> is helpful when we need to get precisely two parts from a string using a separator.</p>
<pre class="language-python"><code class="language-python">server_name <span class="token operator">=</span> <span class="token string">"localhost:5000"</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>server_name<span class="token punctuation">.</span>partition<span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># Output: ('localhost', ':', '5000')</span>

server_name <span class="token operator">=</span> <span class="token string">"localhost"</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>server_name<span class="token punctuation">.</span>partition<span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># Output: ('localhost', '', '')</span>

server_name <span class="token operator">=</span> <span class="token string">""</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>server_name<span class="token punctuation">.</span>partition<span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># Output: ('', '', '')</span>
</code></pre>
<p>Using <code>partition</code> instead of <code>split</code> saves us from additional checks on the result’s length when exactly two parts are required.</p>
<p>Flask uses the <code>partition</code> method to determine the host and port to use when running the Flask app. You can see the <a href="https://github.com/pallets/flask/blob/7b5e176d1ab23e183d0403573358299ed6562dce/src/flask/app.py#L588">source code here</a>.</p>
<p>A simplified version of the code is added as below:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">run</span><span class="token punctuation">(</span>
    self<span class="token punctuation">,</span>
    host<span class="token punctuation">:</span> <span class="token builtin">str</span> <span class="token operator">|</span> <span class="token boolean">None</span> <span class="token operator">=</span> <span class="token boolean">None</span><span class="token punctuation">,</span>
    port<span class="token punctuation">:</span> <span class="token builtin">int</span> <span class="token operator">|</span> <span class="token boolean">None</span> <span class="token operator">=</span> <span class="token boolean">None</span><span class="token punctuation">,</span>
    debug<span class="token punctuation">:</span> <span class="token builtin">bool</span> <span class="token operator">|</span> <span class="token boolean">None</span> <span class="token operator">=</span> <span class="token boolean">None</span><span class="token punctuation">,</span>
    load_dotenv<span class="token punctuation">:</span> <span class="token builtin">bool</span> <span class="token operator">=</span> <span class="token boolean">True</span><span class="token punctuation">,</span>
    <span class="token operator">**</span>options<span class="token punctuation">:</span> t<span class="token punctuation">.</span>Any<span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

    server_name <span class="token operator">=</span> self<span class="token punctuation">.</span>config<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"SERVER_NAME"</span><span class="token punctuation">)</span>
    sn_host <span class="token operator">=</span> sn_port <span class="token operator">=</span> <span class="token boolean">None</span>

    <span class="token keyword">if</span> server_name<span class="token punctuation">:</span>
        sn_host<span class="token punctuation">,</span> _<span class="token punctuation">,</span> sn_port <span class="token operator">=</span> server_name<span class="token punctuation">.</span>partition<span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span>
        <span class="token comment"># The `partition` method will always return a three-membered tuple</span>

    <span class="token keyword">if</span> <span class="token keyword">not</span> host<span class="token punctuation">:</span>
        <span class="token keyword">if</span> sn_host<span class="token punctuation">:</span>
            host <span class="token operator">=</span> sn_host
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            host <span class="token operator">=</span> <span class="token string">"127.0.0.1"</span>

    <span class="token keyword">if</span> port <span class="token keyword">or</span> port <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span>
        port <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span>
    <span class="token keyword">elif</span> sn_port<span class="token punctuation">:</span>
        port <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>sn_port<span class="token punctuation">)</span>
    <span class="token keyword">else</span><span class="token punctuation">:</span>
        port <span class="token operator">=</span> <span class="token number">5000</span>
</code></pre>
<p>In the example above, the <code>server_name</code> string is partitioned into host and port values.</p>
<hr>
<p>I share interesting Python snippets 🐍 like this from open-source projects illustrating Python language features. Subscribe to <a href="https://adarshd.substack.com/">my newsletter</a> to receive new posts to your email 💌🚀.</p>
]]></content>
  </entry>
  <entry>
    <title>List from Typing module is inheritable</title>
    <link href="https://blog.adarshd.dev/posts/til-list-from-typing-module-is-inheritable/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/til-list-from-typing-module-is-inheritable/</id>
    <published>2023-12-20T06:25:00Z</published>
    <updated>2023-12-20T06:25:00Z</updated>
    <content type="html"><![CDATA[<blockquote>
<p>TIL - “Today I learned” series of Posts are short notes of new things I encounter with Python.</p>
</blockquote>
<p>While browsing the source of SQLAlchemy, I came across a <a href="https://github.com/sqlalchemy/sqlalchemy/blob/b12b7b120559d07a6f24fb2d6d29c7049084b4a5/lib/sqlalchemy/ext/orderinglist.py#L231">custom list class</a></p>
<p>The custom class is created by inheriting from <code>List</code> from <code>typing</code>, rather than the builtin <code>list</code> or the <code>UserList</code> class provided by <a href="https://docs.python.org/3/library/collections.html#userlist-objects">collections</a></p>
<p>It works.</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> List


<span class="token keyword">class</span> <span class="token class-name">CustomList</span><span class="token punctuation">(</span>List<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">pass</span>


<span class="token keyword">print</span><span class="token punctuation">(</span>CustomList<span class="token punctuation">.</span>__mro__<span class="token punctuation">)</span>

<span class="token comment"># (&lt;class '__main__.CustomList'>, &lt;class 'list'>, &lt;class 'typing.Generic'>, &lt;class 'object'>)</span>
</code></pre>
<p>The builtin <code>list</code> can be seen as a parent class of CustomList.</p>
<p>Normal list operations also work as expected.</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> List


<span class="token keyword">class</span> <span class="token class-name">CustomList</span><span class="token punctuation">(</span>List<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">pass</span>


c <span class="token operator">=</span> CustomList<span class="token punctuation">(</span><span class="token punctuation">)</span>
c<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token string">"foo"</span><span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span>

<span class="token comment"># ['foo']</span>
</code></pre>
<p>Now, what if we try to instantiate <code>List</code> directly?</p>
<pre class="language-python"><code class="language-python">list_obj1 <span class="token operator">=</span> <span class="token builtin">list</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment"># Works</span>
list_obj2 <span class="token operator">=</span> List<span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment"># TypeError: Type List cannot be instantiated; use list() instead</span>
</code></pre>
<p>Let’s see why this happens.</p>
<p>The <code>List</code> class is defined in typing as an alias of the <code>list</code> class.</p>
<pre class="language-python"><code class="language-python"><span class="token comment"># CPython - typing.py (https://github.com/python/cpython/blob/4afa7be32da32fac2a2bcde4b881db174e81240c/Lib/typing.py#L2609)</span>

List <span class="token operator">=</span> _alias<span class="token punctuation">(</span><span class="token builtin">list</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> inst<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">,</span> name<span class="token operator">=</span><span class="token string">'List'</span><span class="token punctuation">)</span>
</code></pre>
<p>This <code>_alias</code> creates <code>List</code> as a child instance of <code>_BaseGenericAlias</code>. See that the <code>inst</code> attribute is set to False above.</p>
<p>Now, let’s take a look at the <code>_BaseGenericAlias</code> class:</p>
<pre class="language-python"><code class="language-python"><span class="token comment"># CPython - typing.py (https://github.com/python/cpython/blob/4afa7be32da32fac2a2bcde4b881db174e81240c/Lib/typing.py#L1107)</span>

<span class="token keyword">class</span> <span class="token class-name">_BaseGenericAlias</span><span class="token punctuation">(</span>_Final<span class="token punctuation">,</span> _root<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""The central part of internal API.

    This represents a generic version of type 'origin' with type arguments 'params'.
    There are two kind of these aliases: user defined and special. The special ones
    are wrappers around builtin collections and ABCs in collections.abc. These must
    have 'name' always set. If 'inst' is False, then the alias can't be instantiated,
    this is used by e.g. typing.List and typing.Dict.
    """</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> origin<span class="token punctuation">,</span> <span class="token operator">*</span><span class="token punctuation">,</span> inst<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">,</span> name<span class="token operator">=</span><span class="token boolean">None</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>_inst <span class="token operator">=</span> inst
        self<span class="token punctuation">.</span>_name <span class="token operator">=</span> name
        self<span class="token punctuation">.</span>__origin__ <span class="token operator">=</span> origin
        self<span class="token punctuation">.</span>__slots__ <span class="token operator">=</span> <span class="token boolean">None</span>  <span class="token comment"># This is not documented.</span>

    <span class="token keyword">def</span> <span class="token function">__call__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">**</span>kwargs<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">if</span> <span class="token keyword">not</span> self<span class="token punctuation">.</span>_inst<span class="token punctuation">:</span>
            <span class="token keyword">raise</span> TypeError<span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Type </span><span class="token interpolation"><span class="token punctuation">{</span>self<span class="token punctuation">.</span>_name<span class="token punctuation">}</span></span><span class="token string"> cannot be instantiated; "</span></span>
                            <span class="token string-interpolation"><span class="token string">f"use </span><span class="token interpolation"><span class="token punctuation">{</span>self<span class="token punctuation">.</span>__origin__<span class="token punctuation">.</span>__name__<span class="token punctuation">}</span></span><span class="token string">() instead"</span></span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
</code></pre>
<p>The call dunder is overridden to raise a type error if the <code>_inst</code> attribute of the alias is False. The <code>__call__</code> function gets invoked when we call the children of the <code>_BaseGenericAlias</code> class, in our case, the <code>List</code>.</p>
<p>As you can see in the docstring of <code>_BaseGenericAlias</code>:</p>
<blockquote>
<p>If ‘inst’ is False, then the alias can’t be instantiated, this is used by e.g. typing.List and typing.Dict.</p>
</blockquote>
<p>Summarizing,</p>
<ul>
<li><code>typing.List</code> behaves like a <code>list</code> when used for inheritance</li>
<li>But can’t be used directly, just like the <code>list</code> for instantiation.</li>
</ul>
]]></content>
  </entry>
  <entry>
    <title>When I met Guido van Rossum</title>
    <link href="https://blog.adarshd.dev/posts/when-i-met-guido-van-rossum/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/when-i-met-guido-van-rossum/</id>
    <published>2023-10-05T12:30:00Z</published>
    <updated>2023-10-05T12:30:00Z</updated>
    <content type="html"><![CDATA[<p><img src="/assets/img/posts/when-i-met-guido-van-rossum/with_guido.jpg" alt="With Guido" :="" w="400"></p>
<p>This is my account of meeting Guido Van Rossum and his advice on becoming a Python Core Developer.</p>
<p>This interaction is a few months old and happened during Pycascades in Vancouver (18-19 March 2023). Yesterday a random thought about this interaction crossed my mind and I thought I should write a blog post on this.</p>
<h2 id="pycascades-python-conferences" tabindex="-1"><a class="header-anchor" href="#pycascades-python-conferences">Pycascades &amp; Python Conferences</a></h2>
<p>I flew from India to Vancouver to Speak at Pycascades to speak on “Metaprogramming in Python using Metaclasses”</p>
<details>
<summary>My Talk</summary>
<p>If you are interested in the talk, take this digression.</p>
<p>Slides: <a href="https://speakerdeck.com/adarshd/metaprogramming-in-python-using-metaclasses">https://speakerdeck.com/adarshd/metaprogramming-in-python-using-metaclasses</a></p>
<p>Talk Video: <a href="https://www.youtube.com/watch?v=-js0K7Q878c">https://www.youtube.com/watch?v=-js0K7Q878c</a></p>
<p>Pardon my lack of preparation, I was busy exploring Vancouver the previous day.</p>
</details>
<hr>
<p>I would broadly classify tech conferences into two types: Busy vs. Relaxed conferences.</p>
<p>A typical busy conference will have a large number of attendees, multiple parallel tracks, and a whole lot of things going around at the same time. Relaxed conferences have a maximum of a few hundred attendees, there is only a single track and everything occurs at a relaxed pace.</p>
<p>I would classify Pycascades '23 as a relaxed conference. In a typical busy conference, you will have to line up to meet your favorite tech rockstar. But here that was not the case. This was the first “relaxed” conference I have been to and was a bit surprising.</p>
<h2 id="meeting-guido" tabindex="-1"><a class="header-anchor" href="#meeting-guido">Meeting Guido</a></h2>
<p>I had my presentation at 3 PM on day 1 of the conference. I mostly spend my day in the speaker-ready room doing some last-minute preparations. After completing my talk I went on to get some snacks during the evening break.</p>
<p>There he was. I did a quick Google image search and to double-check, I even asked a volunteer to confirm if that was Guido himself.</p>
<p>I don’t exactly remember when I heard about Guido for the first time. This might be from my high school where our computer teacher instilled in us a love for programming or in the first semester of my Bachelors where we had an introductory course in Python.</p>
<p>I mustered up enough courage to talk to him and thanks to Pycascades being a relaxed conference, there was no one around waiting to talk to him. He told me that he listened to the last few slides of my talk and wanted to know how I got into metaprogramming.</p>
<p>This was a part of his character. He always asks ‘Why’. Before answering a question, he wanted to know the motive or the origin of the question. I guess this curiosity is one factor that sets apart great developers from good developers.</p>
<hr>
<p>Here is a brief of the conversation we had on that day(reworded and mostly taken from my memory).</p>
<p>Guido: I missed the initial parts of your talk. I came when you reached slide X(a slide towards the end). How did you get into the idea of metaprogramming?</p>
<p>Me: We have a knowledge-sharing session in our company. I presented an initial version of this talk there. Also, I love teaching others and I explore new topics from Realpython and blogs.</p>
<p>Guido: At your day job, what are you currently working on? Are you using Python?</p>
<p>Me: Yes. I am building a GraphQL API powering the backend of a travel application.</p>
<p>Guido: GraphQL? Is that the Facebook library?</p>
<p>Me: Yes. We are using the Graphene-Python library.</p>
<p>Guido: I see. So there are Python wrappers around it.</p>
<details>
<summary>Python is vast</summary>
I met a lot of experts at the conference and everyone has their areas of expertise. Also, I had a similar discussion with Sarah Kaiser from Microsoft where she talked about the vastness of Python. There are experts in Web (API devs), PyData, ML, etc. and in most cases, everyone has an exclusive area of expertise. From my conversation with Guido, I was surprised to see that he was unaware of GraphQL libraries, but considering the fact that his expertise is in core development, this is nothing surprising.
</details>
<hr>
<p>Then I asked him a few questions which came to my mind:</p>
<p>Q1: I asked a question regarding alternate Python implementations. Something along the lines of “Do you see Pypy overtaking CPython anytime in the future?”</p>
<p>Guido: PyPy or any other implementations won’t be replacing CPython. Alternate implementations are always trying to catch up with the CPython releases.
With the Faster CPython Project, future releases have improved performance reducing the need for alternative implementations, if speed is the concern.
Jit compiler</p>
<p>Q2: Will you be coming to India for our Pycon event (Pycon India)</p>
<p>Guido: I most probably won’t be there since I am not able to travel that long. (I don’t exactly remember the wording)</p>
<p>Q3: I work in a young team of Backend devs using Python. Do you have any advice for getting better at Python Programming?</p>
<p>(I only remember a part of this answer)</p>
<p>Guido:</p>
<ol>
<li>Be a part of local Python communities</li>
<li>Read more code than you write</li>
</ol>
<p>Expanding on point 2, reading good code is a great way to learn new patterns and expand our thinking approaches towards problems.</p>
<p>I recently came across “death and gravity” blog with two great resources for the same.</p>
<p><a href="https://death.andgravity.com/stdlib">Learn by reading code: Python standard library design decisions explained</a> and <a href="https://death.andgravity.com/aosa">Struggling to structure code in larger programs? Great resources a beginner might not find so easily</a></p>
<hr>
<p>I stopped when I ran out of questions to ask.</p>
<p>Initially I had told him that I love teaching. He asked me to connect with the RealPython team (who were present at the conference) and collaborate with them on some tutorials.</p>
<p>Then we took a selfie (The cover image of this post). Also, I was surprised no one else was doing the same (relaxed conference!).</p>
<p>I noted down important points of the conversation to share with my teammates, which became useful when writing this.</p>
<h2 id="how-to-be-a-cpython-core-developer" tabindex="-1"><a class="header-anchor" href="#how-to-be-a-cpython-core-developer">How to be a CPython Core Developer</a></h2>
<p>On day 2, the final day of the conference, I arrived at the venue (SFU Vancouver campus) with a resolve not to miss any of the talks like I did yesterday owing to my talk preparation.</p>
<p>On the morning break, I saw Guido talking to a volunteer regarding Pycon. I was ready with my new set of questions.</p>
<p>I was looking to advance my Python knowledge and was looking to go deeper into CPython implementation. I had my copy of <a href="https://realpython.com/products/cpython-internals-book/">Cpython Internal Book</a> and have taken a peek into the <a href="https://devguide.python.org/">Python Devguide</a> and wanted to get an idea of the next steps to take from the creator himself.</p>
<hr>
<p>If you are new to the below terms, please refer to the listed resources</p>
<details>
<summary>What is CPython</summary>
<p>Read this guide: <a href="https://realpython.com/cpython-source-code-guide/">https://realpython.com/cpython-source-code-guide/</a> by Realpython team</p>
</details>
<details>
<summary>Who are CPython core developers</summary>
<p>Talk by Mariatta: <a href="https://speakerdeck.com/mariatta/what-is-a-python-core-developer">https://speakerdeck.com/mariatta/what-is-a-python-core-developer</a> and Devguide page: <a href="https://devguide.python.org/core-developers/become-core-developer/">https://devguide.python.org/core-developers/become-core-developer/</a></p>
</details>
<hr>
<p>I went ahead and asked Guido, “How can I become a core developer”?</p>
<p>The hall was a bit crowded and noisy at that time. Guido insisted we move to a quieter side and talk. He grabbed a snack, went to a quiet corner overlooking the Vancouver harbor and asked:</p>
<p>Why?</p>
<p>Why do you want to be a core dev? Becoming one is a decision many would regret.</p>
<p>I didn’t quite understand this at that time. It made sense recently when I was reading the BDFL retirement mail thread from the Python mailing list archive at 3 AM. Also see this article from Brett Cannon: <a href="https://snarky.ca/the-social-contract-of-open-source/">The social contract of Open source</a></p>
<p>Going back to the topic:</p>
<p>I somehow evaded his whys and got the answer from him.</p>
<p>Here are some of the points he mentioned as a part of his answer:</p>
<ul>
<li>Be active in discussion forums.</li>
</ul>
<p><a href="https://www.python.org/community/lists/">Python mailing lists</a></p>
<p><a href="https://discuss.python.org/">Python Discourse</a></p>
<ul>
<li>Look for issues or new features to be added to the standard library</li>
</ul>
<p>Since I was already using Python, he asked me to identify bugs/improvements and try to work on those.</p>
<blockquote class="prompt-tip">
<p>Fun fact: Currently, more than 63% of <a href="https://github.com/python/cpython/tree/3.12">CPython source</a> is written in Python. Knowledge of C is not required to contribute to CPython.</p>
</blockquote>
<ul>
<li>There are little bugs everywhere
He said that there is a lack of expert contributors. Even core developers won’t know all parts of the code and there are parts everyone is afraid of.</li>
</ul>
<p>I was happy and relieved hearing this (There are little bugs everywhere, even in CPython). Maybe because I can justify the bugs I create using this statement.</p>
<ul>
<li>Never give up</li>
</ul>
<p>Once started, we should go on taking up issues and continue contributing.</p>
<ul>
<li>Multiple Ways to Contribute:</li>
</ul>
<p>Along with contributing code, we can also take part in discussions, writing docs, or reviewing PRs.</p>
<hr>
<p>Then came the surprising move. He wanted to check how ChatGPT would answer this (no I am not making this up). He pulled in his laptop, logged in to ChatGPT, and asked the same question.</p>
<p>I do not remember exactly the answer it gave, I remember it had a link to the dev guide and some other instructions. He found out that there was a mistake in the answer and reported that and ChatGPT responded with a revised(and correct) answer.</p>
<h2 id="lessons-learnt" tabindex="-1"><a class="header-anchor" href="#lessons-learnt">Lessons Learnt</a></h2>
<ul>
<li>Go to conferences:</li>
</ul>
<p>Pycascades was a very enriching experience for me. I would recommend everyone to go to conferences. I connected &amp; exchanged ideas with many great folks (VSCode-Python Team, RealPython Team, Mariatta, and so many more awesome people)</p>
<ul>
<li>Be nice:</li>
</ul>
<p>There were common traits I observed whenever I spoke with most of the experts during relaxed as well as busy conferences.</p>
<p>They all are humble, welcoming, willing to help and genuinely care about others.</p>
<hr>
<hr>
<p>Looking back on this journey, everything was worth it - the 16-hour long flight, taking a week-long leave from work (and handling the production credentials to juniors)</p>
<h2 id="thanks-to-pysacsades" tabindex="-1"><a class="header-anchor" href="#thanks-to-pysacsades">Thanks to Pysacsades</a></h2>
<p>It isn’t fair to end this post without thanking <a href="https://2023.pycascades.com/about/team/">Pycascades Team</a> for giving me a speaker invite and covering a part of the travel expenses.</p>
<p>Special thanks to Eliza, Ben, Jolene, and the entire Pycascades team for running this awesome conference.</p>
]]></content>
  </entry>
  <entry>
    <title>Thread Safety in Python</title>
    <link href="https://blog.adarshd.dev/posts/thread-safety-in-python/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/thread-safety-in-python/</id>
    <published>2023-09-25T13:30:11Z</published>
    <updated>2023-09-25T13:30:11Z</updated>
    <content type="html"><![CDATA[<h1>Thread Safety in Python</h1>
<p>Before getting started with Thread safety, let’s look into race conditions.</p>
<h2 id="race-conditions" tabindex="-1"><a class="header-anchor" href="#race-conditions">Race Conditions</a></h2>
<p>From Wikipedia:</p>
<blockquote>
<p>A race condition or race hazard is the condition of an electronics, software, or other system where the system’s substantive behavior is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when one or more of the possible behaviors is undesirable.</p>
</blockquote>
<blockquote>
<p>Race conditions can occur especially in logic circuits, multithreaded, or distributed software programs.</p>
</blockquote>
<p>For an example of a race condition on a real-world web app, see this disclosed security bug: <a href="https://hackerone.com/reports/759247">https://hackerone.com/reports/759247</a></p>
<p>Here the hacker could redeem a gift card (intended for single use) multiple times.</p>
<p>A developer may think of implementing a gift card redemption flow like below:</p>
<ol>
<li>Get a gift card code from the user</li>
<li>Validate the code: Check if it exists in DB, is not expired, and is not used.</li>
<li>If validation succeeds, add the gift card value to the user’s wallet and mark the gift card as used in the DB.</li>
</ol>
<p>Think of the case where a user sends multiple requests at the same time. All parallel requests arriving before the execution of step 3, could succeed and can result in adding the gift card amount to the user’s wallet once per request. This might have happened in the case of the above bug.</p>
<h2 id="threading-in-python" tabindex="-1"><a class="header-anchor" href="#threading-in-python">Threading in Python</a></h2>
<p>Threading is a method to implement concurrency in Python programs. Due to the presence of CPython’s GIL, threading is helpful in the case of I/O Bound programs.</p>
<p>threading module and concurrent.future’s ThreadPoolExecutor helper is commonly used to implement multithreading in Python.</p>
<p>Let’s dive into some Python examples after exploring the levels of thread safety.</p>
<h3 id="levels-of-thread-safety" tabindex="-1"><a class="header-anchor" href="#levels-of-thread-safety">Levels of thread safety</a></h3>
<p>From Wikipedia, levels of thread safety:</p>
<blockquote>
<p>Thread safe: Implementation is guaranteed to be free of race conditions when accessed by multiple threads simultaneously.</p>
</blockquote>
<blockquote>
<p>Conditionally safe: Different threads can access different objects simultaneously, and access to shared data is protected from race conditions.</p>
</blockquote>
<blockquote>
<p>Not thread-safe: Data structures should not be accessed simultaneously by different threads.</p>
</blockquote>
<h3 id="race-conditions-in-python-threaded-programs" tabindex="-1"><a class="header-anchor" href="#race-conditions-in-python-threaded-programs">Race conditions in Python Threaded Programs</a></h3>
<h4>Example 1: Incrementing a counter</h4>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> concurrent<span class="token punctuation">.</span>futures

count <span class="token operator">=</span> <span class="token number">0</span>


<span class="token keyword">def</span> <span class="token function">increase_counter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">global</span> count
    <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        count <span class="token operator">+=</span> <span class="token number">1</span>

<span class="token keyword">with</span> concurrent<span class="token punctuation">.</span>futures<span class="token punctuation">.</span>ThreadPoolExecutor<span class="token punctuation">(</span>max_workers<span class="token operator">=</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token keyword">as</span> executor<span class="token punctuation">:</span>
    <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">10_000</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        executor<span class="token punctuation">.</span>submit<span class="token punctuation">(</span>increase_counter<span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">F"</span><span class="token interpolation"><span class="token punctuation">{</span>count<span class="token operator">=</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
</code></pre>
<p>The expected value of the count is 10,000 * 1000.</p>
<p>Running with Python 3.9, the program output varies per execution and gives outputs like below:</p>
<ol>
<li><code>count=2163149</code></li>
<li><code>count=5642376</code></li>
</ol>
<p>From this, we can conclude that the program is not thread-safe, since it gives unexpected results when run using multiple threads.</p>
<p>In the above examples, there are at most 10 parallel threads working since we have given <code>max_workers=10</code></p>
<p>On adjusting max_workers value,</p>
<p>When <code>max_workers=3</code>, the output varies like below:</p>
<ol>
<li><code>count=10000000</code></li>
<li><code>count=9999000</code></li>
</ol>
<p>On reducing max_workers, which is the number of threads running in parallel, there is a visible reduction in errors, and at times, the expected output is also returned.</p>
<p>Consider the above scenario where each thread reads a variable and increments its value with 1. Between reads and writes, context switches to different threads can happen.
With multiple threads, the following scenario may be possible:</p>
<ol>
<li>Thread_1 reads the value of count, gets 0</li>
<li>At the same time thread 2 reads count, and gets 0</li>
<li>Thread_1 sets count=count+1, as 1.</li>
<li>Thread_3 reads count, gets 1.</li>
<li>Thread_3 sets count=count+1, as 2</li>
<li>Thread_2 sets count=count+1, as 1 (since thread_2 holds a stale value of count, i.e., 0)</li>
</ol>
<p>This explains the counter value being far less than expected.</p>
<h4>Example 2: Singleton class in Python</h4>
<p>Let’s take an example of a Singleton class created using Python.
Singleton pattern is used to make sure that only one instance is created for a class. This can be used in cases where the class initialization performs some steps which should be performed exactly once. Singletons are useful in cases where there are restrictions on certain resource usage such as concurrent connection limits.</p>
<p>Below is a commonly used example for a Singleton class in Python:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">SingletonClass</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">__new__</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">if</span> <span class="token keyword">not</span> <span class="token builtin">hasattr</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> <span class="token string">'instance'</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
            cls<span class="token punctuation">.</span>instance <span class="token operator">=</span> <span class="token builtin">super</span><span class="token punctuation">(</span>SingletonClass<span class="token punctuation">,</span> cls<span class="token punctuation">)</span><span class="token punctuation">.</span>__new__<span class="token punctuation">(</span>cls<span class="token punctuation">)</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">.</span>instance


obj1 <span class="token operator">=</span> SingletonClass<span class="token punctuation">(</span><span class="token punctuation">)</span>
obj2 <span class="token operator">=</span> SingletonClass<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span>obj1 <span class="token keyword">is</span> obj2<span class="token punctuation">)</span> <span class="token comment"># True</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># 2402721138768</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token punctuation">(</span>obj2<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># 2402721138768</span>
</code></pre>
<p>In the above example, it is possible because two threads can evaluate the <code>if</code> check for instance (<code>if not hasattr(cls, 'instance')</code>) at near similar times and proceed to create two new objects.</p>
<h4>Example 3: The print function</h4>
<pre class="language-python"><code class="language-python"><span class="token keyword">from</span> concurrent <span class="token keyword">import</span> futures

<span class="token keyword">def</span> <span class="token function">printer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">'testing thread safety of print'</span><span class="token punctuation">)</span>


<span class="token keyword">with</span> futures<span class="token punctuation">.</span>ThreadPoolExecutor<span class="token punctuation">(</span>max_workers<span class="token operator">=</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token keyword">as</span> executor<span class="token punctuation">:</span>
    <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        executor<span class="token punctuation">.</span>submit<span class="token punctuation">(</span>printer<span class="token punctuation">)</span>
</code></pre>
<p>Output can vary from expected:</p>
<pre class="language-text"><code class="language-text">testing thread safety of print
testing thread safety of print
testing thread safety of printtesting thread safety of print
testing thread safety of print

testing thread safety of printtesting thread safety of print
testing thread safety of print
testing thread safety of print

testing thread safety of print
</code></pre>
<p>Before printing ending newlines, context switches happen sometimes printing output from another thread.</p>
<h2 id="making-programs-thread-safe" tabindex="-1"><a class="header-anchor" href="#making-programs-thread-safe">Making programs thread-safe</a></h2>
<h4>Synchronization primitives</h4>
<p>The below synchronization primitives can be used to make a program thread safe.</p>
<h5>Lock</h5>
<p>A lock can be used for exclusive access to a resource. Once a thread acquires a lock, no other threads can acquire it (and proceed further) unless the lock is released.</p>
<h5>RLock</h5>
<p>RLock is a re-entrant lock. It allows when a holding thread requests the lock again.
The simple lock is not aware of the current locking thread and this can lead to deadlocks.</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> threading

num <span class="token operator">=</span> <span class="token number">0</span>
lock <span class="token operator">=</span> threading<span class="token punctuation">.</span>Lock<span class="token punctuation">(</span><span class="token punctuation">)</span>

lock<span class="token punctuation">.</span>acquire<span class="token punctuation">(</span><span class="token punctuation">)</span>
num <span class="token operator">+=</span> <span class="token number">1</span>
lock<span class="token punctuation">.</span>acquire<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># This will block. The program goes into a deadlock after this.</span>
num <span class="token operator">+=</span> <span class="token number">2</span>
lock<span class="token punctuation">.</span>release<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<pre class="language-python"><code class="language-python">
<span class="token comment"># With RLock, that problem doesn't happen.</span>
lock <span class="token operator">=</span> threading<span class="token punctuation">.</span>RLock<span class="token punctuation">(</span><span class="token punctuation">)</span>

lock<span class="token punctuation">.</span>acquire<span class="token punctuation">(</span><span class="token punctuation">)</span>
num <span class="token operator">+=</span> <span class="token number">3</span>
lock<span class="token punctuation">.</span>acquire<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># This won't block.</span>
num <span class="token operator">+=</span> <span class="token number">4</span>
lock<span class="token punctuation">.</span>release<span class="token punctuation">(</span><span class="token punctuation">)</span>
lock<span class="token punctuation">.</span>release<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># We need to call release once for each call to acquire</span>

</code></pre>
<p>For brevity, let’s skip the other primitives (Semaphore &amp; Barrier, Events &amp; Conditions).</p>
<p>Lock acquisition and release can be managed using context managers.</p>
<pre class="language-python"><code class="language-python">lock <span class="token operator">=</span> threading<span class="token punctuation">.</span>Lock<span class="token punctuation">(</span><span class="token punctuation">)</span>

lock<span class="token punctuation">.</span>acquire<span class="token punctuation">(</span><span class="token punctuation">)</span>
num <span class="token operator">+=</span> <span class="token number">1</span>
lock<span class="token punctuation">.</span>release<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<p>is equivalent to:</p>
<pre class="language-python"><code class="language-python">lock <span class="token operator">=</span> threading<span class="token punctuation">.</span>Lock<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">with</span> lock<span class="token punctuation">:</span>
    num <span class="token operator">+=</span> <span class="token number">1</span>
</code></pre>
<h4>Thread safe counter implementation</h4>
<p>We can use a lock at places of code where race conditions(simultaneous modifications in this case) should be avoided.</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> concurrent<span class="token punctuation">.</span>futures
<span class="token keyword">from</span> threading <span class="token keyword">import</span> Lock

count <span class="token operator">=</span> <span class="token number">0</span>
lock <span class="token operator">=</span> Lock<span class="token punctuation">(</span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">increase_counter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">global</span> count
    <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">:</span>

        lock<span class="token punctuation">.</span>acquire<span class="token punctuation">(</span><span class="token punctuation">)</span>
        count <span class="token operator">+=</span> <span class="token number">1</span>
        lock<span class="token punctuation">.</span>release<span class="token punctuation">(</span><span class="token punctuation">)</span>

        <span class="token triple-quoted-string string">"""
        or:
        with lock:
            count += 1
        """</span>


<span class="token keyword">with</span> concurrent<span class="token punctuation">.</span>futures<span class="token punctuation">.</span>ThreadPoolExecutor<span class="token punctuation">(</span>max_workers<span class="token operator">=</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token keyword">as</span> executor<span class="token punctuation">:</span>
    <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">10_000</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        executor<span class="token punctuation">.</span>submit<span class="token punctuation">(</span>increase_counter<span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">F"</span><span class="token interpolation"><span class="token punctuation">{</span>count<span class="token operator">=</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token comment"># count=10000000, for all executions</span>
</code></pre>
<h4>Thread-safe singleton implementation</h4>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> threading


<span class="token keyword">class</span> <span class="token class-name">Singleton</span><span class="token punctuation">:</span>
  _instance <span class="token operator">=</span> <span class="token boolean">None</span>
  _lock <span class="token operator">=</span> threading<span class="token punctuation">.</span>Lock<span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword">def</span> <span class="token function">__new__</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span><span class="token punctuation">:</span>
      <span class="token keyword">if</span> cls<span class="token punctuation">.</span>_instance <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span>
        <span class="token keyword">with</span> cls<span class="token punctuation">.</span>_lock<span class="token punctuation">:</span>
          <span class="token keyword">if</span> cls<span class="token punctuation">.</span>_instance <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span>
              cls<span class="token punctuation">.</span>_instance <span class="token operator">=</span> <span class="token builtin">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__new__<span class="token punctuation">(</span>cls<span class="token punctuation">)</span>
          
      <span class="token keyword">return</span> cls<span class="token punctuation">.</span>_instance
</code></pre>
<p>After acquiring the lock (using <code>with</code>), we are rechecking the presence of an instance to prevent the below-race condition.</p>
<p>Consider two threads thread1 and thread2.</p>
<ol>
<li>Both execute <code>new</code> dunder of singleton class at the same time.</li>
<li><code>cls_.instance</code> is currently None, so both thread tries to acquire the lock</li>
<li>thread1 gets the lock while thread2 is blocked waiting.</li>
<li>thread1 creates the singleton instance and completes execution inside the context manager. Once it exits the <code>with block, the lock (</code>cls._lock`) is released.</li>
<li>Now, thread2 is unblocked and enters the context manager body by acquiring the lock. Since the None check for cls._instance fails, thread2 leaves execution and the already existing instance is returned.</li>
</ol>
<h4>Thread safe print implementation</h4>
<p>Similar to the previous examples, we can use a lock here.</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">from</span> concurrent <span class="token keyword">import</span> futures
<span class="token keyword">from</span> threading <span class="token keyword">import</span> Lock

lock <span class="token operator">=</span> Lock<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">printer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">with</span> lock<span class="token punctuation">:</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">'testing thread safety of print'</span><span class="token punctuation">)</span>


<span class="token keyword">with</span> futures<span class="token punctuation">.</span>ThreadPoolExecutor<span class="token punctuation">(</span>max_workers<span class="token operator">=</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token keyword">as</span> executor<span class="token punctuation">:</span>
    <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        executor<span class="token punctuation">.</span>submit<span class="token punctuation">(</span>printer<span class="token punctuation">)</span>

</code></pre>
<h2 id="context-switching-thread-switch-interval" tabindex="-1"><a class="header-anchor" href="#context-switching-thread-switch-interval">Context switching &amp; Thread switch interval</a></h2>
<p>The switch interval in Python specifies how long the Python interpreter will allow a Python thread to run, after which it is forced for a context switch.</p>
<p>Since only one thread runs at a time due to GIL, the interpreter tries to switch threads after this interval.</p>
<p>The default interval is 5 milliseconds.</p>
<pre class="language-text"><code class="language-text">&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys.getswitchinterval()
0.005
</code></pre>
<p>We can set custom thread switch interval using <a href="https://docs.python.org/3/library/sys.html#sys.setswitchinterval">sys.setswitchinterval</a></p>
<p>If the threads are waiting (e.g.: for IO operations) this switching allows for a parallel execution-like effect. If threads are running any CPU-bound operations, this context switch causes a performance penalty.</p>
<p>Eg: Network IO blocked tasks:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> time

<span class="token keyword">import</span> requests

start <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>
req1 <span class="token operator">=</span> requests<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"https://reqres.in/api/users?delay=3"</span><span class="token punctuation">)</span>
req2 <span class="token operator">=</span> requests<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"https://reqres.in/api/users?delay=3"</span><span class="token punctuation">)</span>
end <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span>end<span class="token operator">-</span>start<span class="token punctuation">,</span> <span class="token string">" seconds"</span><span class="token punctuation">)</span> <span class="token comment"># 6.9710999 seconds</span>
</code></pre>
<p>Using threads:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> threading
<span class="token keyword">import</span> time
<span class="token keyword">import</span> requests


<span class="token keyword">def</span> <span class="token function">make_request</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> requests<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"https://reqres.in/api/users?delay=3"</span><span class="token punctuation">)</span>


start <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>

t1 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>make_request<span class="token punctuation">)</span>
t2 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>make_request<span class="token punctuation">)</span>

t1<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>
t2<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

t1<span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>
t2<span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>

end <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span>end<span class="token operator">-</span>start<span class="token punctuation">,</span> <span class="token string">" seconds"</span><span class="token punctuation">)</span> <span class="token comment"># 3.494618 seconds</span>
</code></pre>
<p>In case of CPU Blocked tasks:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> time

start <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>

s1 <span class="token operator">=</span> <span class="token builtin">sum</span><span class="token punctuation">(</span><span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">100000000</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
s2 <span class="token operator">=</span> <span class="token builtin">sum</span><span class="token punctuation">(</span><span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">100000000</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

end <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span>end<span class="token operator">-</span>start<span class="token punctuation">,</span> <span class="token string">" seconds"</span><span class="token punctuation">)</span> <span class="token comment"># 3.8891145 seconds</span>
</code></pre>
<p>Using threads:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> threading
<span class="token keyword">import</span> time


<span class="token keyword">def</span> <span class="token function">perform_computation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> 
    <span class="token keyword">return</span> <span class="token builtin">sum</span><span class="token punctuation">(</span><span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">100000000</span><span class="token punctuation">)</span><span class="token punctuation">)</span>


start <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>

t1 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>perform_computation<span class="token punctuation">)</span>
t2 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>perform_computation<span class="token punctuation">)</span>

t1<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>
t2<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

t1<span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>
t2<span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>

end <span class="token operator">=</span> time<span class="token punctuation">.</span>perf_counter<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span>end<span class="token operator">-</span>start<span class="token punctuation">,</span> <span class="token string">" seconds"</span><span class="token punctuation">)</span> <span class="token comment"># 3.9622078999999997 seconds</span>
</code></pre>
<p>As expected, the use of threading slowed down this CPU-bound operation.</p>
<h2 id="revisiting-the-increment_counter-example" tabindex="-1"><a class="header-anchor" href="#revisiting-the-increment_counter-example">Revisiting the increment_counter example</a></h2>
<p>On running the non-thread-safe increment_counter() used above using Python 3.11 always gives the expected result and race conditions are not seen.</p>
<p>We can use dis module to inspect the Python bytecode for the increase_counter function</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> dis

count <span class="token operator">=</span> <span class="token number">0</span>


<span class="token keyword">def</span> <span class="token function">increase_counter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">global</span> count
    <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        count <span class="token operator">+=</span> <span class="token number">1</span>

dis<span class="token punctuation">.</span>dis<span class="token punctuation">(</span>increase_counter<span class="token punctuation">)</span>
</code></pre>
<p>Python 3.9 output:</p>
<pre class="language-text"><code class="language-text">9 0 LOAD_GLOBAL 0 (range)
2 LOAD_CONST 1 (1000)
4 CALL_FUNCTION 1
6 GET_ITER
&gt;&gt; 8 FOR_ITER 12 (to 22)
10 STORE_FAST 0 (i)

10 12 LOAD_GLOBAL 1 (count)
14 LOAD_CONST 2 (1)
16 INPLACE_ADD
18 STORE_GLOBAL 1 (count)
20 JUMP_ABSOLUTE 8
&gt;&gt; 22 LOAD_CONST 0 (None)
24 RETURN_VALUE
</code></pre>
<p>Python 3.11 output:</p>
<pre class="language-text"><code class="language-text">7 0 RESUME 0

9 2 LOAD_GLOBAL 1 (NULL + range)
14 LOAD_CONST 1 (1000)
16 PRECALL 1
20 CALL 1
30 GET_ITER
&gt;&gt; 32 FOR_ITER 12 (to 58)
34 STORE_FAST 0 (i)

10 36 LOAD_GLOBAL 2 (count)
48 LOAD_CONST 2 (1)
50 BINARY_OP 13 (+=)
54 STORE_GLOBAL 1 (count)
56 JUMP_BACKWARD 13 (to 32)

9 &gt;&gt; 58 LOAD_CONST 0 (None)
60 RETURN_VALUE
</code></pre>
<p>We can see that INPLACE_ADD opcode has been replaced with BINARY_OP in Python 3.11. I guess that the BINARY_OP is now a thread-safe operation.</p>
<h2 id="references" tabindex="-1"><a class="header-anchor" href="#references">References</a></h2>
<p><a href="https://medium.com/analytics-vidhya/how-to-create-a-thread-safe-singleton-class-in-python-822e1170a7f6">Medium article by Jordan Gillard</a></p>
<p><a href="https://realpython.com/intro-to-python-threading/#working-with-many-threads">RealPython Article on Threading</a></p>
<p><a href="https://betterprogramming.pub/synchronization-primitives-in-python-564f89fee732">Article by Saurabh Chaturvedi</a></p>
<p><a href="https://superfastpython.com/thread-safe-print-in-python/">Superfastpython - Thread Safe print</a></p>
]]></content>
  </entry>
  <entry>
    <title>Building a Python REPL Themed Portfolio Website</title>
    <link href="https://blog.adarshd.dev/posts/building-a-portfolio-REPL/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/building-a-portfolio-REPL/</id>
    <published>2023-08-17T09:30:11Z</published>
    <updated>2023-08-17T09:30:11Z</updated>
    <content type="html"><![CDATA[<h1>Building a Python REPL Themed Portfolio Website</h1>
<p>Launched the initial version of my Python REPL-themed portfolio website (with an ASCII art photo gallery) 🚀.</p>
<p><img src="/assets/img/posts/building-a-portfolio-REPL/portfolio-repl-home.png" alt="site home page">
<em>Home Page</em></p>
<p>Website: <a href="https://adarshd.dev">adarshd.dev</a> or <a href="https://adarsh.pizza">adarsh.pizza</a> 🍕</p>
<p>Source code: <a href="https://github.com/adarshdigievo/Portfolio-REPL/">github.com/adarshdigievo/Portfolio-REPL/</a></p>
<h3 id="internals" tabindex="-1"><a class="header-anchor" href="#internals">Internals</a></h3>
<ul>
<li>UI layer and Python layer - both executed in the client browser</li>
</ul>
<p>The site can be considered to have two components. One is the terminal Ui which is powered by <a href="https://github.com/xtermjs/xterm.js/">Xterm.js</a>. Accepting user input, handling the events, and printing the results are handled using Xterm functions.</p>
<p>The second component is the Python interpreter which runs the code and returns the result to xterm for printing in terminal UI. This is created using Python’s built-in InteractiveInterpreter class found in
<a href="https://docs.python.org/3/library/code.html">code module</a> of stdlib.</p>
<ul>
<li>Loading the portfolio data</li>
</ul>
<p>The portfolio data like skills, experience, etc. are fetched from the free API from <a href="https://gitconnected.com/">Gitconnected</a> and follow the open <a href="https://jsonresume.org/">Jsonresume standard</a></p>
<ul>
<li>Metaprogramming</li>
</ul>
<p>I love metaprogramming and have (over)used some metaprogramming concepts on the site. I wanted the variables storing my portfolio to be dynamic. This is possible with Descriptors where we can call custom getters when an attribute is accessed.</p>
<p>Currently, I have mapped profile data variables passed to the REPL like SKILLS, EXPERIENCE, etc into attributes of the class named <code>ProfileData</code> and have created descriptor attributes using <code>ProfileFetchDescriptor</code>. This descriptor is responsible for calling Gitconnected API and reloading the data if it is expired.</p>
<p>I have stored all profile data fields in an enum named <code>ProfileFields</code> which is used multiple times when I need to list all available pre-loaded variables in the REPL.</p>
<pre class="language-python"><code class="language-python">attrs <span class="token operator">=</span> <span class="token punctuation">{</span>
        field<span class="token punctuation">.</span>name<span class="token punctuation">.</span>lower<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> ProfileFetchDescriptor<span class="token punctuation">(</span>field<span class="token punctuation">)</span> <span class="token keyword">for</span> field <span class="token keyword">in</span> ProfileFields
    <span class="token punctuation">}</span>

    <span class="token comment"># Dynamically generate a class named ProfileData. Each member attribute of the class is a descriptor</span>
    ProfileData <span class="token operator">=</span> <span class="token builtin">type</span><span class="token punctuation">(</span><span class="token string">"ProfileData"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> attrs<span class="token punctuation">)</span>
</code></pre>
<ul>
<li>Redirecting stdout</li>
</ul>
<p>I needed to get the output of code executed by InteractiveIntrpetor into a string so that it can be printed on the xterm ui. For that, <code>redirect_stdout</code> and <code>redirect_stderr</code> functions from contextlib were used.</p>
<pre class="language-python"><code class="language-python">
<span class="token keyword">import</span> io
<span class="token keyword">from</span> contextlib <span class="token keyword">import</span> redirect_stdout

<span class="token keyword">with</span> io<span class="token punctuation">.</span>StringIO<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> buf<span class="token punctuation">,</span> redirect_stdout<span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">'redirected'</span><span class="token punctuation">)</span>
    output <span class="token operator">=</span> buf<span class="token punctuation">.</span>getvalue<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<p>This output is then written on the xterm terminal.</p>
<ul>
<li>Ascii Art powered photo Gallery</li>
</ul>
<p>In the <a href="https://github.com/adarshdigievo/Portfolio-REPL/blob/c6857009255847a8bc507e53a0547a8d45b8c286/scripts/py/gallery.py">gallery code</a>, I have used <a href="https://pypi.org/project/ascii-magic/">ascii magic</a> library to convert images into colored text and it is printed directly to an xterm terminal.</p>
<p>To store image metadata, I have added a <a href="https://github.com/adarshdigievo/Portfolio-REPL/blob/c6857009255847a8bc507e53a0547a8d45b8c286/images/image_meta.json">metadata json file</a> which includes the path to image resources and the caption to display on each image. The page then does an infinite iteration over the image data displaying each of them one by one. I have not decided to cache the images in Python due to memory worries.</p>
<ul>
<li>Opening links</li>
</ul>
<p>To open links to my blog, photo gallery, etc, another descriptor is used. The descriptor uses webbrowser.open() to open the corresponding link. This Python functionality is supported out of the box in Pyscript. I have created an object of <code>ProfileLinks</code> class named <code>VISIT</code></p>
<p><a href="https://github.com/adarshdigievo/Portfolio-REPL/blob/8698ba2b1ccf630d14986953cf9b97b7d2ae664a/scripts/py/index.py#L193">Code here</a></p>
<pre class="language-python"><code class="language-python">link_attrs <span class="token operator">=</span> <span class="token punctuation">{</span>
        field<span class="token punctuation">.</span>upper<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> ProfileLinkDescriptor<span class="token punctuation">(</span>field<span class="token punctuation">)</span>
        <span class="token keyword">for</span> field <span class="token keyword">in</span> ProfileLinkDescriptor<span class="token punctuation">.</span>link_map
    <span class="token punctuation">}</span>
    
    ProfileLinks <span class="token operator">=</span> <span class="token builtin">type</span><span class="token punctuation">(</span><span class="token string">"ProfileLinks"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> link_attrs<span class="token punctuation">)</span>

    VISIT <span class="token operator">=</span> <span class="token punctuation">(</span>
        ProfileLinks<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span>  <span class="token comment"># This class contains descriptor attributes which open corresponding webpages on access. Ex: VISIT.BLOG</span>
    
    interpreter<span class="token punctuation">.</span><span class="token builtin">locals</span> <span class="token operator">|</span><span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">"VISIT"</span><span class="token punctuation">:</span> VISIT<span class="token punctuation">}</span>  <span class="token comment"># Load the class to interpreter locals</span>

</code></pre>
<h3 id="learnings" tabindex="-1"><a class="header-anchor" href="#learnings">Learnings</a></h3>
<p>Random new things I learned from this project:</p>
<ul>
<li>Eruda - JS debugging on mobile browsers</li>
</ul>
<p>The website had some weird input issues on Chrome-based Android browsers while testing. While searching for a tool to debug JS on mobile browsers, I found <a href="https://github.com/liriliri/eruda">Eruda</a>. Eruda will emulate the desktop browser dev console with almost all features like js console logs, network requests, etc and this was super helpful to debug on mobile devices.</p>
<ul>
<li>Pyscript</li>
</ul>
<p><a href="https://pyscript.net/">Pyscript</a> as their tagline says is ‘Programming for the 99%’. Pyscript makes it a lot easier to deploy an application with no manual configuration steps needed like pip installs and can be easily hosted as a static site. It is Python for the front end and will be well-suited for data applications (visualizations, graphics, etc).</p>
<ul>
<li>Pyodide</li>
</ul>
<p>The magic of Pyscript exists thanks to Pyodide and webassembly.
<a href="https://pyodide.org/en/stable/">Pyodide</a> is a python runtime that runs in Webassembly. Pyscript uses Pyodide internally and it can be seen as a convenient wrapper over pyodide. It gets the python code from &lt;py-script&gt; tags and sends them to the interpreter (which is Pyodide by default, and can be customized) for execution. Refer <a href="https://github.com/pyscript/pyscript/blob/be79f70f664467f0074e44309451e8aba6f84798/pyscriptjs/src/components/pyscript.ts#L6">makePyscript()</a> or <a href="https://github.com/pyscript/pyscript/blob/be79f70f664467f0074e44309451e8aba6f84798/pyscriptjs/src/pyexec.ts">pyExec()</a> in the Pyscript source code to see more on the same. Pyscript exposes the power of Pyodide in a simple and convenient manner.</p>
<p>I found the function and variable interoperability b/w Python and JS super convenient. We can call the functions/variables defined in Python using javascript and vice versa. I have used this in the photo gallery page, to print python ascii-magic library output to xterm terminal.</p>
<p>See the example:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// js file</span>

<span class="token keyword">var</span> term <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Terminal</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">cursorBlink</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
    <span class="token literal-property property">fontSize</span><span class="token operator">:</span><span class="token number">6</span><span class="token punctuation">,</span>
    <span class="token literal-property property">cols</span><span class="token operator">:</span><span class="token number">200</span><span class="token punctuation">,</span>
    <span class="token literal-property property">rows</span><span class="token operator">:</span><span class="token number">150</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

term<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'terminal'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre>
<pre class="language-python"><code class="language-python"><span class="token comment"># python file</span>

<span class="token keyword">from</span> js <span class="token keyword">import</span> term  <span class="token comment"># the js object is now available in Python</span>
  
<span class="token comment"># can access all js functions defined in xterm js library from Python!</span>
term<span class="token punctuation">.</span>clear<span class="token punctuation">(</span><span class="token punctuation">)</span>
term<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token string">'some_string'</span><span class="token punctuation">)</span>

</code></pre>
<p>Here is the <a href="https://github.com/pyodide/pyodide/blob/1c765db28fe279fc590bd2d12d530bdded7aad74/src/core/python2js.c#L754">C Soruce code of the function to_js()</a> which is used to convert Python calls as seen above to js and <a href="https://github.com/pyodide/pyodide/blob/1c765db28fe279fc590bd2d12d530bdded7aad74/src/js/api.ts#L343">this is the function toPy()</a> used to convert js objects to Python.</p>
<h3 id="planned-updates-for-the-next-versions" tabindex="-1"><a class="header-anchor" href="#planned-updates-for-the-next-versions">Planned updates for the next versions</a></h3>
<p>Web (frontend) development is hard. There are a lot of layout &amp; compatibility issues across multiple platforms and browsers.</p>
<p>When I tested the home page on mobile devices for the first time, inputs were not working on Chromium-based mobile browsers, but were working fine on Firefox focus. I finally ended up with code like this:</p>
<pre class="language-javascript"><code class="language-javascript">
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">Android|webOS|iPhone|iPad|iPod|BlackBerry</span><span class="token regex-delimiter">/</span><span class="token regex-flags">i</span></span><span class="token punctuation">.</span><span class="token function">test</span><span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>userAgent<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// onKey event is not firing on android chromium based browser. This workaround is applied in that case.</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'mobile device'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
</code></pre>
<p>Other tasks planned for coming versions:</p>
<ul>
<li>Support multiline inputs (to support function and class definitions)</li>
</ul>
<p>I guess I might need to switch to InteractiveConsole class of the code module instead of the currently used InteractiveIntrepretor class.</p>
<ul>
<li>Switch to py-terminal</li>
</ul>
<p>Switch to <a href="https://docs.pyscript.net/latest/reference/plugins/py-terminal.html">py-terminal plugin</a> of Pyscript and perform all term-related tasks (managing history, xterm events, etc) in Python code itself. I can skip the load and init steps of xterm using js since pyscript now support xterm terminal out of the box.
Looking to implement something along the lines of <a href="https://github.com/JeffersGlass/emscripten-shell/blob/main/pyxterm/src/interactive.py">this Pyscript plugin(emscripten-shell)</a></p>
<ul>
<li>
<p>Use a custom on-screen keyboard using <a href="https://github.com/furcan/KioskBoard">kioskboard.js</a>, in case of mobile devices to support arrow keys, command history, etc.</p>
</li>
<li>
<p>Develop a visual version of the website</p>
</li>
<li>
<p>Support pastes in REPL</p>
</li>
<li>
<p>ast based variable parsing</p>
</li>
</ul>
<p>Currently, the profile data variables are mapped to the descriptor attributes using a simple and hacky <code>string.contains()</code> call <a href="https://github.com/adarshdigievo/Portfolio-REPL/blob/c6857009255847a8bc507e53a0547a8d45b8c286/scripts/py/index.py#L27">here</a>. I plan to first parse the input code into AST and replace the areas of variable access to use the descriptors.</p>
<h3 id="prior-art" tabindex="-1"><a class="header-anchor" href="#prior-art">Prior Art</a></h3>
<ul>
<li>Terminal UI was Adapted from <a href="https://codepen.io/mwelgharb/pen/qBEpLEe">this Codepen</a>.</li>
</ul>
<hr>
<p><img src="/assets/img/posts/building-a-portfolio-REPL/photo-gallery.png" alt="photo gallery">
<em>Ascii Photo Gallery</em></p>
<p>Visit the site: <a href="https://adarshd.dev">adarshd.dev</a></p>
<p>Photo gallery: <a href="https://adarshd.dev/gallery.html">adarshd.dev/gallery.html</a></p>
<p>Source code: <a href="https://github.com/adarshdigievo/Portfolio-REPL/">github.com/adarshdigievo/Portfolio-REPL/</a></p>
]]></content>
  </entry>
  <entry>
    <title>It is said that, in Python everything is an object. I took that literally.</title>
    <link href="https://blog.adarshd.dev/posts/everything-is-an-object/" rel="alternate" type="text/html"/>
    <id>https://blog.adarshd.dev/posts/everything-is-an-object/</id>
    <published>2023-07-26T16:57:11Z</published>
    <updated>2023-07-26T16:57:11Z</updated>
    <content type="html"><![CDATA[<p>You must have heard the saying that <em>“In Python everything is an object”</em>. Let’s take that statement literally and test it.</p>
<h3 id="lets-check" tabindex="-1"><a class="header-anchor" href="#lets-check">Let’s check:</a></h3>
<blockquote class="prompt-info">
<p>The code in the below examples works as-is. No additional imports are required.</p>
</blockquote>
<p>We will be using isinstance function to check if ‘everything’ is an object.</p>
<blockquote>
<p>From <a href="https://docs.python.org/3/library/functions.html#isinstance">Python Docs</a>:</p>
<p>builtin function <strong>isinstance(object, classinfo)</strong>:</p>
<p>Return True if the object argument is an instance of the classinfo argument, or of a (direct, indirect, or virtual) subclass thereof. If object is not an object of the given type, the function always returns False</p>
</blockquote>
<h4>1. int</h4>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> dummy_int <span class="token operator">=</span> <span class="token number">3</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>dummy_int<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
</code></pre>
<p>Testing the class ‘int’ itself</p>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
</code></pre>
<h4>2. Testing a user defined class</h4>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">class</span> <span class="token class-name">DummyClass</span><span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> 	<span class="token keyword">pass</span>

<span class="token operator">>></span><span class="token operator">></span> dummy_obj <span class="token operator">=</span> DummyClass<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>dummy_obj<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>DummyClass<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>

</code></pre>
<h4>3. list</h4>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> l <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">]</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>l<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token boolean">True</span>
</code></pre>
<h4>4. Functions</h4>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">def</span> <span class="token function">dummy_function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>     <span class="token keyword">pass</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>dummy_function<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
</code></pre>
<p>Testing the ‘isinstance’ function</p>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span><span class="token builtin">isinstance</span><span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
</code></pre>
<h4>5. Exceptions and tracebacks</h4>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">import</span> sys
<span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">try</span><span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>     <span class="token keyword">raise</span> Exception<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">except</span> Exception <span class="token keyword">as</span> e<span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>     exc <span class="token operator">=</span> e
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>     traceback <span class="token operator">=</span> sys<span class="token punctuation">.</span>exc_info<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>traceback<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>exc<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
</code></pre>
<h4>6. Modules</h4>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">import</span> math
<span class="token operator">>></span><span class="token operator">></span> math
<span class="token operator">&lt;</span>module <span class="token string">'math'</span> <span class="token punctuation">(</span>built<span class="token operator">-</span><span class="token keyword">in</span><span class="token punctuation">)</span><span class="token operator">></span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">isinstance</span><span class="token punctuation">(</span>math<span class="token punctuation">,</span> <span class="token builtin">object</span><span class="token punctuation">)</span>
<span class="token boolean">True</span>
</code></pre>
<h3 id="why" tabindex="-1"><a class="header-anchor" href="#why">Why:</a></h3>
<blockquote>
<p>From <a href="https://docs.python.org/3/library/functions.html?highlight=object#object">Python docs</a>:</p>
<p><strong>class object</strong>:</p>
<p>Return a new featureless object. object is a base for all classes. It has methods that are common to all instances of Python classes. This function does not accept any arguments.</p>
</blockquote>
<p>The object class is the base class for all other classes in Python. This means that all Python classes inherit from the object class, either directly or indirectly. The object class provides a number of methods that are common to all objects in Python, such as the <strong>init</strong>() method, and the <strong>setattr</strong>() method. These methods allow objects to be created, accessed, and modified.</p>
<p>We can verify that ‘object’ is the base class of other classes by using the base dunder.</p>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">import</span> math
<span class="token operator">>></span><span class="token operator">></span> math
<span class="token operator">&lt;</span>module <span class="token string">'math'</span> <span class="token punctuation">(</span>built<span class="token operator">-</span><span class="token keyword">in</span><span class="token punctuation">)</span><span class="token operator">></span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">type</span><span class="token punctuation">(</span>math<span class="token punctuation">)</span><span class="token punctuation">.</span>__base__
<span class="token operator">&lt;</span><span class="token keyword">class</span> <span class="token string">'object'</span><span class="token operator">></span>
</code></pre>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> dummy_int <span class="token operator">=</span> <span class="token number">10_000</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">type</span><span class="token punctuation">(</span>dummy_int<span class="token punctuation">)</span>
<span class="token operator">&lt;</span><span class="token keyword">class</span> <span class="token string">'int'</span><span class="token operator">></span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">type</span><span class="token punctuation">(</span>dummy_int<span class="token punctuation">)</span><span class="token punctuation">.</span>__base__
<span class="token operator">&lt;</span><span class="token keyword">class</span> <span class="token string">'object'</span><span class="token operator">></span>
</code></pre>
<pre class="language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">type</span><span class="token punctuation">(</span><span class="token builtin">type</span><span class="token punctuation">)</span>
<span class="token operator">&lt;</span><span class="token keyword">class</span> <span class="token string">'type'</span><span class="token operator">></span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token builtin">type</span><span class="token punctuation">.</span>__base__
<span class="token operator">&lt;</span><span class="token keyword">class</span> <span class="token string">'object'</span><span class="token operator">></span>
</code></pre>
]]></content>
  </entry>
</feed>
