<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="notesofcliff.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="notesofcliff.github.io/" rel="alternate" type="text/html" /><updated>2024-10-05T13:20:56+00:00</updated><id>notesofcliff.github.io/feed.xml</id><title type="html">Notes of Cliff</title><subtitle>Notes of Cliff is my personal-professional blog. If you follow my posts, you can expect to read about Development, Automation, Linux, Windows and a lot of other tech.</subtitle><entry><title type="html">Writing a simplified, cross-platform grep clone in Python</title><link href="notesofcliff.github.io/python/python3/windows/mac/linux/cli/argparse/standard%20library/grep/search/text%20files/2024/10/02/creating-grep.py-a-simplified-version-of-grep.html" rel="alternate" type="text/html" title="Writing a simplified, cross-platform grep clone in Python" /><published>2024-10-02T20:00:00+00:00</published><updated>2024-10-02T20:00:00+00:00</updated><id>notesofcliff.github.io/python/python3/windows/mac/linux/cli/argparse/standard%20library/grep/search/text%20files/2024/10/02/creating-grep.py-a-simplified-version-of-grep</id><content type="html" xml:base="notesofcliff.github.io/python/python3/windows/mac/linux/cli/argparse/standard%20library/grep/search/text%20files/2024/10/02/creating-grep.py-a-simplified-version-of-grep.html"><![CDATA[<h2 id="writing-a-simplified-cross-platform-grep-clone-in-python">Writing a simplified, cross-platform grep clone in Python</h2>

<p>In this tutorial, we will explore how to write a simplified, cross-platform version of the <code class="language-plaintext highlighter-rouge">grep</code> command line utility.</p>

<p>If you don’t know, <code class="language-plaintext highlighter-rouge">grep</code> is a posix utility present on nearly every version of Linux. <code class="language-plaintext highlighter-rouge">grep</code> takes a regular expression and an optional list of files, then it outputs every line of the files that match the regular expression.</p>

<p>If you don’t know what regular expressions are, please stay tuned for a regular expression tutorial that I will have coming soon.</p>

<h3 id="step-1-setting-up-the-basic-structure">Step 1: Setting up the basic structure</h3>

<p>First, we are going to set up the basic structure of the program.</p>

<p>Take a look at the code, and then we will explore what it is doing:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="k">return</span> <span class="mi">0</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]))</span>
</code></pre></div></div>

<p>We import the <code class="language-plaintext highlighter-rouge">sys</code> module on the first line, which provides functions and variables for interacting with the operating system and the Python interpreter.</p>

<p>The <code class="language-plaintext highlighter-rouge">main</code> function is defined next. It is empty now save for the <code class="language-plaintext highlighter-rouge">return 0</code>, but we will soon add our logic here.</p>

<p>The <code class="language-plaintext highlighter-rouge">if __name__ == "__main__":</code> block checks if the script is being run directly (i.e., not being imported as a module by another script). If so, it calls the <code class="language-plaintext highlighter-rouge">main</code> function with the command-line arguments.</p>

<p>The <code class="language-plaintext highlighter-rouge">sys.argv[1:]</code> expression returns the command-line arguments passed to the script, excluding the script name itself.</p>

<h3 id="step-2-adding-argument-parsing">Step 2: Adding argument parsing</h3>

<p>Next, we will add argument parsing to the program.</p>

<p>Take a look at the code, and then we will explore what it is doing:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">argparse</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span>
        <span class="n">prog</span><span class="o">=</span><span class="s">"grepy"</span><span class="p">,</span>
        <span class="n">description</span><span class="o">=</span><span class="s">"A grep-inspired tool with different trade-offs"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"expression"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The regular expression to search for"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"input_files"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"r"</span><span class="p">),</span>
        <span class="n">nargs</span><span class="o">=</span><span class="s">"*"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="nb">tuple</span><span class="p">(</span><span class="s">"-"</span><span class="p">),</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The file(s) to search"</span>
    <span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="k">return</span> <span class="mi">0</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]))</span>
</code></pre></div></div>

<p>Here, we add <code class="language-plaintext highlighter-rouge">argparse</code> to the list of imports at the top, which provides a way to define the command-line interface of the program.</p>

<p>The <code class="language-plaintext highlighter-rouge">main</code> function is then updated to create an <code class="language-plaintext highlighter-rouge">ArgumentParser</code> object, which defines the available arguments.</p>

<p>In this case, we define two arguments: <code class="language-plaintext highlighter-rouge">expression</code> and <code class="language-plaintext highlighter-rouge">input_files</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">expression</code> argument is a string that specifies the regular expression to search for, and the <code class="language-plaintext highlighter-rouge">input_files</code> argument is a list of files to search in.</p>

<p>The <code class="language-plaintext highlighter-rouge">nargs="*"</code> argument specifies that <code class="language-plaintext highlighter-rouge">input_files</code> should be a list of files, and the <code class="language-plaintext highlighter-rouge">default=tuple("-")</code> argument specifies that if no files are provided, the program should search <code class="language-plaintext highlighter-rouge">stdin</code> (i.e., the <code class="language-plaintext highlighter-rouge">-</code> character is replaced <code class="language-plaintext highlighter-rouge">stdin</code>).</p>

<p>The value <code class="language-plaintext highlighter-rouge">"-"</code> is being passed to the <code class="language-plaintext highlighter-rouge">tuple</code> factory, I’m doing it like this because the <code class="language-plaintext highlighter-rouge">nargs="*"</code> will cause any arguments passed to be in a list, but if no arguments are passed the default value will be used. I chose <code class="language-plaintext highlighter-rouge">tuple</code> instead of <code class="language-plaintext highlighter-rouge">list</code> because <code class="language-plaintext highlighter-rouge">list</code>s are mutable and would be attached to the <code class="language-plaintext highlighter-rouge">ArgumentParser</code> instance which creates an oportunity for the value to change unexpectedly.</p>

<h3 id="step-3-adding-loops-to-go-through-each-line-of-the-files">Step 3: Adding loops to go through each line of the files</h3>

<p>Next, we will add in the loops to go through each line of each of the files.</p>

<p>Take a look at the code, and then we will explore what it is doing:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">argparse</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span>
        <span class="n">prog</span><span class="o">=</span><span class="s">"grepy"</span><span class="p">,</span>
        <span class="n">description</span><span class="o">=</span><span class="s">"A grep-inspired tool with different trade-offs"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"expression"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The regular expression to search for"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"input_files"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"r"</span><span class="p">),</span>
        <span class="n">nargs</span><span class="o">=</span><span class="s">"*"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="nb">tuple</span><span class="p">(</span><span class="s">"-"</span><span class="p">),</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The file(s) to search"</span>
    <span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">input_file</span> <span class="ow">in</span> <span class="n">args</span><span class="p">.</span><span class="n">input_files</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">input_file</span><span class="p">:</span>
            <span class="p">...</span>
    <span class="k">return</span> <span class="mi">0</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]))</span>
</code></pre></div></div>

<p>Here, we add loops to go through each line of the files in turn.</p>

<p>We import the <code class="language-plaintext highlighter-rouge">re</code> module, which provides regular expression matching operations.</p>

<p>The <code class="language-plaintext highlighter-rouge">main</code> function is then updated to iterate over the files and then to iterate through each line of the file.</p>

<h3 id="step-4-adding-support-for-regular-expression-search">Step 4: Adding support for regular expression search</h3>

<p>Next, we are going to add the magic of our script with the Python <code class="language-plaintext highlighter-rouge">re</code> module</p>

<p>Take a look at the code, and then we will explore what it is doing:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Add support for regular expression search
</span><span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">argparse</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span>
        <span class="n">prog</span><span class="o">=</span><span class="s">"grepy"</span><span class="p">,</span>
        <span class="n">description</span><span class="o">=</span><span class="s">"A grep-inspired tool with different trade-offs"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"expression"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The regular expression to search for"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"input_files"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"r"</span><span class="p">),</span>
        <span class="n">nargs</span><span class="o">=</span><span class="s">"*"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="nb">tuple</span><span class="p">(</span><span class="s">"-"</span><span class="p">),</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The file(s) to search"</span>
    <span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="n">expression</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nb">compile</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">expression</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">input_file</span> <span class="ow">in</span> <span class="n">args</span><span class="p">.</span><span class="n">input_files</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">input_file</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">expression</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
                <span class="k">print</span><span class="p">(</span><span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">())</span>
    <span class="k">return</span> <span class="mi">0</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]))</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">re</code> module was added to the imports at the top.</p>

<p>Then <code class="language-plaintext highlighter-rouge">expression.search(line)</code> is used to see if the line matches the regular expression, and if it matches, the <code class="language-plaintext highlighter-rouge">print(line.strip())</code> statement prints the matched line.</p>

<h3 id="step-5-adding-support-for-filenames-and-line-numbers">Step 5: Adding support for filenames and line numbers</h3>

<p>There are two common options that I almost always end up passing to <code class="language-plaintext highlighter-rouge">grep</code>, so let’s add them now.</p>

<p>Take a look at the code, and then we will explore what it is doing:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">argparse</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span>
        <span class="n">prog</span><span class="o">=</span><span class="s">"grepy"</span><span class="p">,</span>
        <span class="n">description</span><span class="o">=</span><span class="s">"A grep-inspired tool with different trade-offs"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"-n"</span><span class="p">,</span> <span class="s">"--line-number"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"store_true"</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"Print the line number with the match"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"-H"</span><span class="p">,</span> <span class="s">"--with-filename"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"store_true"</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"Print the filename with the match"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"expression"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The regular expression to search for"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"input_files"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"r"</span><span class="p">),</span>
        <span class="n">nargs</span><span class="o">=</span><span class="s">"*"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="nb">tuple</span><span class="p">(</span><span class="s">"-"</span><span class="p">),</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The file(s) to search"</span>
    <span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="n">expression</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nb">compile</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">expression</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">input_file</span> <span class="ow">in</span> <span class="n">args</span><span class="p">.</span><span class="n">input_files</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">line_number</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">input_file</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">expression</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
                <span class="n">message</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span>
                <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">line_number</span><span class="p">:</span>
                    <span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">line_number</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">message</span><span class="si">}</span><span class="s">"</span>
                <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">with_filename</span><span class="p">:</span>
                    <span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">input_file</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">message</span><span class="si">}</span><span class="s">"</span>
                <span class="k">print</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
    <span class="k">return</span> <span class="mi">0</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]))</span>
</code></pre></div></div>

<p>Here, we add support for adding the filenames and line numbers to the output.</p>

<p>The first change you will probably notice are the updates the <code class="language-plaintext highlighter-rouge">main</code> function to define two additional arguments: <code class="language-plaintext highlighter-rouge">--line-number</code> and <code class="language-plaintext highlighter-rouge">--with-filename</code>. I tried to add these options with the same names as are used in GNU grep.</p>

<p>The <code class="language-plaintext highlighter-rouge">--line-number</code> argument specifies whether to print the line number with the match, and the <code class="language-plaintext highlighter-rouge">--with-filename</code> argument specifies whether to print the filename with the match.</p>

<p>The <code class="language-plaintext highlighter-rouge">enumerate(input_file)</code> function returns an iterator over the lines of the input file, along with their line numbers so we can add them to the output.</p>

<p>The <code class="language-plaintext highlighter-rouge">if expression.search(line)</code> call checks if the line matches the regular expression, and the <code class="language-plaintext highlighter-rouge">print(message)</code> statement prints the matched line with the specified options.</p>

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

<p>Thanks for taking the time to read this, I tried to make it as accessible and sensible as I could, but if you have any questions or concerns, please feel free to open an issue in the <a href="https://github.com/notesofcliff/notesofcliff.github.io/issues">Github repo</a> for this blog.</p>]]></content><author><name></name></author><category term="python" /><category term="python3" /><category term="windows" /><category term="mac" /><category term="linux" /><category term="CLI" /><category term="argparse" /><category term="standard library" /><category term="grep" /><category term="search" /><category term="text files" /><summary type="html"><![CDATA[Writing a simplified, cross-platform grep clone in Python]]></summary></entry><entry><title type="html">Introducing command.py - a flexible template for Python CLIs using the standard library</title><link href="notesofcliff.github.io/python/python3/windows/mac/linux/cli/argparse/logging/standard%20library/2024/09/30/Introducing-command.py.html" rel="alternate" type="text/html" title="Introducing command.py - a flexible template for Python CLIs using the standard library" /><published>2024-09-30T20:00:00+00:00</published><updated>2024-09-30T20:00:00+00:00</updated><id>notesofcliff.github.io/python/python3/windows/mac/linux/cli/argparse/logging/standard%20library/2024/09/30/Introducing-command.py</id><content type="html" xml:base="notesofcliff.github.io/python/python3/windows/mac/linux/cli/argparse/logging/standard%20library/2024/09/30/Introducing-command.py.html"><![CDATA[<h2 id="introducing-commandpy">Introducing <code class="language-plaintext highlighter-rouge">command.py</code></h2>
<p><em>A Flexible CLI Template using just the standard library</em></p>

<p>I often need to create little CLI scripts, and I usually reach for Python for this task. Now there are many easy ways to make sensible CLIs with Python such as <a href="https://click.palletsprojects.com/en/8.1.x/">click</a>, <a href="https://github.com/openstack/cliff">cliff</a> and others.</p>

<p>However, reaching for third party libraries, while frequently necessary, can lead to problems later on as you continue to support your utility. Sometimes libraries become unmaintained or introduce incompatibilities and leave you with extra work updating all of your scripts.</p>

<p>Luckily, Python’s standard library contains everything you need to have a sensible, featureful CLI and has a pretty strong history of maintaining backwards compatibility.</p>

<p>In this article, we will explore the template I made to kickstart simple CLI scripts using the Python standard library.</p>

<h2 id="the-code">The code</h2>

<p>The template (at of the time of writing) is shown below, I am pasting it here in its entirety and I will refer to pieces of the code throughout this article.</p>

<p><em>NOTE</em>: You can find the latest version of this template in the repository <a href="https://github.com/notesofcliff/python_cli_template">here</a>, please feel free to open issues or submit pull requests.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">"""This script is meant to serve as an example and a
template for writing programs with a Command Line
Interface.

Features Include:
    * -vvv style logging verbosity arguments
    * Option to output logs to a file in addition to stderr
    * Sensible Return Codes
    * Intuitive code organization
    * Input file that defaults to stdin
    * Output file that defaults to stdout 

This code is released under the GPL v3.

Feel free to copy this file `command.py` for the scaffolding
of the script and the file `test_command.py` for the tests.
"""</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">pathlib</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">argparse</span>

<span class="n">LOG_LEVELS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s">'CRITICAL'</span><span class="p">,</span>
    <span class="s">'FATAL'</span><span class="p">,</span>
    <span class="s">'ERROR'</span><span class="p">,</span>
    <span class="s">'WARNING'</span><span class="p">,</span>
    <span class="s">'INFO'</span><span class="p">,</span>
    <span class="s">'DEBUG'</span><span class="p">,</span>
<span class="p">]</span>

<span class="n">RETURN_CODES</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"SUCCESS"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
    <span class="s">"UNHANDLED_EXCEPTION"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">def</span> <span class="nf">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="s">"""This function is responsible for parsing the command line
    arguments as well as any required post-processing.
    """</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">()</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"source"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"r"</span><span class="p">),</span>
        <span class="n">default</span><span class="o">=</span><span class="s">"-"</span><span class="p">,</span>
        <span class="n">nargs</span><span class="o">=</span><span class="s">"?"</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The input file. Defaults to stdin"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"destination"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"w"</span><span class="p">),</span>
        <span class="n">default</span><span class="o">=</span><span class="s">"-"</span><span class="p">,</span>
        <span class="n">nargs</span><span class="o">=</span><span class="s">"?"</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"The output file. Defaults to stdout."</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"-v"</span><span class="p">,</span> <span class="s">"--verbose"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"count"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"If specified, increases logging verbosity. "</span>
             <span class="s">"Can be specified multiple times (ie. -vvv)"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"-l"</span><span class="p">,</span> <span class="s">"--log-file"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">pathlib</span><span class="p">.</span><span class="n">Path</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"If provided, it must be the path to a file. The "</span>
             <span class="s">"given file will be used for logging. NOTE: This file"</span>
             <span class="s">"will be overwritten."</span>
    <span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="n">args</span><span class="p">.</span><span class="n">verbose</span> <span class="o">=</span> <span class="n">LOG_LEVELS</span><span class="p">[</span><span class="nb">min</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">verbose</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">LOG_LEVELS</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)]</span>
    <span class="k">return</span> <span class="n">args</span>

<span class="k">def</span> <span class="nf">configure_logging</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="s">"""This function configures the Python logging system
    based on the level and filename provided.
    """</span>
    <span class="n">handlers</span> <span class="o">=</span> <span class="p">[</span>
        <span class="n">logging</span><span class="p">.</span><span class="n">StreamHandler</span><span class="p">(</span>
            <span class="n">stream</span><span class="o">=</span><span class="n">sys</span><span class="p">.</span><span class="n">stderr</span><span class="p">,</span>
        <span class="p">),</span>
    <span class="p">]</span>
    <span class="k">if</span> <span class="n">filename</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">handlers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span>
            <span class="n">logging</span><span class="p">.</span><span class="n">FileHandler</span><span class="p">(</span>
                <span class="n">filename</span><span class="p">,</span>
                <span class="n">mode</span><span class="o">=</span><span class="s">"w"</span><span class="p">,</span>
            <span class="p">)</span>
        <span class="p">)</span>

    <span class="n">logging</span><span class="p">.</span><span class="n">basicConfig</span><span class="p">(</span>
        <span class="n">level</span><span class="o">=</span><span class="n">level</span><span class="p">,</span>
        <span class="n">handlers</span><span class="o">=</span><span class="n">handlers</span><span class="p">,</span>
    <span class="p">)</span>

<span class="k">def</span> <span class="nf">_main</span><span class="p">(</span><span class="n">args</span><span class="p">):</span>
    <span class="s">"""This function is responsible for executing the application logic.
    """</span>
    <span class="n">log</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">args</span><span class="p">.</span><span class="n">source</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s">"Found line: </span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">bytes_written</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="n">destination</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
        <span class="n">log</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s">"Wrote </span><span class="si">{</span><span class="n">bytes_written</span><span class="si">}</span><span class="s"> bytes to </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">destination</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s">"Exiting successfully"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">RETURN_CODES</span><span class="p">[</span><span class="s">"SUCCESS"</span><span class="p">]</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="s">"""This method is executed when this script is called
    from the command line.

    This function is responsible for the following:

    * parse command line arguments
    * Configure logging based on arguments
    * Call the function `_main` which should hold the application logic
    * return the return code as determined by function `_main`
    """</span>
    <span class="n">log</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">args</span> <span class="o">=</span> <span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">exception</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="n">critical</span><span class="p">(</span><span class="sa">f</span><span class="s">"An unhandled exception has occurred: </span><span class="si">{</span><span class="n">exception</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">RETURN_CODES</span><span class="p">[</span><span class="s">"UNHANDLED_EXCEPTION"</span><span class="p">]</span>
    <span class="n">configure_logging</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">verbose</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">log_file</span><span class="p">)</span>
    <span class="n">log</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
    <span class="n">log</span><span class="p">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s">"Received args: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">return_value</span> <span class="o">=</span> <span class="n">_main</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">exception</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="n">critical</span><span class="p">(</span><span class="sa">f</span><span class="s">"An unhandled exception has occurred: </span><span class="si">{</span><span class="n">exception</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">return_value</span> <span class="o">=</span> <span class="n">RETURN_CODES</span><span class="p">[</span><span class="s">"UNHANDLED_EXCEPTION"</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">return_value</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">rc</span> <span class="o">=</span> <span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:])</span>
    <span class="k">except</span><span class="p">:</span>
        <span class="n">rc</span> <span class="o">=</span> <span class="n">RETURN_CODES</span><span class="p">[</span><span class="s">"UNHANDLED_EXCEPTION"</span><span class="p">]</span>
    <span class="k">finally</span><span class="p">:</span>
        <span class="n">logging</span><span class="p">.</span><span class="n">shutdown</span><span class="p">()</span>
    <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">rc</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="what-the-script-does">What the Script does</h2>

<p>As a template, there was little point in writing complex application logic, but I still wanted to show a use-case for context, so I decided to give the script the task to take a source file and writes its contents to a destination file. A simple echo program.</p>

<h2 id="the-execution">The Execution</h2>

<p>When executed, the interpreter will run from top-to-bottom defining the global variables and functions until it encounters the line <code class="language-plaintext highlighter-rouge">if __name__ == "__main__":</code>. That’s where our application logic begins.</p>

<p>Inside the <code class="language-plaintext highlighter-rouge">if __name__ == "__main__":</code> block, we execute the <code class="language-plaintext highlighter-rouge">main</code> function in a try…except block. Any exceptions that are caught cause a generic return code. Finally, we shutdown the logging and exit with our return code which was either returned by main or assigned the generic error code <code class="language-plaintext highlighter-rouge">UNHANDLED_EXCEPTION</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">main</code> function handles the following tasks:</p>

<ul>
  <li>parse the command line arguments</li>
  <li>Configure logging based on user-provided arguments</li>
  <li>Call the function <code class="language-plaintext highlighter-rouge">_main</code></li>
  <li>return the return code as returned by function <code class="language-plaintext highlighter-rouge">_main</code></li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">_main</code> function is where the bulk of our application logic will live. In our case, the <code class="language-plaintext highlighter-rouge">_main</code> function logs some information and copies the contents of source to destination.</p>

<p>That’s the basic execution flow, let’s look next at some specifics.</p>

<h3 id="input-and-output-files">Input and Output Files</h3>

<p>One of the things that makes <code class="language-plaintext highlighter-rouge">command.py</code> so flexible is its input and output options. By default, the input is <code class="language-plaintext highlighter-rouge">stdin</code> and the output is <code class="language-plaintext highlighter-rouge">stdout</code>. However, users can also specify a file path to read or write to files on disk.</p>

<p>Let’s look at where those arguments are defined:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">...</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
    <span class="s">"source"</span><span class="p">,</span>
    <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"r"</span><span class="p">),</span>
    <span class="n">default</span><span class="o">=</span><span class="s">"-"</span><span class="p">,</span>
    <span class="n">nargs</span><span class="o">=</span><span class="s">"?"</span><span class="p">,</span>
    <span class="n">help</span><span class="o">=</span><span class="s">"The input file. Defaults to stdin"</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
    <span class="s">"destination"</span><span class="p">,</span>
    <span class="nb">type</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">FileType</span><span class="p">(</span><span class="s">"w"</span><span class="p">),</span>
    <span class="n">default</span><span class="o">=</span><span class="s">"-"</span><span class="p">,</span>
    <span class="n">nargs</span><span class="o">=</span><span class="s">"?"</span><span class="p">,</span>
    <span class="n">help</span><span class="o">=</span><span class="s">"The output file. Defaults to stdout."</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">...</span>
</code></pre></div></div>

<p>As you can see in the code above, we’re creating two arguments:</p>

<ul>
  <li><strong>source</strong>: This argument represents the input file(s) for our program. We’re using argparse.FileType(“r”) to specify that this argument should accept a file path that opens for reading (“r”). The default=”-“ means that if no input file is provided, it will default to the standard input (stdin).</li>
  <li><strong>destination</strong>: This argument represents the output file(s) for our program. We’re using argparse.FileType(“w”) to specify that this argument should accept a file path that opens for writing (“w”). The default=”-“ means that if no output file is provided, it will default to the standard output (stdout).</li>
</ul>

<h2 id="logging-made-easy-for-the-user">Logging Made Easy (for the user)</h2>

<p>One of the key features that sets <code class="language-plaintext highlighter-rouge">command.py</code> apart is how flexible the logging is on the user’s end.</p>

<p>With a simple <code class="language-plaintext highlighter-rouge">-v</code> flag, you can control the verbosity of your logs. What’s clever about this is that you can specify <code class="language-plaintext highlighter-rouge">-v</code> multiple times to increase the logging level with each occurrence. For example, the default is <code class="language-plaintext highlighter-rouge">CRITICAL</code> and <code class="language-plaintext highlighter-rouge">-v</code> would change the log level to <code class="language-plaintext highlighter-rouge">FATAL</code>, <code class="language-plaintext highlighter-rouge">-vvvv</code> would output info messages, <code class="language-plaintext highlighter-rouge">-vvvvv</code> would output debug messages. This pattern makes it easy to fine-tune the logging at runtime to suit your needs.</p>

<p>Let’s look at how that works:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">LOG_LEVELS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s">'CRITICAL'</span><span class="p">,</span>
    <span class="s">'FATAL'</span><span class="p">,</span>
    <span class="s">'ERROR'</span><span class="p">,</span>
    <span class="s">'WARNING'</span><span class="p">,</span>
    <span class="s">'INFO'</span><span class="p">,</span>
    <span class="s">'DEBUG'</span><span class="p">,</span>
<span class="p">]</span>

<span class="k">def</span> <span class="nf">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">()</span>
    <span class="p">...</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"-v"</span><span class="p">,</span> <span class="s">"--verbose"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"count"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"If specified, increases logging verbosity. "</span>
             <span class="s">"Can be specified multiple times (ie. -vvv)"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="p">...</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
    <span class="n">args</span><span class="p">.</span><span class="n">verbose</span> <span class="o">=</span> <span class="n">LOG_LEVELS</span><span class="p">[</span><span class="nb">min</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">verbose</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">LOG_LEVELS</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)]</span>
</code></pre></div></div>

<p>The first part of the verbosity option is the <code class="language-plaintext highlighter-rouge">action="count""</code> which causes the <code class="language-plaintext highlighter-rouge">ArgumentParser</code> to count the number of times the option was specified at the command line.</p>

<p>Next, let’s examine where the real magic happens. At the top of the script, in the <code class="language-plaintext highlighter-rouge">global</code> scope, we define a constant called <code class="language-plaintext highlighter-rouge">LOG_LEVELS</code>.</p>

<p><code class="language-plaintext highlighter-rouge">LOG_LEVELSis</code> is used in the semi-confusing line <code class="language-plaintext highlighter-rouge">args.verbose = LOG_LEVELS[min(args.verbose, len(LOG_LEVELS)-1)]</code>. Here we are pulling out an item from <code class="language-plaintext highlighter-rouge">LOG_LEVELS</code> based on the count of <code class="language-plaintext highlighter-rouge">-v</code>s present in the arguments. The weird part is the call to <code class="language-plaintext highlighter-rouge">min</code> inside of the square brackets (ie. <code class="language-plaintext highlighter-rouge">[]</code>), this is needed in case someone specifies too many <code class="language-plaintext highlighter-rouge">-v</code>s in which case we will select the most verbose option available (ie. <code class="language-plaintext highlighter-rouge">DEBUG</code>)</p>

<p>The next piece of logging configuration exposed to the user is the ability to specify a file to write logs to using the <code class="language-plaintext highlighter-rouge">--log-file</code> option. Logs default to going to <code class="language-plaintext highlighter-rouge">stderr</code>, but if a user provides a log file, logs will be written to the log file in addition to <code class="language-plaintext highlighter-rouge">stderr</code>. This flexibility makes it easy for your users to customize their logging to suit their own needs.</p>

<p><strong>Error Codes, Simplified</strong></p>

<p>When it comes to error handling, <code class="language-plaintext highlighter-rouge">command.py</code> uses a simple global dictionary called <code class="language-plaintext highlighter-rouge">RETURN_CODES</code> to keep track of all possible return codes. This makes it easy to add new error codes or modify existing ones without having to modify the underlying code.</p>

<p>In order to use the <code class="language-plaintext highlighter-rouge">RETURN_CODES</code> dict, simply return the relevant return code from the dict, like we do in <code class="language-plaintext highlighter-rouge">_main</code> in the line <code class="language-plaintext highlighter-rouge">return RETURN_CODES["SUCCESS"]</code>.</p>

<p><strong>Free and Open-Source</strong></p>

<p>Finally, we want to emphasize that <code class="language-plaintext highlighter-rouge">command.py</code> is free to use and is released under the GPL v3. This means that you can modify and distribute the code as you see fit, without worrying about licensing restrictions.</p>

<p>We hope you find <code class="language-plaintext highlighter-rouge">command.py</code> to be a valuable tool in your development workflow. Whether you’re building a new CLI tool or just need a template to get started, this script is designed to make your life easier. So go ahead, give it a try, and see what you can create!</p>]]></content><author><name></name></author><category term="python" /><category term="python3" /><category term="windows" /><category term="mac" /><category term="linux" /><category term="CLI" /><category term="argparse" /><category term="logging" /><category term="standard library" /><summary type="html"><![CDATA[Introducing command.py A Flexible CLI Template using just the standard library]]></summary></entry><entry><title type="html">How to install Python</title><link href="notesofcliff.github.io/python/python3/windows/mac/linux/2024/09/27/installing-python.html" rel="alternate" type="text/html" title="How to install Python" /><published>2024-09-27T18:21:56+00:00</published><updated>2024-09-27T18:21:56+00:00</updated><id>notesofcliff.github.io/python/python3/windows/mac/linux/2024/09/27/installing-python</id><content type="html" xml:base="notesofcliff.github.io/python/python3/windows/mac/linux/2024/09/27/installing-python.html"><![CDATA[<p><strong>Installing the Latest Version of Python: A Step-by-Step Guide for Beginners</strong></p>

<p>Python is a popular and versatile programming language that has become an essential tool for various industries, including web development, data science, and artificial intelligence. If you’re new to Python, installing the latest version can seem daunting, but don’t worry, we’ve got you covered. In this article, we’ll walk you through the process of installing Python on Windows, Linux, and MacOS.</p>

<p><strong>Prerequisites</strong></p>

<p>Before you begin, make sure you have:</p>

<ul>
  <li>A computer with a compatible operating system (Windows, Linux, or MacOS)</li>
  <li>A reliable internet connection</li>
</ul>

<p><strong>Installing Python on Windows</strong></p>

<ol>
  <li><strong>Download the latest version of Python</strong>: Go to the official Python website’s Windows download page (<a href="https://www.python.org/downloads/windows/">www.python.org</a>), Click on the link that says “Latest Python 3 Release - Python 3.x.x” where x.x is the latest minor and patch version of Python.</li>
  <li><strong>Run the installer</strong>: Once the download is complete, run the installer file (usually named “python-3.x.x.exe”). Follow the prompts to select the installation location and other options.</li>
  <li><strong>Choose the installation type</strong>: You’ll be presented with two installation options:
    <ul>
      <li><strong>Add Python 3.x to PATH</strong>: This option adds Python to your system’s PATH environment variable, allowing you to run Python from anywhere.</li>
      <li><strong>Install for me</strong>: This option installs Python in a specific location, such as “C:\Python39” (for Python 3.9.5). Choose the option that suits your needs.</li>
    </ul>
  </li>
</ol>

<p><strong>Installing Python on Linux</strong></p>

<ol>
  <li><strong>Update your package list</strong>: Open a terminal or command prompt and run the command <code class="language-plaintext highlighter-rouge">sudo apt-get update</code> (for Ubuntu-based distributions) or <code class="language-plaintext highlighter-rouge">sudo yum update</code> (for Red Hat-based distributions).</li>
  <li><strong>Install Python</strong>: Run the command <code class="language-plaintext highlighter-rouge">sudo apt-get install python3</code> (for Ubuntu-based distributions) or <code class="language-plaintext highlighter-rouge">sudo yum install python3</code> (for Red Hat-based distributions).</li>
  <li><strong>Verify the installation</strong>: Run <code class="language-plaintext highlighter-rouge">python3 --version</code> to verify that Python is installed correctly.</li>
</ol>

<p><strong>Installing Python on MacOS</strong></p>

<ol>
  <li><strong>Install Python using Homebrew</strong>: Open the Terminal app and run the command <code class="language-plaintext highlighter-rouge">brew install python</code>.</li>
  <li><strong>Verify the installation</strong>: Run <code class="language-plaintext highlighter-rouge">python3 --version</code> to verify that Python is installed correctly.</li>
</ol>

<p><strong>Verifying the Installation</strong></p>

<p>After installing Python, you can verify that it’s working correctly by running the following commands:</p>

<ul>
  <li>Windows: <code class="language-plaintext highlighter-rouge">python --version</code></li>
  <li>Linux: <code class="language-plaintext highlighter-rouge">python3 --version</code></li>
  <li>MacOS: <code class="language-plaintext highlighter-rouge">python3 --version</code></li>
</ul>

<p><strong>Conclusion</strong></p>

<p>Congratulations! You’ve successfully installed the latest version of Python on your computer. Start exploring the world of Python programming and enjoy the journey!</p>]]></content><author><name></name></author><category term="python" /><category term="python3" /><category term="windows" /><category term="mac" /><category term="linux" /><summary type="html"><![CDATA[Installing the Latest Version of Python: A Step-by-Step Guide for Beginners]]></summary></entry><entry><title type="html">Welcome to Notes of Cliff!</title><link href="notesofcliff.github.io/welcome/introduction/2024/06/05/welcome-Notes-Of-Cliff.html" rel="alternate" type="text/html" title="Welcome to Notes of Cliff!" /><published>2024-06-05T19:38:56+00:00</published><updated>2024-06-05T19:38:56+00:00</updated><id>notesofcliff.github.io/welcome/introduction/2024/06/05/welcome-Notes-Of-Cliff</id><content type="html" xml:base="notesofcliff.github.io/welcome/introduction/2024/06/05/welcome-Notes-Of-Cliff.html"><![CDATA[<p>Hello and welcome to my new blog’s first post.</p>

<p>I will keep it short for this first post so I can start in on my plans for this blog.</p>

<p>Stay tuned to this blog or follow our <a href="/feed.xml">RSS feed</a> because we have some big plans
coming soon.</p>]]></content><author><name></name></author><category term="welcome" /><category term="introduction" /><summary type="html"><![CDATA[Hello and welcome to my new blog’s first post.]]></summary></entry></feed>