Ramblingshttps://clamytoe.github.io/2021-05-14T07:03:00-05:00of an autodidact...More advanced Dataclass sorting2021-05-14T07:03:00-05:002021-05-14T07:03:00-05:00Martin Uribetag:clamytoe.github.io,2021-05-14:/articles/2021/May/14/"more-advanced-dataclass-sorting"<p>A useful tip for making your dataclasses more useful</p><h1>More advanced Dataclass sorting</h1> <p>While checking my email this morning I came across another useful <a href="proxy.php?url=https://codechalleng.es/tips/dataclasses-and-order">Pybites</a> tip.</p> <p><img alt="dataclass-order" src="proxy.php?url=https://clamytoe.github.io/images/python-dataclasses-and-order.png"></p> <p>It was a great little tip, but I wondered, "what if I wanted to sort by bite level?"</p> <h2>Pretty up the output</h2> <p>I know it's just an example code, but I like to make my output a bit more readable. A simple loop of the results is all it takes:</p> <div class="highlight"><pre><span></span><code><span class="k">for</span> <span class="n">bite</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">bites</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">bite</span><span class="p">)</span> </code></pre></div> <p>Output:</p> <div class="highlight"><pre><span></span><code><span class="err">Bite(number=11, title=&#39;Enrich a class&#39;, level=&lt;BiteLevel.ADVANCED: 4&gt;)</span> <span class="err">Bite(number=21, title=&#39;Query a nested DS&#39;, level=&lt;BiteLevel.BEGINNER: 2&gt;)</span> <span class="err">Bite(number=25, title=&#39;No promo twice&#39;, level=&lt;BiteLevel.INTERMEDIATE: 3&gt;)</span> </code></pre></div> <p>Ah, much better!</p> <h2>Using a <em>sort_index</em></h2> <p>As you can see, the bites are being sorted by their number, but I want to see them sorted by difficulty. The first thing I did was add a <em>sort_index</em> to the dataclass and set it to the property that I wanted to sort by, <strong>level</strong>:</p> <div class="highlight"><pre><span></span><code><span class="nd">@dataclass</span><span class="p">(</span><span class="n">order</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Bite</span><span class="p">:</span> <span class="n">sort_index</span><span class="p">:</span> <span class="nb">int</span> <span class="n">number</span><span class="p">:</span> <span class="nb">int</span> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="n">level</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">BiteLevel</span><span class="o">.</span><span class="n">BEGINNER</span> <span class="k">def</span> <span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">sort_index</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">level</span> </code></pre></div> <p>Output:</p> <div class="highlight"><pre><span></span><code><span class="err">Traceback (most recent call last):</span> <span class="err"> File &quot;enum_play.py&quot;, line 28, in &lt;module&gt;</span> <span class="err"> Bite(21, &quot;Query a nested DS&quot;),</span> <span class="c">TypeError: __init__() missing 1 required positional argument: &#39;title&#39;</span> </code></pre></div> <p>Uh oh! Although it's complaining about a missing title, the problem is actually that it's looking for the <em>sort_index</em>.</p> <h2>Dataclasses <em>field(init=False)</em></h2> <p>I don't want to initialize that variable, so I make the following change to the <strong>Bite</strong> class:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span><span class="p">,</span> <span class="n">field</span> <span class="o">...</span> <span class="k">class</span> <span class="nc">Bite</span><span class="p">:</span> <span class="n">sort_index</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="n">number</span><span class="p">:</span> <span class="nb">int</span> <span class="o">...</span> </code></pre></div> <p>We first import <strong>field</strong> from dataclasses and then set it to <em>init=False</em> in order to prevent that variable from being initialized.</p> <div class="highlight"><pre><span></span><code><span class="err">Bite(sort_index=&lt;BiteLevel.BEGINNER: 2&gt;, number=21, title=&#39;Query a nested DS&#39;, level=&lt;BiteLevel.BEGINNER: 2&gt;)</span> <span class="err">Bite(sort_index=&lt;BiteLevel.INTERMEDIATE: 3&gt;, number=25, title=&#39;No promo twice&#39;, level=&lt;BiteLevel.INTERMEDIATE: 3&gt;)</span> <span class="err">Bite(sort_index=&lt;BiteLevel.ADVANCED: 4&gt;, number=11, title=&#39;Enrich a class&#39;, level=&lt;BiteLevel.ADVANCED: 4&gt;)</span> </code></pre></div> <p>Um, ok better but I don't want to see sort_index being displayed in the output.</p> <h2>Dataclasses <em>field(repr=False)</em></h2> <p>In order to prevent <em>sort_index</em> from being displayed, we will have to set the <em>repr</em> to <code>False</code>.</p> <div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Bite</span><span class="p">:</span> <span class="n">sort_index</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="o">...</span> </code></pre></div> <p>Output:</p> <div class="highlight"><pre><span></span><code><span class="err">Bite(number=21, title=&#39;Query a nested DS&#39;, level=&lt;BiteLevel.BEGINNER: 2&gt;)</span> <span class="err">Bite(number=25, title=&#39;No promo twice&#39;, level=&lt;BiteLevel.INTERMEDIATE: 3&gt;)</span> <span class="err">Bite(number=11, title=&#39;Enrich a class&#39;, level=&lt;BiteLevel.ADVANCED: 4&gt;)</span> </code></pre></div> <p>Much better!</p> <h2>Dataclasses <em>frozen</em></h2> <p>Looking much better! But what if I wanted to make the dataclass so that it could not be changed in the code? That's where <em>frozen</em> comes in:</p> <div class="highlight"><pre><span></span><code><span class="nd">@dataclass</span><span class="p">(</span><span class="n">order</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">frozen</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Bite</span><span class="p">:</span> <span class="n">sort_index</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="o">...</span> </code></pre></div> <p>Output:</p> <div class="highlight"><pre><span></span><code><span class="err">Traceback (most recent call last):</span> <span class="err"> File &quot;enum_play.py&quot;, line 25, in &lt;module&gt;</span> <span class="err"> Bite(11, &quot;Enrich a class&quot;, BiteLevel.ADVANCED),</span> <span class="err"> File &quot;&lt;string&gt;&quot;, line 6, in __init__</span> <span class="err"> File &quot;enum_play.py&quot;, line 21, in __post_init__</span> <span class="err"> self.sort_index = self.level</span> <span class="err"> File &quot;&lt;string&gt;&quot;, line 4, in __setattr__</span> <span class="err">dataclasses.FrozenInstanceError: cannot assign to field &#39;sort_index&#39;</span> </code></pre></div> <p>Ugh, well at least we know it works!</p> <h2>Setting class variables with <em><strong>setattr</strong></em></h2> <p>It's complaining because we are setting the <em>index_value</em> in <code>__post_init__</code>. In order to get around it, we'll have to set that value a different way.</p> <div class="highlight"><pre><span></span><code><span class="o">...</span> <span class="k">def</span> <span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">object</span><span class="o">.</span><span class="fm">__setattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;sort_index&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">level</span><span class="p">)</span> </code></pre></div> <p>Output:</p> <div class="highlight"><pre><span></span><code><span class="err">Bite(number=21, title=&#39;Query a nested DS&#39;, level=&lt;BiteLevel.BEGINNER: 2&gt;)</span> <span class="err">Bite(number=25, title=&#39;No promo twice&#39;, level=&lt;BiteLevel.INTERMEDIATE: 3&gt;)</span> <span class="err">Bite(number=11, title=&#39;Enrich a class&#39;, level=&lt;BiteLevel.ADVANCED: 4&gt;)</span> </code></pre></div> <p>There we go!</p> <h2>Conclusion</h2> <p>As you can see, working with dataclasses isn't that hard at all and will save you from having to type too much boilerplate code.</p> <p>Final code:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span><span class="p">,</span> <span class="n">field</span> <span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">IntEnum</span> <span class="k">class</span> <span class="nc">BiteLevel</span><span class="p">(</span><span class="n">IntEnum</span><span class="p">):</span> <span class="n">BEGINNER</span> <span class="o">=</span> <span class="mi">2</span> <span class="n">INTERMEDIATE</span> <span class="o">=</span> <span class="mi">3</span> <span class="n">ADVANCED</span> <span class="o">=</span> <span class="mi">4</span> <span class="nd">@dataclass</span><span class="p">(</span><span class="n">order</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">frozen</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Bite</span><span class="p">:</span> <span class="n">sort_index</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="n">number</span><span class="p">:</span> <span class="nb">int</span> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="n">level</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">BiteLevel</span><span class="o">.</span><span class="n">BEGINNER</span> <span class="k">def</span> <span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">object</span><span class="o">.</span><span class="fm">__setattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;sort_index&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">level</span><span class="p">)</span> <span class="n">bites</span> <span class="o">=</span> <span class="p">[</span> <span class="n">Bite</span><span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="s2">&quot;Enrich a class&quot;</span><span class="p">,</span> <span class="n">BiteLevel</span><span class="o">.</span><span class="n">ADVANCED</span><span class="p">),</span> <span class="n">Bite</span><span class="p">(</span><span class="mi">21</span><span class="p">,</span> <span class="s2">&quot;Query a nested DS&quot;</span><span class="p">),</span> <span class="n">Bite</span><span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="s2">&quot;No promo twice&quot;</span><span class="p">,</span> <span class="n">BiteLevel</span><span class="o">.</span><span class="n">INTERMEDIATE</span><span class="p">),</span> <span class="p">]</span> <span class="k">for</span> <span class="n">bite</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">bites</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">bite</span><span class="p">)</span> </code></pre></div> <p>If you would like to learn more about Dataclasses, check out the <a href="proxy.php?url=https://docs.python.org/3/library/dataclasses.html">documentation</a>.</p>Troubleshooting Connectivity Issues2020-08-10T11:11:00-05:002020-08-10T11:11:00-05:00Martin Uribetag:clamytoe.github.io,2020-08-10:/articles/2020/Aug/10/troubleshooting-connectivity-issues<p>Had issues connecting to FTP server running on Windows 10</p><h1>Troubleshooting Connectivity Issues</h1> <p>My son likes to live stream on Youtube and was in the middle of one the other day when his phone ran out of space. He asked me to backup his images and videos for him so that he could make some room.</p> <h2>Some Background</h2> <p>I've been an avid Linux user for ages so I've gotten used to not having to use iTunes. I recently made the move back to Windows 10, but I detest having to use iTunes to do anything on my phone, so I don't even have it installed.</p> <p>Back when I was on Linux, I had found <a href="proxy.php?url=https://apps.apple.com/us/app/ftpmanager-pro/id522627917">FTPManager Pro</a> in the Apple app store, which would allow me to start an FTP server so that I could just use <a href="proxy.php?url=https://filezilla-project.org/download.php">Filezilla</a> to upload audio files to my phone as well as allow me to copy my images and videos to my network storage device.</p> <h2>Attacking the Problem</h2> <p>I thought, no problem. I already had Filezilla installed, so I fired it up and connected. Unfortunately, those images and videos did not show up. FTPManager has its own directory that you can access, but Apple apparently won't allow it to access the rest of the filesystem.</p> <p>My next thought was to just run an FTP server on my computer, and use FTPManager to transfer the files, because from within the app the media files in question are available; just not to its built-in FTP server. So I downloaded <a href="proxy.php?url=https://filezilla-project.org/download.php?type=server">Filezilla's FTP server</a> utility. After a quick run through the settings, creating a user and home directory, I fired it up.</p> <p>Back on the phone, I setup the new server and attempted to connect to it. No-Go. The phone kept reporting that the server was not reachable. I double, tripple-checked all of the settings and everything looked good. I fired up Filezilla on my computer and connected to localhost on port 21 and I was able to get in!</p> <h2>The Problem</h2> <p>Back on the phone, I found a way to diagnose connection issues. Under <em>Network Diagnostics</em> it listed the following entries:</p> <ul> <li>Resolve Host</li> <li>Ping Host</li> <li>Connect Port</li> </ul> <p>The <em>Resolve Host</em> was the IP address that I had entered for my machine. I opened up a command prompt on one of my kids computers and attempted to ping my machine. Nothing, 100% packet loss...</p> <p>So with no connectivity between my phone and my computer and not being able to ping my computer from another computer, it's easy to conclude that the issue is with my computer blocking the traffic.</p> <h2>Firewall: Custom Rule</h2> <p>I remembered that at work, while setting up some Samba mounts on a Windows machine, I had run into similar problems because the server could not ping the machines. I had to go into the <em>Firewall</em> settings and enable ICMP ping on IPv4, we don't use IPv6 so I didn't see a need to enable it on that protocol. I found this really good <a href="proxy.php?url=https://www.osradar.com/how-to-enable-and-disable-ping-icmp-in-windows-10-firewall/">article</a> on how to do it and was now able to ping my machine in no time.</p> <h2>Firewall: Program Rule</h2> <p>Although FTPManager could now ping my machine, it still could not connect to the FTP server. I went back into the Firewall settings and did the same as above for ICMP, but this time instead of creating a new <strong>Custom</strong> command, I selected <strong>Program</strong>.</p> <p><img alt="program" src="proxy.php?url=https://clamytoe.github.io/images/fw-program.png"></p> <p>On the <strong>Program</strong> screen, I selected <strong>This program path</strong> and clicked on the <em>Browse</em>:</p> <p><img alt="path" src="proxy.php?url=https://clamytoe.github.io/images/fw-path.png"></p> <p>From within the <strong>Open</strong> dialog windows, I navigated to where the <em>Filezilla Server</em> was installed and selected it:</p> <p><img alt="filezilla" src="proxy.php?url=https://clamytoe.github.io/images/fw-filezilla.png"></p> <p>From the <strong>Action</strong> dialog window, I left it at the default <em>Allow the connection</em>:</p> <p><img alt="action" src="proxy.php?url=https://clamytoe.github.io/images/fw-action.png"></p> <p>I left the <strong>Profile</strong> page as default:</p> <p><img alt="profile" src="proxy.php?url=https://clamytoe.github.io/images/fw-profile.png"></p> <p>I then gave it a generic name and clicked on <em>Finish</em>:</p> <p><img alt="name" src="proxy.php?url=https://clamytoe.github.io/images/fw-name.png"></p> <p>Here you can see that the new rul is running and is active:</p> <p><img alt="rule" src="proxy.php?url=https://clamytoe.github.io/images/fw-rule.png"></p> <p>With that final addition, FTPManager was able to connect and I was able to proceeded with the backing up the media files.</p> <h2>Conclusion</h2> <p>Don't give up when you run into problems, there's always a way!</p>Benchmarking Python Code2020-05-04T08:56:00-05:002020-05-04T08:56:00-05:00Martin Uribetag:clamytoe.github.io,2020-05-04:/articles/2020/May/04/benchmarking-python-code<p>How much faster is one implementation compared to another?</p><h1>Need to compare which code is faster, read on to find out how</h1> <p>A friend of mine, <a href="proxy.php?url=https://dev.to/mridubhatnagar">Mridu Bhatnagar</a>, has been posting some really cool articles explaining how she's gone about solving some <a href="proxy.php?url=https://leetcode.com/">LeetCode</a> challenges. They are all interesting, but due to time constraints, I have only attempted to see if I can do a couple of them.</p> <h2>Move Zeroes</h2> <p>One of the first that I attempted to do was <a href="proxy.php?url=https://dev.to/mridubhatnagar/day-3-move-zeroes-1lbb">Move Zeroes</a>. I thought that I was being clever, and sure enough, my initial time test seemed to indicate the same. That is, until <a href="proxy.php?url=https://www.linkedin.com/in/sps22/">Suraj Sharma</a> pointed out to me that once the numbers got bigger, that my implementation would be slower.</p> <p>I decided to see if it was true, and sure enough, once I increased the starting size number, mine was way slower!! At any rate, Suraj really liked how I benchmarked the codes, so I decided to share it here.</p> <p>I wanted to write a script that would indicate, whose code it was currently running with the results to the right of it. In order to keep it <a href="proxy.php?url=https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a>, I decided to use decorators.</p> <p>Here is what the results look like:</p> <div class="highlight"><pre><span></span><code><span class="go">...</span> <span class="go">Working with 8 zeroes out of 100 numbers</span> <span class="go"> mridu: 0.0000221729</span> <span class="go">clamytoe: 0.0000185966</span> <span class="go"> striker: 0.0000629425</span> <span class="go"> ...</span> <span class="go">Working with 10003 zeroes out of 100,000 numbers</span> <span class="go"> mridu: 8.4343523979</span> <span class="go">clamytoe: 8.0974295139</span> <span class="go"> striker: 0.0438303947</span> </code></pre></div> <p>Full code can be found in this <a href="proxy.php?url=https://gist.github.com/clamytoe/1fcd47d6f1b5db4d2657623947cd5646">gist</a>.</p> <h3>Decorator: ID Checker</h3> <p>Part of the requirements for the challenge, was that the same object needed to be modified and not just create a new one. I'm not really sure if this is the way to do it, but I decided that checking to see if the <code>id</code> of the before and after objects were the same was the way to go.</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">id_checker</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">nums</span><span class="p">):</span> <span class="n">before</span> <span class="o">=</span> <span class="nb">id</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="n">func</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="n">after</span> <span class="o">=</span> <span class="nb">id</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="k">if</span> <span class="n">DEBUG</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="s1">&#39;before&#39;</span><span class="si">:</span><span class="s2">&gt;8</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">before</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="s1">&#39;after&#39;</span><span class="si">:</span><span class="s2">&gt;8</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">after</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="n">before</span> <span class="o">!=</span> <span class="n">after</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;The nums list is not the same!&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="n">wrapper</span> </code></pre></div> <p>Since our functions all only passed the <code>nums</code> variable, I decided to just hard code it in instead of using <code>*args</code>. In hindsight, I should have used <code>*args</code>. Sometimes while developing code, I like to throw in a <code>DEBUG</code> global variable to turn on/off print statements, so those can be ignored.</p> <h3>Decorator: Timer</h3> <p>I decided that the easiest way to keep track of whose code was being benchmarked, would be to name each of our functions, with our nicknames. Then it would be simple enough to extract the function name from within the code.</p> <p>To time the functions, I created this <code>timer</code> decorator. It's a pretty straight forward timer. The only unique part is how I extracted the function name.</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">timer</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">nums</span><span class="p">):</span> <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">()</span> <span class="n">func</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="n">stop</span> <span class="o">=</span> <span class="n">time</span><span class="p">()</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">:</span><span class="s2">&gt;8</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">stop</span> <span class="o">-</span> <span class="n">start</span><span class="si">:</span><span class="s2">.10f</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="n">wrapper</span> </code></pre></div> <p>If you are not familiar with formatting strings within <em>f-strings</em>, the <code>:&gt;8</code> simply means to right align it and use 8 spaces. The <code>:.10f</code> just means that it's a float value and that I want it to use 10 digits of accuracy.</p> <h3>Decorating the Functions</h3> <p>Each of the separate functions were decorated in the same manner. To try and keep this discussion short, I'll only illustrate mine.</p> <div class="highlight"><pre><span></span><code><span class="nd">@id_checker</span> <span class="nd">@timer</span> <span class="k">def</span> <span class="nf">clamytoe</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">nums</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span> <span class="k">break</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">count</span><span class="p">):</span> <span class="n">nums</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span> </code></pre></div> <p>The decorators are applied from bottom up. Meaning that the <code>@timer</code> decorator is the first to be ran, followed by the <code>@id_checker</code>. If you are not familiar with decorators, <a href="proxy.php?url=https://dbader.org/blog/python-decorators">Dan Bader</a> wrote a nice article on them.</p> <h3>Generating the Numbers</h3> <p>Since the size of the number played such a significant part in how the code runs, I decided to create a small little number generator.</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">generate_nums</span><span class="p">(</span><span class="n">size</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span> <span class="n">numbers</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span> <span class="n">numbers</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">sample</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">1</span><span class="p">))</span> <span class="k">return</span> <span class="n">numbers</span> </code></pre></div> <p>I decided to go with <code>list</code>'s built in <code>.extend</code> method in order to add the iterable generated by <code>random.sample</code> to my list of numbers.</p> <h3>Test Function</h3> <p>Originally, my intent was to create a "testing"/"benchmarking" function for each piece of code, but ended up being about to run them all from the one. I just forgot to rename it...</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">test_one</span><span class="p">(</span><span class="n">original</span><span class="p">):</span> <span class="n">nums1</span> <span class="o">=</span> <span class="n">deepcopy</span><span class="p">(</span><span class="n">original</span><span class="p">)</span> <span class="n">nums2</span> <span class="o">=</span> <span class="n">deepcopy</span><span class="p">(</span><span class="n">original</span><span class="p">)</span> <span class="n">nums3</span> <span class="o">=</span> <span class="n">deepcopy</span><span class="p">(</span><span class="n">original</span><span class="p">)</span> <span class="n">mridu</span><span class="p">(</span><span class="n">nums1</span><span class="p">)</span> <span class="n">clamytoe</span><span class="p">(</span><span class="n">nums2</span><span class="p">)</span> <span class="n">striker</span><span class="p">(</span><span class="n">nums3</span><span class="p">)</span> <span class="k">try</span><span class="p">:</span> <span class="k">assert</span> <span class="n">nums1</span> <span class="o">==</span> <span class="n">nums2</span> <span class="o">==</span> <span class="n">nums3</span> <span class="k">except</span> <span class="ne">AssertionError</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Outputs do not match!&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;original: </span><span class="se">\n</span><span class="si">{</span><span class="n">original</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;mridu: </span><span class="se">\n</span><span class="si">{</span><span class="n">nums1</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;clamytoe: </span><span class="se">\n</span><span class="si">{</span><span class="n">nums2</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;striker: </span><span class="se">\n</span><span class="si">{</span><span class="n">nums3</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> </code></pre></div> <p>I started off by making copies of the original number and then used the copies to make sure that all three implementations were returning the same numbers.</p> <h2>Benchmarking</h2> <p>Phew, finally getting to kicking it all off! The following bit of code runs the benchmark and generates the report.</p> <div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span> <span class="n">sizes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">1_000</span><span class="p">,</span> <span class="mi">10_000</span><span class="p">,</span> <span class="mi">100_000</span><span class="p">]</span> <span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">sizes</span><span class="p">:</span> <span class="n">original</span> <span class="o">=</span> <span class="n">generate_nums</span><span class="p">(</span><span class="n">size</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Working with </span><span class="si">{</span><span class="n">original</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="s2"> zeroes out of </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">original</span><span class="p">)</span><span class="si">:</span><span class="s2">,</span><span class="si">}</span><span class="s2"> numbers&quot;</span><span class="p">)</span> <span class="n">test_one</span><span class="p">(</span><span class="n">original</span><span class="p">)</span> </code></pre></div> <p>I first start with a list of the number lengths that I want to benchmark against. I then iterate over that list and using the <code>generate_nums()</code> function, generate the numbers. This is followed by printing some information of how many zeroes are actually in the number and the length of the number being used. Then finally the actual benchmark is performed.</p> <p>This is repeated until all of the sizes have been benchmarked.</p> <h2>A Better Way to Benchmark Code</h2> <p>I was working through another one of the challenges that Mridu had completed, <a href="proxy.php?url=https://dev.to/mridubhatnagar/day-16-reverse-words-in-a-string-iii-jn3">Reverse words in a string III</a>. When it came time to compare my implementation with hers, I decided to use the <code>timeit</code> module this time around.</p> <p>Python comes with the <a href="proxy.php?url=https://docs.python.org/3/library/timeit.html"><code>timeit</code></a> module in the standard library. I don't like that you have to pass the code that you want to benchmark to it as a string, but it works pretty well. It times how long a particular piece of code takes to execute a certain number of times, and returns the fastest time.</p> <p>Here is the script in it's entirety:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">timeit</span> <span class="kn">import</span> <span class="n">timeit</span> <span class="n">setup_code</span> <span class="o">=</span> <span class="s2">&quot;&quot;&quot;</span> <span class="s2">s = &quot;Let&#39;s take LeetCode contest&quot;</span> <span class="s2">&quot;&quot;&quot;</span> <span class="n">mridu</span> <span class="o">=</span> <span class="s2">&quot;&quot;&quot;</span> <span class="s2">def reverseWords(s: str) -&gt; str:</span> <span class="s2"> s = s.split()</span> <span class="s2"> s1 = &#39;&#39;</span> <span class="s2"> for x in range(0, len(s)):</span> <span class="s2"> if x == len(s) - 1:</span> <span class="s2"> s1 += s[x][::-1]</span> <span class="s2"> else:</span> <span class="s2"> s1 += s[x][::-1] + &#39; &#39;</span> <span class="s2"> return s1</span> <span class="s2">reverseWords(s)</span> <span class="s2">&quot;&quot;&quot;</span> <span class="n">clamytoe</span> <span class="o">=</span> <span class="s2">&quot;&quot;&quot;</span> <span class="s2">def reverse_words(s):</span> <span class="s2"> return &quot; &quot;.join([word[::-1] for word in s.split()])</span> <span class="s2">reverse_words(s)</span> <span class="s2">&quot;&quot;&quot;</span> <span class="n">codes</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;clamytoe&quot;</span><span class="p">:</span> <span class="n">clamytoe</span><span class="p">,</span> <span class="s2">&quot;mridu&quot;</span><span class="p">:</span> <span class="n">mridu</span> <span class="p">}</span> <span class="k">for</span> <span class="n">code</span> <span class="ow">in</span> <span class="n">codes</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">timeit</span><span class="p">(</span><span class="n">stmt</span><span class="o">=</span><span class="n">codes</span><span class="p">[</span><span class="n">code</span><span class="p">],</span> <span class="n">setup</span><span class="o">=</span><span class="n">setup_code</span><span class="p">))</span> </code></pre></div> <p>The results from running it, look like this:</p> <div class="highlight"><pre><span></span><code><span class="go">clamytoe 1.9202047239996318</span> <span class="go">mridu 3.630925637000473</span> </code></pre></div> <p>As you can see, in the <code>timeit</code> function, I'm passing <code>stmt</code> and <code>setup</code> variables. The <code>stmt</code> is the actual code to benchmark, while <code>setup</code> is used to setup any code that will be required for it to run properly. The values passed, just need to be strings, so the easiest thing to do is to just enclose your code in triple double quotes and set it to a variable.</p> <p>You can also pass a <code>number</code> variable that allows you to modify how many times the code is ran, with the default being 1,000,000.</p> <p>I didn't see a way of using decorators here, so I simply used a dictionary to do the identifying for me.</p> <h2>Summary</h2> <p>I hope that you will now know how to benchmark any of your code. The first method, using <code>time</code> is more suited towards larger code, while <code>timeit</code> is good for shorter snippets of code. The <em>timeit</em> module can do a few more things. I suggest reading up on it if you're curious.</p> <p>Until next time, keep on coding!</p> <h2>Update</h2> <p>I've always thought that having to enclose your code in triple double quotes was kind of clunky. Well, my buddy <a href="proxy.php?url=https://harrisonmorgan.dev/">Harrison Morgan</a> just demonstrated to me that you don't have to! The same thing can be done with a decorator and a <code>lambda</code> expression!</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span> <span class="kn">from</span> <span class="nn">timeit</span> <span class="kn">import</span> <span class="n">timeit</span> <span class="k">def</span> <span class="nf">test_performance</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="n">result</span> <span class="o">=</span> <span class="n">timeit</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">))</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">:</span><span class="s2">&gt;8</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">result</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="n">wrapper</span> <span class="nd">@test_performance</span> <span class="k">def</span> <span class="nf">mridu</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> <span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">split</span><span class="p">()</span> <span class="n">s1</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)):</span> <span class="k">if</span> <span class="n">x</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span> <span class="n">s1</span> <span class="o">+=</span> <span class="n">s</span><span class="p">[</span><span class="n">x</span><span class="p">][::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">else</span><span class="p">:</span> <span class="n">s1</span> <span class="o">+=</span> <span class="n">s</span><span class="p">[</span><span class="n">x</span><span class="p">][::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s1">&#39; &#39;</span> <span class="k">return</span> <span class="n">s1</span> <span class="nd">@test_performance</span> <span class="k">def</span> <span class="nf">clamytoe</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot; &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">word</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">s</span><span class="o">.</span><span class="n">split</span><span class="p">()])</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span> <span class="n">s</span> <span class="o">=</span> <span class="s2">&quot;Let&#39;s take LeetCode contest&quot;</span> <span class="n">clamytoe</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="n">mridu</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> </code></pre></div> <p>And the results:</p> <div class="highlight"><pre><span></span><code><span class="go">clamytoe: 1.9616992009999876</span> <span class="go"> mridu: 3.411077960000057</span> </code></pre></div> <p>How awesome is that? This is why the Python community is so great! Everyone is willing to freely share in their knowledge and help each other, thanks again Harrison!</p>Advanced type annotations2020-04-19T12:20:00-05:002020-04-19T12:20:00-05:00Martin Uribetag:clamytoe.github.io,2020-04-19:/articles/2020/Apr/19/advanced-type-annotations<p>Some concrete examples on how to type hint more advanced class objects</p><h1>How to add type annotations to Python dataclasses</h1> <p>During the COVID19 outbreak, a lof of companies are either giving away free books, tutorials, or access their training material. One such company is Pluralsight. For the whole month of April 2020, they have given everyone free access to their complete library! The content is just awesome, some of the best I've seen. I have been working through their <a href="proxy.php?url=https://app.pluralsight.com/library/courses/python-beyond-basics">Python - Beyond the Basics</a> when I came across their section on <em>Properties and Class Methods</em>.</p> <h2>Sample program</h2> <p>Their sample program was pretty cool:</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">og-shipping.py</span></figcaption> <div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">iso6346</span> <span class="k">class</span> <span class="nc">ShippingContainer</span><span class="p">:</span> <span class="n">HEIGHT_FT</span> <span class="o">=</span> <span class="mf">8.5</span> <span class="n">WIDTH_FT</span> <span class="o">=</span> <span class="mf">8.0</span> <span class="n">next_serial</span> <span class="o">=</span> <span class="mi">1337</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_make_bic_code</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="p">):</span> <span class="k">return</span> <span class="n">iso6346</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">owner_code</span><span class="o">=</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">serial</span><span class="p">)</span><span class="o">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span> <span class="nd">@classmethod</span> <span class="k">def</span> <span class="nf">_get_next_serial</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span> <span class="n">result</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">next_serial</span> <span class="bp">cls</span><span class="o">.</span><span class="n">next_serial</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="n">result</span> <span class="nd">@classmethod</span> <span class="k">def</span> <span class="nf">create_empty</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">return</span> <span class="bp">cls</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">contents</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="nd">@classmethod</span> <span class="k">def</span> <span class="nf">create_with_items</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">items</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">return</span> <span class="bp">cls</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">contents</span><span class="o">=</span><span class="nb">list</span><span class="p">(</span><span class="n">items</span><span class="p">),</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">contents</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">contents</span> <span class="o">=</span> <span class="n">contents</span> <span class="bp">self</span><span class="o">.</span><span class="n">length_ft</span> <span class="o">=</span> <span class="n">length_ft</span> <span class="bp">self</span><span class="o">.</span><span class="n">bic</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_make_bic_code</span><span class="p">(</span> <span class="n">owner_code</span><span class="o">=</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="o">=</span><span class="n">ShippingContainer</span><span class="o">.</span><span class="n">_get_next_serial</span><span class="p">())</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">volume_ft3</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="n">ShippingContainer</span><span class="o">.</span><span class="n">HEIGHT_FT</span> <span class="o">*</span> <span class="n">ShippingContainer</span><span class="o">.</span><span class="n">WIDTH_FT</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">length_ft</span> <span class="k">class</span> <span class="nc">RefrigeratedShippingContainer</span><span class="p">(</span><span class="n">ShippingContainer</span><span class="p">):</span> <span class="n">MAX_CELSIUS</span> <span class="o">=</span> <span class="mf">4.0</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_make_bic_code</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="p">):</span> <span class="k">return</span> <span class="n">iso6346</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">owner_code</span><span class="o">=</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">serial</span><span class="p">)</span><span class="o">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">6</span><span class="p">),</span> <span class="n">category</span><span class="o">=</span><span class="s1">&#39;R&#39;</span><span class="p">)</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_c_to_f</span><span class="p">(</span><span class="n">celsius</span><span class="p">):</span> <span class="k">return</span> <span class="n">celsius</span> <span class="o">*</span> <span class="mi">9</span><span class="o">/</span><span class="mi">5</span> <span class="o">+</span> <span class="mi">32</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_f_to_c</span><span class="p">(</span><span class="n">fahrenheit</span><span class="p">):</span> <span class="k">return</span> <span class="p">(</span><span class="n">fahrenheit</span> <span class="o">-</span> <span class="mi">32</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span><span class="o">/</span><span class="mi">9</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">contents</span><span class="p">,</span> <span class="n">celsius</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">contents</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">celsius</span> <span class="o">=</span> <span class="n">celsius</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_celsius</span> <span class="nd">@celsius</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">if</span> <span class="n">value</span> <span class="o">&gt;</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">MAX_CELSIUS</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Temperature too hot!&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_celsius</span> <span class="o">=</span> <span class="n">value</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">fahrenheit</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">_c_to_f</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">celsius</span><span class="p">)</span> <span class="nd">@fahrenheit</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">fahrenheit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">celsius</span> <span class="o">=</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">_f_to_c</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">volume_ft3</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">volume_ft3</span> <span class="o">-</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">FRIDGE_VOLUME_FT3</span> <span class="k">class</span> <span class="nc">HeatedRefrigeratedShippingContainer</span><span class="p">(</span><span class="n">RefrigeratedShippingContainer</span><span class="p">):</span> <span class="n">MIN_CELSIUS</span> <span class="o">=</span> <span class="o">-</span><span class="mf">20.0</span> <span class="nd">@RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">celsius</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">if</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="n">HeatedRefrigeratedShippingContainer</span><span class="o">.</span><span class="n">MIN_CELSIUS</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Temperature too cold!&quot;</span><span class="p">)</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">celsius</span><span class="o">.</span><span class="n">fset</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> </code></pre></div> </figure> <p>As you can see, there is a lot going on in that code.</p> <blockquote> <p>I don't know how you guys learn, but I just can't sit and watch a video; I have to be actually coding along to make it stick.</p> </blockquote> <p>That bit of code seemed simple enough, so I decided to kick it up a notch by using <em>dataclasses</em> and <em>type annotations</em>!</p> <h2>Hell week</h2> <p>Hence my journey into hell week commenced!</p> <p>To make it an even better learning experience, I decided to only read the documentation on <a href="proxy.php?url=https://docs.python.org/3/library/typing.html">type annotations</a>. I lost track of how many hours I spent trying to get it, not only to work, but to get <em>mypy</em> to NOT complain about any part of it.</p> <p>After a few days of pulling out my hair I relaxed that restriction, but to my surprise, there really wasn't that much documentation on how to do what I wanted to do! Mostly every example out there dealt with type hinting normal classes, not dataclasses!</p> <p>In the end I discovered that <em>mypy</em> doesn't support type annotations of <a href="proxy.php?url=https://github.com/python/mypy/issues/220">settable property variables</a>! Oh well, I could not get a clean <em>mypy</em> run in the end, but I definitely learned a lot about type hinting the rest of the code.</p> <h2>What I ended up with</h2> <p>Instead of going step by step over every bit of the code, I'm just going to present it in its entirety and let you soak it in. I'll talk about the parts that gave me the most problems and point out the parts were I learned things.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span></figcaption> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">InitVar</span><span class="p">,</span> <span class="n">dataclass</span><span class="p">,</span> <span class="n">field</span> <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">ClassVar</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Union</span> <span class="kn">import</span> <span class="nn">iso6346</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">ShippingContainer</span><span class="p">:</span> <span class="n">owner_code</span><span class="p">:</span> <span class="nb">str</span> <span class="n">length_ft</span><span class="p">:</span> <span class="nb">float</span> <span class="n">contents</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Union</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Any</span><span class="p">],</span> <span class="nb">str</span><span class="p">]]</span> <span class="n">HEIGHT_FT</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mf">8.5</span> <span class="n">WIDTH_FT</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mf">8.0</span> <span class="n">next_serial</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1337</span> <span class="n">args</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Tuple</span><span class="p">[</span><span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="n">kwargs</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">bic</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_make_bic_code</span><span class="p">(</span> <span class="n">owner_code</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="o">=</span><span class="n">ShippingContainer</span><span class="o">.</span><span class="n">_get_next_serial</span><span class="p">(),</span> <span class="p">)</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_make_bic_code</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="p">):</span> <span class="k">return</span> <span class="n">iso6346</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">owner_code</span><span class="o">=</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">serial</span><span class="p">)</span><span class="o">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span> <span class="nd">@classmethod</span> <span class="k">def</span> <span class="nf">_get_next_serial</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span> <span class="n">result</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">next_serial</span> <span class="bp">cls</span><span class="o">.</span><span class="n">next_serial</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="n">result</span> <span class="nd">@classmethod</span> <span class="k">def</span> <span class="nf">create_empty</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">return</span> <span class="bp">cls</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">contents</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="nd">@classmethod</span> <span class="k">def</span> <span class="nf">create_with_items</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">items</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">return</span> <span class="bp">cls</span><span class="p">(</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">length_ft</span><span class="p">,</span> <span class="n">contents</span><span class="o">=</span><span class="nb">list</span><span class="p">(</span><span class="n">items</span><span class="p">),</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="k">def</span> <span class="nf">_calc_volume</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="n">ShippingContainer</span><span class="o">.</span><span class="n">HEIGHT_FT</span> <span class="o">*</span> <span class="n">ShippingContainer</span><span class="o">.</span><span class="n">WIDTH_FT</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">length_ft</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">volume_ft3</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_calc_volume</span><span class="p">()</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">RefrigeratedShippingContainer</span><span class="p">(</span><span class="n">ShippingContainer</span><span class="p">):</span> <span class="n">celsius</span><span class="p">:</span> <span class="nb">float</span> <span class="n">_celsius</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="n">MAX_CELSIUS</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mf">4.0</span> <span class="n">FRIDGE_VOLUME_FT3</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mi">100</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_make_bic_code</span><span class="p">(</span><span class="n">owner_code</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">serial</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> <span class="k">return</span> <span class="n">iso6346</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> <span class="n">owner_code</span><span class="o">=</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">serial</span><span class="p">)</span><span class="o">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">6</span><span class="p">),</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;R&quot;</span><span class="p">,</span> <span class="p">)</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_c_to_f</span><span class="p">(</span><span class="n">celsius</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span> <span class="k">return</span> <span class="n">celsius</span> <span class="o">*</span> <span class="mi">9</span> <span class="o">/</span> <span class="mi">5</span> <span class="o">+</span> <span class="mi">32</span> <span class="nd">@staticmethod</span> <span class="k">def</span> <span class="nf">_f_to_c</span><span class="p">(</span><span class="n">fahrenheit</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span> <span class="k">return</span> <span class="p">(</span><span class="n">fahrenheit</span> <span class="o">-</span> <span class="mi">32</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">9</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_celsius</span> <span class="k">def</span> <span class="nf">_set_celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="k">if</span> <span class="n">value</span> <span class="o">&gt;</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">MAX_CELSIUS</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Temperature too hot!&quot;</span><span class="p">)</span> <span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span> <span class="s2">&quot;__init__() missing 1 required positional argumentL &#39;celsius&#39;&quot;</span> <span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_celsius</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="nd">@celsius</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_set_celsius</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">fahrenheit</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span> <span class="k">return</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">_c_to_f</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">celsius</span><span class="p">)</span> <span class="nd">@fahrenheit</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">fahrenheit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">celsius</span> <span class="o">=</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">_f_to_c</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">def</span> <span class="nf">_calc_volume</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">_calc_volume</span><span class="p">()</span> <span class="o">-</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">FRIDGE_VOLUME_FT3</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">HeatedRefrigeratedShippingContainer</span><span class="p">(</span><span class="n">RefrigeratedShippingContainer</span><span class="p">):</span> <span class="n">MIN_CELSIUS</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mf">20.0</span> <span class="k">def</span> <span class="nf">_set_celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="k">if</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="n">HeatedRefrigeratedShippingContainer</span><span class="o">.</span><span class="n">MIN_CELSIUS</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Temperature too cold!&quot;</span><span class="p">)</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">_set_celsius</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span> <span class="sa">f</span><span class="s2">&quot;TypeError: __init__() missing 1 required keyword argument: </span><span class="si">{</span><span class="s1">&#39;celsius&#39;</span><span class="si">}</span><span class="s2">&quot;</span> <span class="p">)</span> </code></pre></div> </figure> <h2>Learning points</h2> <p>Let's start at the top.</p> <h3>The class definitions</h3> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span><span class="liquid-tags-code-lines">[Lines 7-17]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">ShippingContainer</span><span class="p">:</span> <span class="n">owner_code</span><span class="p">:</span> <span class="nb">str</span> <span class="n">length_ft</span><span class="p">:</span> <span class="nb">float</span> <span class="n">contents</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Union</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Any</span><span class="p">],</span> <span class="nb">str</span><span class="p">]]</span> <span class="n">HEIGHT_FT</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mf">8.5</span> <span class="n">WIDTH_FT</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mf">8.0</span> <span class="n">next_serial</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1337</span> <span class="n">args</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Tuple</span><span class="p">[</span><span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="n">kwargs</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> </code></pre></div> </figure> <p>This class definition is filled with lots of good nuggets.</p> <ul> <li>Class variables must be annotated with <em>ClassVar</em></li> <li><em>contents</em> is not enforced because of the use of the classmethod <em>create_empty</em>, so it was annotated with <em>Optional</em></li> <li><em>args</em> and <em>kwargs</em> were declared because PyCharm was complaining about them not being there, even though the code will run fine without them</li> <li><em>args</em> are <em>tuples</em>, not a <em>list</em>!</li> <li><em>kwargs</em> are just <em>dicts</em> not a list of them</li> </ul> <h3>__post_init__</h3> <p>In this method we generate the custom <strong>bic</strong> code that's made up from the <em>owner_code</em>, the <em>serial</em> number, and a <em>category</em> code.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span><span class="liquid-tags-code-lines">[Lines 18-22]</span></figcaption> <div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">bic</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_make_bic_code</span><span class="p">(</span> <span class="n">owner_code</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">owner_code</span><span class="p">,</span> <span class="n">serial</span><span class="o">=</span><span class="n">ShippingContainer</span><span class="o">.</span><span class="n">_get_next_serial</span><span class="p">(),</span> <span class="p">)</span> </code></pre></div> </figure> <h3>Volume property</h3> <p>The <em>@classmethods</em> remained the same, so we're not going to discuss those. Things start to get interesting when it comes to the <em>volume_ft3</em> property though.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span><span class="liquid-tags-code-lines">[Lines 41-47]</span></figcaption> <div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">_calc_volume</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="n">ShippingContainer</span><span class="o">.</span><span class="n">HEIGHT_FT</span> <span class="o">*</span> <span class="n">ShippingContainer</span><span class="o">.</span><span class="n">WIDTH_FT</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">length_ft</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">volume_ft3</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_calc_volume</span><span class="p">()</span> </code></pre></div> </figure> <p>As you can see, the logic for the volume calculation was extracted from the property and moved into its own method.</p> <blockquote> <p>This will come in handy later on when this method gets overwritten in another class that inherits from this one.</p> </blockquote> <h3>Inheriting the main class</h3> <p>Now comes the fun parts! When you normally inherit a class, you usually call <em>super()</em> to initialize some of the variables. Not so with <em>dataclasses</em>! This is one point where I was pulling my hair out to figure out how to declare these and it turned out to be simpler than I thought. Just don't!</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span><span class="liquid-tags-code-lines">[Lines 49-55]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">RefrigeratedShippingContainer</span><span class="p">(</span><span class="n">ShippingContainer</span><span class="p">):</span> <span class="n">celsius</span><span class="p">:</span> <span class="nb">float</span> <span class="n">_celsius</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="n">MAX_CELSIUS</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mf">4.0</span> <span class="n">FRIDGE_VOLUME_FT3</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="mi">100</span> </code></pre></div> </figure> <p>The part that gave me the biggest headache was declaring the <strong>required</strong> <em>celcius</em> variable because <em>celsius</em> is also a property value!</p> <p>We also don't want the user to be able to manually set the <em>celsius</em> value, because it has to be within a set range, so the "private" variable <em>_celsius</em> was created.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span><span class="liquid-tags-code-lines">[Lines 52-52]</span></figcaption> <div class="highlight"><pre><span></span><code> <span class="n">_celsius</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> </code></pre></div> </figure> <p>Pay attention to the usage of <em>field</em>. This tells the class constructor not only to not expect a value to be passed during initialization, but also to not display this variable through <em>repr</em>!</p> <h3>Property init values</h3> <p>Here is how you handle <em>init</em> values that are also properties of the class. A more detailed explanation can be found in this wonderful write-up: <a href="proxy.php?url=https://florimond.dev/blog/articles/2018/10/reconciling-dataclasses-and-properties-in-python/">Reconciling Dataclasses and Properties in Python</a></p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span><span class="liquid-tags-code-lines">[Lines 70-83]</span></figcaption> <div class="highlight"><pre><span></span><code> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_celsius</span> <span class="k">def</span> <span class="nf">_set_celsius</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="k">if</span> <span class="n">value</span> <span class="o">&gt;</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">MAX_CELSIUS</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Temperature too hot!&quot;</span><span class="p">)</span> <span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span> <span class="s2">&quot;__init__() missing 1 required positional argumentL &#39;celsius&#39;&quot;</span> <span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_celsius</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> </code></pre></div> </figure> <p>As with the <em>property</em> value in the main class, the logic was also extracted from the <em>property</em> and moved into its own method. I also had to add a <em>try/except</em> clause to it because for some reason, if the <em>celcius</em> value was not passed during initialization, it would try and initialize it with a <em>None</em> value but would give some obscure error message...</p> <h3>Overwriting methods</h3> <p>Next we'll skip to overwriting the <em>_calc_volume</em> method.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">shipping.py</span><span class="liquid-tags-code-lines">[Lines 96-98]</span></figcaption> <div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">_calc_volume</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">_calc_volume</span><span class="p">()</span> <span class="o">-</span> <span class="n">RefrigeratedShippingContainer</span><span class="o">.</span><span class="n">FRIDGE_VOLUME_FT3</span> </code></pre></div> </figure> <p>Notice how we call the parent class with <em>super()</em> but that we also use the class name while retrieving the class variable <em>FRIDGE_VOLUME_FT3</em>!</p> <h2>Conclusion</h2> <p>There's a lot going on here, and it might take you a while to grok it all, but keep at it and I can assure you that the next time that you need to annotate some <em>dataclasses</em>, this information will come in really handy!</p>Cleaning up Anaconda environment.yml files2020-03-24T07:46:00-05:002020-03-24T07:46:00-05:00Martin Uribetag:clamytoe.github.io,2020-03-24:/articles/2020/Mar/24/cleaning-up-anaconda-environment_yml-files<p>Cleaning up Anaconda exported environment files is pretty tedious work</p><h1>Recreating virtual environments with Anaconda</h1> <p>I love working with Anaconda. It makes things so much easier. I enjoy using it so much that I even wrote a guest post article on <a href="proxy.php?url=https://pybit.es/guest-anaconda-workflow.html">PyBit.es</a> about my workflow. It goes into much more detail on the subject, but I'll cover a few basics here.</p> <p>With that being the case, there is one aspect of it that I really hate and that is creating the <code>environment.yml</code> project file for distribution.</p> <h2>Table of Contents</h2> <ul> <li><a href="proxy.php?url=#what-is-an-environment.yml-file">What is an environment.yml file</a></li> <li><a href="proxy.php?url=#how-do-you-use-an-environment.yml-file">How do you use an environment.yml file</a></li> <li><a href="proxy.php?url=#how-do-you-create-and-environment.yml-file">How do you create and environment.yml file</a></li> <li><a href="proxy.php?url=#how-to-prepare-an-environment.yml-file-for-mass-use">How to prepare an environment.yml file for mass use</a></li> <li><a href="proxy.php?url=#how-to-use-the-cleanup-yml.py-script">How to use the cleanup-yml.py script</a></li> <li><a href="proxy.php?url=#how-to-create-an-environment.yml-without-version-lock-in">How to create an environment.yml without version lock in</a></li> <li><a href="proxy.php?url=#conclusion">Conclusion</a></li> </ul> <h2><a name="what-is-an-environment.yml-file"></a>What is an environment.yml file</h2> <p>Think of it as the equivalent of python's <code>requirements.txt</code>. With it, anyone can pick up your project and have a working environment up in no time and with all package dependencies resolved!</p> <p>By convention, just like the <code>requirements.txt</code> file, the file is usually named <em>environment.yml</em> although it can be any name you wish. Services like <a href="proxy.php?url=https://mybinder.org/">binder</a> will automatically know that you are using an Anaconda environment when it detects it and automatically generate the environment from it.</p> <h2><a name="how-do-you-use-an-environment.yml-file"></a>How do you use an environment.yml file</h2> <p>Replicating a working virtual environment from the file is relatively simple. If you or the author of the project stuck to conventions and named it <em>environment.yml</em>, then issuing the following command will get you up and running:</p> <div class="highlight"><pre><span></span><code>conda env create </code></pre></div> <p>But what if it's named something else, like for example <em>secret_sauce.yml</em>? in that case you will have to specify the name of the file while using the <code>-f</code> flag, like ah so:</p> <div class="highlight"><pre><span></span><code>conda env create -f secret_sauce.yml </code></pre></div> <p>Anaconda will then create the virtual environment. It will download and install the packages that are listed in the yml file.</p> <h2><a name="how-do-you-create-and-environment.yml-file"></a>How do you create and environment.yml file</h2> <p>So let's say that you want to be able to recreate your current working environment and you want an exact copy, with matching package versions and everything. It's really easy to do, you simply issue the following command:</p> <div class="highlight"><pre><span></span><code>conda env <span class="nb">export</span> &gt; environment.yml </code></pre></div> <p>The generated file can be used to recreate your virtual environment exactly if you ever had the need. One draw back is that, in it's current form, it only works for your specific hardware setup!</p> <h2><a name="how-to-prepare-an-environment.yml-file-for-mass-use"></a>How to prepare an environment.yml file for mass use</h2> <p>To make it so that others can use it, whether they are on a Windows or a Mac, you will have to prepare the file. In it's current state it looks something like this:</p> <div class="highlight"><pre><span></span><code><span class="n">name</span><span class="o">:</span> <span class="n">dataviz</span> <span class="n">channels</span><span class="o">:</span> <span class="o">-</span> <span class="n">conda</span><span class="o">-</span><span class="n">forge</span> <span class="o">-</span> <span class="n">defaults</span> <span class="n">dependencies</span><span class="o">:</span> <span class="o">-</span> <span class="n">_libgcc_mutex</span><span class="o">=</span><span class="mf">0.1</span><span class="o">=</span><span class="n">conda_forge</span> <span class="o">-</span> <span class="n">_openmp_mutex</span><span class="o">=</span><span class="mf">4.5</span><span class="o">=</span><span class="mi">0</span><span class="n">_gnu</span> <span class="o">-</span> <span class="n">appdirs</span><span class="o">=</span><span class="mf">1.4</span><span class="o">.</span><span class="mi">3</span><span class="o">=</span><span class="n">py_1</span> <span class="o">-</span> <span class="n">attrs</span><span class="o">=</span><span class="mf">19.3</span><span class="o">.</span><span class="mi">0</span><span class="o">=</span><span class="n">py_0</span> <span class="o">-</span> <span class="n">backcall</span><span class="o">=</span><span class="mf">0.1</span><span class="o">.</span><span class="mi">0</span><span class="o">=</span><span class="n">py_0</span> <span class="o">-</span> <span class="n">black</span><span class="o">=</span><span class="mf">19.10</span><span class="n">b0</span><span class="o">=</span><span class="n">py37_0</span> <span class="o">-</span> <span class="n">bleach</span><span class="o">=</span><span class="mf">3.1</span><span class="o">.</span><span class="mi">3</span><span class="o">=</span><span class="n">pyh8c360ce_0</span> <span class="o">-</span> <span class="n">bokeh</span><span class="o">=</span><span class="mf">2.0</span><span class="o">.</span><span class="mi">0</span><span class="o">=</span><span class="n">py37hc8dfbb8_0</span> <span class="o">-</span> <span class="n">bzip2</span><span class="o">=</span><span class="mf">1.0</span><span class="o">.</span><span class="mi">8</span><span class="o">=</span><span class="n">h516909a_2</span> <span class="o">-</span> <span class="n">ca</span><span class="o">-</span><span class="n">certificates</span><span class="o">=</span><span class="mf">2019.11</span><span class="o">.</span><span class="mi">28</span><span class="o">=</span><span class="n">hecc5488_0</span> <span class="o">-</span> <span class="n">certifi</span><span class="o">=</span><span class="mf">2019.11</span><span class="o">.</span><span class="mi">28</span><span class="o">=</span><span class="n">py37hc8dfbb8_1</span> <span class="o">-</span> <span class="n">click</span><span class="o">=</span><span class="mf">7.1</span><span class="o">.</span><span class="mi">1</span><span class="o">=</span><span class="n">pyh8c360ce_0</span> <span class="o">...</span> <span class="o">-</span> <span class="n">yaml</span><span class="o">=</span><span class="mf">0.2</span><span class="o">.</span><span class="mi">2</span><span class="o">=</span><span class="n">h516909a_1</span> <span class="o">-</span> <span class="n">zeromq</span><span class="o">=</span><span class="mf">4.3</span><span class="o">.</span><span class="mi">2</span><span class="o">=</span><span class="n">he1b5a44_2</span> <span class="o">-</span> <span class="n">zipp</span><span class="o">=</span><span class="mf">3.1</span><span class="o">.</span><span class="mi">0</span><span class="o">=</span><span class="n">py_0</span> <span class="o">-</span> <span class="n">zlib</span><span class="o">=</span><span class="mf">1.2</span><span class="o">.</span><span class="mi">11</span><span class="o">=</span><span class="n">h516909a_1006</span> <span class="o">-</span> <span class="n">zstd</span><span class="o">=</span><span class="mf">1.4</span><span class="o">.</span><span class="mi">4</span><span class="o">=</span><span class="n">h3b9ef0a_2</span> <span class="o">-</span> <span class="n">pip</span><span class="o">:</span> <span class="o">-</span> <span class="n">geoplotlib</span><span class="o">==</span><span class="mf">0.3</span><span class="o">.</span><span class="mi">2</span> <span class="o">-</span> <span class="n">pyqt5</span><span class="o">-</span><span class="n">sip</span><span class="o">==</span><span class="mf">4.19</span><span class="o">.</span><span class="mi">18</span> <span class="o">-</span> <span class="n">pyqtwebengine</span><span class="o">==</span><span class="mf">5.12</span><span class="o">.</span><span class="mi">1</span> <span class="n">prefix</span><span class="o">:</span> <span class="sr">/home/mohh/anaconda3/envs/</span><span class="n">dataviz</span> </code></pre></div> <blockquote> <p><strong>NOTE</strong>: I've shortened it because it was pretty long...</p> </blockquote> <p>As I described in my <a href="proxy.php?url=https://pybit.es/guest-anaconda-workflow.html#prepare_environment_yaml_for_distribution">workflow</a> article, you have to remove everything after the second <strong>=</strong> sign and the <strong>prefix:</strong> line towards the end.</p> <p>This particular one had <strong>165</strong> lines!! As you can imagine, that can get pretty tedious. What would any other Python Ninja do in this situation? That's write, hack together a quick little script to do it for you, and that is what I have done.</p> <p>You can find the script on my gist account here: <a href="proxy.php?url=https://gist.github.com/clamytoe/5019dd7c60688be9be7ccc3132c391ed">cleanup-yml.py</a></p> <p><em>cleanup-yml.py</em>:</p> <div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span> <span class="c1"># -*- coding: utf-8 -*-</span> <span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span> <span class="k">def</span> <span class="nf">clean_yaml</span><span class="p">(</span><span class="n">file</span><span class="p">:</span> <span class="n">Path</span><span class="p">,</span> <span class="n">skip</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">5</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> <span class="sd">&quot;&quot;&quot;Cleans up an Anaconda environment file</span> <span class="sd"> The contents of the file are generated by issuing the following</span> <span class="sd"> command from within an activated Anaconda environment:</span> <span class="sd"> conda env export &gt; environment.yml</span> <span class="sd"> Arguments:</span> <span class="sd"> file {Path} -- Path file object that contains the exported env</span> <span class="sd"> skip {int} -- Number of lines to skip/leave alone at the top</span> <span class="sd"> of the file</span> <span class="sd"> Returns:</span> <span class="sd"> str -- cleaned up data</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">cleaned</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span> <span class="k">with</span> <span class="n">file</span><span class="o">.</span><span class="n">open</span><span class="p">()</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span> <span class="k">for</span> <span class="n">n</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">f</span><span class="o">.</span><span class="n">readlines</span><span class="p">()):</span> <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="n">skip</span> <span class="ow">or</span> <span class="s2">&quot;==&quot;</span> <span class="ow">in</span> <span class="n">line</span> <span class="ow">or</span> <span class="s2">&quot;pip:&quot;</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span> <span class="n">cleaned</span> <span class="o">+=</span> <span class="n">line</span> <span class="k">elif</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;prefix:&quot;</span><span class="p">):</span> <span class="n">cleaned</span> <span class="o">+=</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="k">elif</span> <span class="s2">&quot;=&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span> <span class="n">cleaned</span> <span class="o">+=</span> <span class="n">line</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span> <span class="k">else</span><span class="p">:</span> <span class="n">keep</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s2">&quot;=&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="n">cleaned</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">keep</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="k">return</span> <span class="n">cleaned</span> <span class="k">def</span> <span class="nf">save</span><span class="p">(</span><span class="n">file</span><span class="p">:</span> <span class="n">Path</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Saves the data to the file Path object</span> <span class="sd"> Arguments:</span> <span class="sd"> file {Path} -- Path file object to save the data to</span> <span class="sd"> data {str} -- The data to save to the file object</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">file</span><span class="o">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span> <span class="n">file</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">&quot;environment.yml&quot;</span><span class="p">)</span> <span class="n">parsed</span> <span class="o">=</span> <span class="n">clean_yaml</span><span class="p">(</span><span class="n">file</span><span class="p">)</span> <span class="n">save</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">parsed</span><span class="p">)</span> </code></pre></div> <h2><a name="how-to-use-the-cleanup-yml.py-script"></a>How to use the cleanup-yml.py script</h2> <p>Using the script is simple. I like to put my scripts into the <code>~/bin/</code> directory that I have in my home folder. Since that directory will automatically be in my path, I will be able to run the script from anywhere on my system. Make sure to make the script executable: <code>chmod +x ~/bin/cleanup-yml.py</code></p> <p>To use it, you must run it from your project's home directory so that it can find the <em>environment.yml</em> file. There is no output; I guess I should have added a message stating that the modification were completed... A typical workflow would be as follows once you've setup your virtual environment by hand:</p> <div class="highlight"><pre><span></span><code>conda env <span class="nb">export</span> &gt; environment.yml cleanup-yml.py pip freeze &gt; requirements.txt </code></pre></div> <h2><a name="how-to-create-an-environment.yml-without-version-lock-in"></a>How to create an environment.yml without version lock in</h2> <p>If you don't really care about having an exact replica of your environment. Meaning that you don't care about the package versions, and you just want the environment with the latest version of the packages installed.</p> <p>This command is what you want:</p> <div class="highlight"><pre><span></span><code>conda env <span class="nb">export</span> --from-history &gt; environment.yml </code></pre></div> <p>This will generate an a yml file with just the packages that you specified and non of their dependencies.</p> <p>Here's an example one:</p> <div class="highlight"><pre><span></span><code><span class="n">name</span><span class="o">:</span> <span class="n">dataviz</span> <span class="n">channels</span><span class="o">:</span> <span class="o">-</span> <span class="n">conda</span><span class="o">-</span><span class="n">forge</span> <span class="o">-</span> <span class="n">defaults</span> <span class="n">dependencies</span><span class="o">:</span> <span class="o">-</span> <span class="n">python</span><span class="o">=</span><span class="mf">3.7</span> <span class="o">-</span> <span class="n">pandas</span> <span class="o">-</span> <span class="n">seaborn</span> <span class="o">-</span> <span class="n">jupyterlab</span> <span class="o">-</span> <span class="n">squarify</span> <span class="o">-</span> <span class="n">bokeh</span> <span class="o">-</span> <span class="n">matplotlib</span> <span class="o">-</span> <span class="n">pyglet</span> <span class="o">-</span> <span class="n">numpy</span> <span class="o">-</span> <span class="n">flake8</span> <span class="o">-</span> <span class="n">isort</span> <span class="o">-</span> <span class="n">black</span> <span class="o">-</span> <span class="n">pytest</span> <span class="o">-</span> <span class="n">pytest</span><span class="o">-</span><span class="n">cov</span> <span class="o">-</span> <span class="n">mypy</span> <span class="n">prefix</span><span class="o">:</span> <span class="sr">/home/mohh/anaconda3/envs/</span><span class="n">dataviz</span> </code></pre></div> <p>For that one, you would just need to remove the <strong>prefix</strong> line at the end before distributing.</p> <h2><a name="conclusion"></a>Conclusion</h2> <p>Writing that cleanup script is really going to improve my workflow, and I hope that it will do the same for yours!</p> <p>In normal Python, using the <em>requirements.txt</em> file, you would first create your virtual environment, activate it, and then install the packages with the requirements.txt file. With Anaconda, it's all done in one step. You also have the option of choosing different versions of Python, plus you get the guarantee that there will be no package conflicts.</p> <blockquote> <p>On a side note, it feels great to write a script, with type hinting to boot, and not have <a href="proxy.php?url=https://github.com/psf/black">black</a>, <a href="proxy.php?url=https://gitlab.com/pycqa/flake8">flake8</a>, <a href="proxy.php?url=https://github.com/timothycrosley/isort">isort</a>, or <a href="proxy.php?url=https://github.com/python/mypy">mypy</a> complain about any of it! Sure helps to shake that importer syndrome a bit...</p> </blockquote>Bash script into a Makefile2020-03-17T05:08:00-05:002020-03-18T09:13:00-05:00Martin Uribetag:clamytoe.github.io,2020-03-17:/articles/2020/Mar/17/bash-into-make<p>How to convert a bash script into a Makefile</p><h1>Using make can literally make your life easier</h1> <p>So the other day on the Pybites slack channel, <a href="proxy.php?url=https://opensource.com/users/jnyjny">Erik O'Shaughnessy</a> and I were chatting about something and I happened to mention that I had written a bash script to generate documents from Asciidoc files but wanted to create a Makefile to do the same instead. Without hesitation, he asked for my bash script and got to work! I don't think that I could have found a better tutorial on the subject if I had looked. I've looked things up before and nothing is as succinct as what he wrote up for me.</p> <p>This is all his work and even though I made some changes, the credit for it all goes to him. I thought it was so great, that I had to share it with everyone because I believe that it can help others get up to speed with creating Makefiles.</p> <h2>What is a make and what is a Makefile</h2> <p>The <a href="proxy.php?url=https://www.gnu.org/software/make/">GNU Make</a> project's page describes it as such:</p> <blockquote> <p>GNU Make is a tool which controls the generation of executables and other non-source files of a program from the program's source files.</p> </blockquote> <p>It basically means that you can automate some common tasks with it. To illustrate what it can do, I am going to show you how Erik converted my bash script.</p> <h2>The bash script</h2> <p>Here is the script in all its glory... It's not much. It was just thrown together pretty quick and it was getting the job done.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">gen_book.sh</span></figcaption> <div class="highlight"><pre><span></span><code><span class="c">#!/bin/bash</span> <span class="nv">DOC</span><span class="o">=</span><span class="s1">&#39;sop&#39;</span> <span class="nv">DATE</span><span class="o">=</span><span class="sb">`</span>date +%Y-%m-%d<span class="sb">`</span> <span class="nv">REV</span><span class="o">=</span><span class="sb">`</span>grep revnumber sop.asc <span class="p">|</span> cut -d <span class="s1">&#39; &#39;</span> -f <span class="m">2</span><span class="sb">`</span> <span class="nv">PARAMS</span><span class="o">=</span><span class="s2">&quot;--attribute revnumber=</span><span class="nv">$REV</span><span class="s2"> --attribute revdate=</span><span class="nv">$DATE</span><span class="s2">&quot;</span> <span class="err">echo</span> <span class="s2">&quot;Converting to HTML...&quot;</span> <span class="err">bundle</span> <span class="err">exec</span> <span class="err">asciidoctor</span> <span class="err">$PARAMS</span> <span class="err">$DOC.asc</span> <span class="err">echo</span> <span class="s2">&quot; -- HTML outpt at $DOC.html&quot;</span> <span class="err">echo</span> <span class="s2">&quot;Converting to ePub...&quot;</span> <span class="err">bundle</span> <span class="err">exec</span> <span class="err">asciidoctor-epub3</span> <span class="err">$PARAMS</span> <span class="err">$DOC.asc</span> <span class="err">echo</span> <span class="s2">&quot; -- ePub output at $DOC.epub&quot;</span> <span class="err">echo</span> <span class="s2">&quot;Converting to Mobi (kf8)...&quot;</span> <span class="err">bundle</span> <span class="err">exec</span> <span class="err">asciidoctor-epub3</span> <span class="err">$PARAMS</span> <span class="err">-a</span> <span class="nv">ebook-format</span><span class="o">=</span>kf8 <span class="nv">$DOC</span>.asc <span class="err">echo</span> <span class="s2">&quot; -- Mobi output at $DOC.mobi&quot;</span> <span class="err">echo</span> <span class="s2">&quot;Converting to PDF ... (This one takes a while)&quot;</span> <span class="err">bundle</span> <span class="err">exec</span> <span class="err">asciidoctor-pdf</span> <span class="err">$PARAMS</span> <span class="err">$DOC.asc</span> <span class="err">echo</span> <span class="s2">&quot; -- PDF output at $DOC.pdf&quot;</span> </code></pre></div> </figure> <h2>Creating the Makefile</h2> <h3>Variables</h3> <p>This is how you assign a string to a variable name in <em>Make</em>. The variable name doesn't have to be all caps and the equal doesn't have to be snugged up to the variable name; it's just how I write Makefiles</p> <div class="highlight"><pre><span></span><code><span class="nv">FILENAME</span><span class="o">=</span> sop </code></pre></div> <h3>Referencing variables</h3> <p><code>$(IDENTIFIER)</code> is how you reference a variable in Make, you can use <code>${}</code> too, but I prefer <code>$()</code>. If you forget the parentheses or the curly braces, eg <code>$INDENTIFER</code>, Make will interpret that as <code>$I</code> with <code>NDENTFIER</code> appended to it. Probably not what you are expecting.</p> <div class="highlight"><pre><span></span><code><span class="nv">SOURCE</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.asc <span class="nv">HTML</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.html <span class="nv">PDF</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.pdf <span class="nv">EPUB</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.epub <span class="nv">MOBI</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.mobi </code></pre></div> <h3>Shell commands</h3> <p><code>$(shell shell command )</code> is how you invoke a command and save the results to a variable. It gets kinda tricky since every time you reference <code>$(DATE)</code> it will execute the command. The weird assignment operator <code>:=</code> means just assign it one time.</p> <div class="highlight"><pre><span></span><code><span class="nv">DATE</span> <span class="o">:=</span> <span class="k">$(</span>shell date +%Y-%m-%d<span class="k">)</span> </code></pre></div> <p>Again, calling shell. This time using <code>AWK</code> to pull out the revision number.</p> <p>The <em>$</em> needs to be doubled in the command string to keep Make from trying to expand <code>$2</code> into something we didn't intend.</p> <p>I know there was a <code>grep</code>|<code>cut</code> in the bash version, I prefer to use <code>awk</code> for these kinds of <em>snip</em> operations since it's a single process invocation. Those are easier to deal with in this context since you don't have to worry about any pipe weirdness imposed by Make.</p> <div class="highlight"><pre><span></span><code><span class="nv">REVISION</span> <span class="o">:=</span> <span class="k">$(</span>shell awk <span class="s1">&#39;/revnumber/ {print $$2}&#39;</span> <span class="k">$(</span>SOURCE<span class="k">))</span> </code></pre></div> <h3>Functions / macros</h3> <p>Ok this is a "function" definition that we use to build the various <code>ASCIIDOCTOR</code> invocations. We could have just written the format specific definitions:</p> <div class="highlight"><pre><span></span><code><span class="nv">ADOC_HTML</span><span class="o">=</span> bundle <span class="nb">exec</span> asciidoctor <span class="nv">ADOC_PDF</span><span class="o">=</span> bundle <span class="nb">exec</span> asiidoctor-pdf ... </code></pre></div> <p>The advantage of this technique is you only have to change the <code>BUNDLE_EXEC</code> part if the way you invoke <em>asciidoctor</em> changes (I don't know why it would change, but the idea is to isolate stuff that's repeated so you don't have to freaking change it everywhere).</p> <p>Macro or <em>function</em> definition</p> <div class="highlight"><pre><span></span><code><span class="nv">BUNDLE_EXEC</span><span class="o">=</span> bundle <span class="nb">exec</span> <span class="k">$(</span><span class="m">1</span><span class="k">)</span> </code></pre></div> <h3>Using the macro</h3> <div class="highlight"><pre><span></span><code><span class="nv">ASCIIDOCTOR_HTML</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor<span class="k">)</span> <span class="nv">ASCIIDOCTOR_PDF</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor-pdf<span class="k">)</span> <span class="nv">ASCIIDOCTOR_EPUB</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor-epub3<span class="k">)</span> <span class="nv">ASCIIDOCTOR_MOBI</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor-epub3<span class="k">)</span> </code></pre></div> <h3>Shared flags</h3> <p>Here we build the shared flags used by <em>asciidoctor</em> by all invocations. I use the <code>+=</code> assignment to show how you can add to a variable after it's initial assignment.</p> <div class="highlight"><pre><span></span><code><span class="nv">ADOC_FLAGS</span><span class="o">=</span> --attribute <span class="nv">revnumber</span><span class="o">=</span><span class="k">$(</span>REVISION<span class="k">)</span> <span class="nv">ADOC_FLAGS</span><span class="o">+=</span> --attribute <span class="nv">revdate</span><span class="o">=</span><span class="k">$(</span>DATE<span class="k">)</span> </code></pre></div> <h3>Phony rule</h3> <p>This next bit is some <em>make</em> magic. The <code>.PHONY</code> rule is how we tell <em>make</em> that some of our rules are not associated directly with a file.</p> <blockquote> <p>The <code>all</code> rule below is by default a dependency of <code>.PHONY</code></p> </blockquote> <div class="highlight"><pre><span></span><code><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">html</span> <span class="n">pdf</span> <span class="n">epub</span> <span class="n">mobi</span> </code></pre></div> <h3>Default rule</h3> <p>The default rule that <em>Make</em> looks for when invoked as <code>make</code> is <code>all</code>. To build the <code>all</code> target, <em>make</em> builds, replace all with "a rule" name.</p> <div class="highlight"><pre><span></span><code><span class="nf">rulename</span><span class="o">:</span> <span class="n">dep</span>1 <span class="n">dep</span>2 <span class="n">dep</span>3 ... <span class="n">depN</span> <span class="err">command_0</span> <span class="err">@command_1</span> <span class="err">...</span> <span class="err">comand_n</span> <span class="nf">dep1</span><span class="o">:</span> <span class="n">subdep</span>1 ... </code></pre></div> <p>Here, the dependencies for <code>all</code> are <code>$(HTML)</code>, <code>$(PDF)</code>, <code>$(EPUB)</code> and <code>$(MOBI)</code> which expand in to the names of the files that <em>asciidoctor</em> will create. So <code>make all</code> will run the <em>HTML</em>, <em>PDF</em>, <em>EPUB</em> and <em>MOBI</em> rules in that order.</p> <div class="highlight"><pre><span></span><code><span class="nf">all</span><span class="o">:</span> <span class="k">$(</span><span class="nv">HTML</span><span class="k">)</span> <span class="k">$(</span><span class="nv">PDF</span><span class="k">)</span> <span class="k">$(</span><span class="nv">EPUB</span><span class="k">)</span> <span class="k">$(</span><span class="nv">MOBI</span><span class="k">)</span> </code></pre></div> <h3>Rules</h3> <p>The <code>$(HTML)</code> rule depends on <code>$(SOURCE)</code>, and only executes if the source file has changed or the destination file does not exist. <code>$@</code> is an alias for the name of the rule to be used in the body of the recipe.</p> <p>By default, <em>make</em> will print the command that is being executed to stdout followed by it's output. To suppress printing the command, preface the command with an <code>@</code>.</p> <p>Lastly, the indention is a <strong>TAB</strong> and not <strong>8 spaces</strong>. <em>Make</em> is an old-school tool and will complain if it doesn't get it's tabs.</p> <div class="highlight"><pre><span></span><code><span class="nf">$(HTML)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_HTML<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> <span class="nf">$(PDF)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_PDF<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> <span class="nf">$(EPUB)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_EPUB<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> <span class="nf">$(MOBI)</span><span class="o">:</span> <span class="n">ADOC_FLAGS</span> += -<span class="n">a</span> <span class="n">ebook</span>-<span class="n">format</span>=<span class="n">kf</span>8 <span class="nf">$(MOBI)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_MOBI<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> </code></pre></div> <blockquote> <p><strong>NOTE</strong>: The <em>MOBI</em> <em>asciidoctor</em> command requires additional flags to generate the file. This is how you would make the changes to it.</p> </blockquote> <p>At this point, you can add each of the <em>PHONY</em> rules. You might want to group these together along with the regular rules for keep related blocks of logic together, but I'll add them here for now.</p> <div class="highlight"><pre><span></span><code><span class="nf">html</span><span class="o">:</span> <span class="k">$(</span><span class="nv">HTML</span><span class="k">)</span> <span class="nf">pdf</span><span class="o">:</span> <span class="k">$(</span><span class="nv">PDF</span><span class="k">)</span> <span class="nf">epub</span><span class="o">:</span> <span class="k">$(</span><span class="nv">EPUB</span><span class="k">)</span> <span class="nf">mobi</span><span class="o">:</span> <span class="k">$(</span><span class="nv">MOBI</span><span class="k">)</span> </code></pre></div> <h3>The debug rule</h3> <p>The debug rule is how I checked to make sure all of the variables I constructed contained the things I thought they should.</p> <div class="highlight"><pre><span></span><code><span class="nf">debug</span><span class="o">:</span> @echo Rule -&gt; <span class="nv">$@</span> @echo <span class="s1">&#39; SOURCE: $(SOURCE)&#39;</span> @echo <span class="s1">&#39; REVISION: $(REVISION)&#39;</span> @echo <span class="s1">&#39; HTML: $(HTML)&#39;</span> @echo <span class="s1">&#39; PDF: $(PDF)&#39;</span> @echo <span class="s1">&#39; EPUB: $(EPUB)&#39;</span> @echo <span class="s1">&#39; MOBI: $(MOBI)&#39;</span> @echo <span class="s1">&#39; DOC_FLAGS: $(ADOC_FLAGS)&#39;</span> @echo <span class="s1">&#39;ASCIIDOCTOR HTML: $(ASCIIDOCTOR_HTML)&#39;</span> @echo <span class="s1">&#39; ASCIIDOCTOR PDF: $(ASCIIDOCTOR_PDF)&#39;</span> @echo <span class="s1">&#39;ASCIIDOCTOR EPUB: $(ASCIIDOCTOR_EPUB)&#39;</span> @echo <span class="s1">&#39;ASCIIDOCTOR MOBI: $(ASCIIDOCTOR_MOBI)&#39;</span> </code></pre></div> <h3>The clean rule</h3> <p>Often times we want to restart from a known good "clean" state. A clean rule is a good place to remove transient files so you ensure that all your dependencies in your project are rebuilt. In this case we just remove the translated files. We could use wildcards in this rule like <code>rm *.html</code> but this can have unintended consequences if we have other files in HTML format that we didn't want to smoke.</p> <p>Always be as explicit as possible in clean rules.</p> <div class="highlight"><pre><span></span><code><span class="nf">clean</span><span class="o">:</span> @/bin/rm -f <span class="k">$(</span>HTML<span class="k">)</span> <span class="k">$(</span>PDF<span class="k">)</span> <span class="k">$(</span>EPUB<span class="k">)</span> <span class="k">$(</span>MOBI<span class="k">)</span> </code></pre></div> <h3>The help rule</h3> <p>I've found it to be very useful for letting users know what commands are available and what they do. Mostly it's for me, I forget everything...</p> <div class="highlight"><pre><span></span><code><span class="nf">help</span><span class="o">:</span> @echo <span class="s1">&#39;Makefile for generating documents from Asciidoc source files &#39;</span> @echo <span class="s1">&#39; &#39;</span> @echo <span class="s1">&#39;Usage: &#39;</span> @echo <span class="s1">&#39; make runs rules specified under all &#39;</span> @echo <span class="s1">&#39; make all generates all of the file formats &#39;</span> @echo <span class="s1">&#39; make clean remove the generated files &#39;</span> @echo <span class="s1">&#39; make debug prints all of the variables used &#39;</span> @echo <span class="s1">&#39; make epub (re)generates an epub file &#39;</span> @echo <span class="s1">&#39; make help prints this message &#39;</span> @echo <span class="s1">&#39; make html (re)generates an html file &#39;</span> @echo <span class="s1">&#39; make mobi (re)generates a mobi file &#39;</span> @echo <span class="s1">&#39; make pdf (re)generates a pdf file &#39;</span> @echo <span class="s1">&#39; make -n [epub, html, mobi, pdf] prints out the commands it would &#39;</span> @echo <span class="s1">&#39; run without executing them &#39;</span> @echo <span class="s1">&#39; &#39;</span> </code></pre></div> <h2>Conclusion</h2> <p><em>Make</em> is a very versatile program and can be used for many more things. For instance Erik uses it to launch his kids Minecraft server! Now that I know how a <em>Makefile</em> is constructed, I will be able to automate other parts of my daily tasks!</p> <blockquote> <p><strong>NOTE</strong>: The <em>MOBI</em> format kept failing on me. It would leave behind a file with the extension <em>-kf8.epub</em> and never generate the <em>.mobi</em> one. My guess is that I need to install some kind of Amazon Kindle app program or something, so I removed it from the <code>all</code> rule to prevent it from generating by default.</p> </blockquote> <p>This is the final file:</p> <div class="highlight"><pre><span></span><code><span class="c"># Makefile</span> <span class="c"># Main project file name</span> <span class="nv">FILENAME</span><span class="o">=</span> pnc-sop <span class="c"># Variables used</span> <span class="nv">SOURCE</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.asc <span class="nv">HTML</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.html <span class="nv">PDF</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.pdf <span class="nv">EPUB</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.epub <span class="nv">MOBI</span><span class="o">=</span> <span class="k">$(</span>FILENAME<span class="k">)</span>.mobi <span class="c"># Store the current date</span> <span class="nv">DATE</span> <span class="o">:=</span> <span class="k">$(</span>shell date +%Y-%m-%d<span class="k">)</span> <span class="c"># Grab revision number from the source document</span> <span class="nv">REVISION</span> <span class="o">:=</span> <span class="k">$(</span>shell awk <span class="s1">&#39;/revnumber/ {print $$2}&#39;</span> <span class="k">$(</span>SOURCE<span class="k">))</span> <span class="c"># Macro or &#39;function&#39; definition</span> <span class="nv">BUNDLE_EXEC</span><span class="o">=</span> bundle <span class="nb">exec</span> <span class="k">$(</span><span class="m">1</span><span class="k">)</span> <span class="c"># Assigning the macro</span> <span class="nv">ASCIIDOCTOR_HTML</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor<span class="k">)</span> <span class="nv">ASCIIDOCTOR_PDF</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor-pdf<span class="k">)</span> <span class="nv">ASCIIDOCTOR_EPUB</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor-epub3<span class="k">)</span> <span class="nv">ASCIIDOCTOR_MOBI</span><span class="o">=</span> <span class="k">$(</span>call BUNDLE_EXEC,asciidoctor-epub3<span class="k">)</span> <span class="c"># Shared flags</span> <span class="nv">ADOC_FLAGS</span><span class="o">=</span> --attribute <span class="nv">revnumber</span><span class="o">=</span><span class="k">$(</span>REVISION<span class="k">)</span> <span class="nv">ADOC_FLAGS</span><span class="o">+=</span> --attribute <span class="nv">revdate</span><span class="o">=</span><span class="k">$(</span>DATE<span class="k">)</span> <span class="c"># Enable phony rules</span> <span class="nf">.PHONY</span><span class="o">:</span> <span class="n">html</span> <span class="n">pdf</span> <span class="n">epub</span> <span class="n">mobi</span> <span class="c"># Define the make commands</span> <span class="nf">all</span><span class="o">:</span> <span class="k">$(</span><span class="nv">HTML</span><span class="k">)</span> <span class="k">$(</span><span class="nv">PDF</span><span class="k">)</span> <span class="k">$(</span><span class="nv">EPUB</span><span class="k">)</span> <span class="c"># Define each of the commands and specifying their outputs</span> <span class="nf">$(HTML)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_HTML<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> <span class="nf">html</span><span class="o">:</span> <span class="k">$(</span><span class="nv">HTML</span><span class="k">)</span> <span class="nf">$(PDF)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_PDF<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> <span class="nf">pdf</span><span class="o">:</span> <span class="k">$(</span><span class="nv">PDF</span><span class="k">)</span> <span class="nf">$(EPUB)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_EPUB<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> <span class="nf">epub</span><span class="o">:</span> <span class="k">$(</span><span class="nv">EPUB</span><span class="k">)</span> <span class="nf">$(MOBI)</span><span class="o">:</span> <span class="n">ADOC_FLAGS</span> += -<span class="n">a</span> <span class="n">ebook</span>-<span class="n">format</span>=<span class="n">kf</span>8 <span class="nf">$(MOBI)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">SOURCE</span><span class="k">)</span> @echo Converting <span class="k">$(</span>SOURCE<span class="k">)</span> to <span class="nv">$@</span> @<span class="k">$(</span>ASCIIDOCTOR_MOBI<span class="k">)</span> <span class="k">$(</span>ADOC_FLAGS<span class="k">)</span> <span class="k">$(</span>SOURCE<span class="k">)</span> <span class="nf">mobi</span><span class="o">:</span> <span class="k">$(</span><span class="nv">MOBI</span><span class="k">)</span> <span class="c"># Use debug rule to check that all of the variables were</span> <span class="c"># constructed properly.</span> <span class="nf">debug</span><span class="o">:</span> @echo Rule -&gt; <span class="nv">$@</span> @echo <span class="s1">&#39; SOURCE: $(SOURCE)&#39;</span> @echo <span class="s1">&#39; REVISION: $(REVISION)&#39;</span> @echo <span class="s1">&#39; HTML: $(HTML)&#39;</span> @echo <span class="s1">&#39; PDF: $(PDF)&#39;</span> @echo <span class="s1">&#39; EPUB: $(EPUB)&#39;</span> @echo <span class="s1">&#39; MOBI: $(MOBI)&#39;</span> @echo <span class="s1">&#39; DOC_FLAGS: $(ADOC_FLAGS)&#39;</span> @echo <span class="s1">&#39;ASCIIDOCTOR HTML: $(ASCIIDOCTOR_HTML)&#39;</span> @echo <span class="s1">&#39; ASCIIDOCTOR PDF: $(ASCIIDOCTOR_PDF)&#39;</span> @echo <span class="s1">&#39;ASCIIDOCTOR MOBI: $(ASCIIDOCTOR_MOBI)&#39;</span> @echo <span class="s1">&#39;ASCIIDOCTOR EPUB: $(ASCIIDOCTOR_EPUB)&#39;</span> <span class="c"># Simple help menu showing what commands are available</span> <span class="c"># and what they do.</span> <span class="nf">help</span><span class="o">:</span> @echo <span class="s1">&#39;Makefile for generating documents from Asciidoc source files &#39;</span> @echo <span class="s1">&#39; &#39;</span> @echo <span class="s1">&#39;Usage: &#39;</span> @echo <span class="s1">&#39; make runs rules specified under all &#39;</span> @echo <span class="s1">&#39; make all generates all of the file formats &#39;</span> @echo <span class="s1">&#39; make clean remove the generated files &#39;</span> @echo <span class="s1">&#39; make debug prints all of the variables used &#39;</span> @echo <span class="s1">&#39; make epub (re)generates an epub file &#39;</span> @echo <span class="s1">&#39; make help prints this message &#39;</span> @echo <span class="s1">&#39; make html (re)generates an html file &#39;</span> @echo <span class="s1">&#39; make mobi (re)generates a mobi file &#39;</span> @echo <span class="s1">&#39; make pdf (re)generates a pdf file &#39;</span> @echo <span class="s1">&#39; make -n [epub, html, mobi, pdf] prints out the commands it would &#39;</span> @echo <span class="s1">&#39; run without executing them &#39;</span> @echo <span class="s1">&#39; &#39;</span> <span class="c"># Specify clean-up rules.</span> <span class="nf">clean</span><span class="o">:</span> @/bin/rm -f <span class="k">$(</span>HTML<span class="k">)</span> <span class="k">$(</span>PDF<span class="k">)</span> <span class="k">$(</span>EPUB<span class="k">)</span> <span class="k">$(</span>MOBI<span class="k">)</span> <span class="k">$(</span>FILENAME<span class="k">)</span>-kf8.epub </code></pre></div> <blockquote> <p>Thanks again Erik! Check out his commented <a href="proxy.php?url=https://gist.github.com/clamytoe/68d13bb8481fc7acb81e373dea921d7d">Makefile</a> for further insights!</p> </blockquote>Testing ABC's with abstract methods with Pytest2020-03-12T10:31:00-05:002020-03-12T21:51:00-05:00Martin Uribetag:clamytoe.github.io,2020-03-12:/articles/2020/Mar/12/testing-abcs-with-abstract-methods-with-pytest<p>What I had to do to get 100% coverage on my tests</p><h1>So you want to test ABC's with pytest</h1> <p>I was working on writing a new code challenge for <a href="proxy.php?url=https://codechalleng.es/">Codechalleng.es</a> the other day. It's based on <a href="proxy.php?url=https://realpython.com/inheritance-composition-python/">inheritance and composition</a> and uses <a href="proxy.php?url=https://docs.python.org/3/library/abc.html">Abstract Base Classes</a> to define the interfaces that should be implemented in classes derived from it.</p> <p>I was just finishing up with the tests and everything was passing. Then when I checked the code coverage on it, I saw that it was complaining about my <em>Site</em> class, which is the one derived from ABC has several methods decorated as <em>abstractmethods</em>. <a href="proxy.php?url=https://coverage.readthedocs.io/">Coverage</a> wanted me to test the <code>pass</code> statements on them...</p> <p>Initial searches didn't turn up much, so I posed the question in PyBites slack channel. The best suggestion was to use the <em>dis</em> module and test it that way, but since it's not really running the code, coverage still counts it as untested.</p> <h2>How to test abstract methods</h2> <p>Finally I came across this post on <a href="proxy.php?url=https://stackoverflow.com/questions/9757299/python-testing-an-abstract-base-class#17345619">reddit</a> that had the solution. Here is what the code looks like, with doctrings removed:</p> <div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Site</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span> <span class="n">web</span><span class="p">:</span> <span class="n">Web</span> <span class="k">def</span> <span class="nf">find_table</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">loc</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">web</span><span class="o">.</span><span class="n">soup</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s2">&quot;table&quot;</span><span class="p">)[</span><span class="n">loc</span><span class="p">]</span> <span class="nd">@abstractmethod</span> <span class="k">def</span> <span class="nf">parse_rows</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">table</span><span class="p">:</span> <span class="n">Soup</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Any</span><span class="p">]:</span> <span class="k">pass</span> <span class="nd">@abstractmethod</span> <span class="k">def</span> <span class="nf">polls</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">table</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Any</span><span class="p">]:</span> <span class="k">pass</span> <span class="nd">@abstractmethod</span> <span class="k">def</span> <span class="nf">stats</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">loc</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">):</span> <span class="k">pass</span> </code></pre></div> <p>Here is how you go about testing them:</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">test_site</span><span class="p">(</span><span class="n">test_file</span><span class="p">):</span> <span class="n">Site</span><span class="o">.</span><span class="n">__abstractmethods__</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span> <span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">Dummy</span><span class="p">(</span><span class="n">Site</span><span class="p">):</span> <span class="n">web</span><span class="p">:</span> <span class="n">Web</span> <span class="n">url</span> <span class="o">=</span> <span class="s2">&quot;https://projects.fivethirtyeight.com/polls/&quot;</span> <span class="n">test_web</span> <span class="o">=</span> <span class="n">Web</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">test_file</span><span class="p">)</span> <span class="n">d</span> <span class="o">=</span> <span class="n">Dummy</span><span class="p">(</span><span class="n">test_web</span><span class="p">)</span> <span class="n">table</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">find_table</span><span class="p">()</span> <span class="n">rows</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">parse_rows</span><span class="p">(</span><span class="n">table</span><span class="p">)</span> <span class="n">polls</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">polls</span><span class="p">()</span> <span class="n">stats</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">stats</span><span class="p">()</span> <span class="k">assert</span> <span class="n">d</span><span class="o">.</span><span class="n">web</span><span class="o">.</span><span class="n">file</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">&quot;test.html&quot;</span> <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">Site</span><span class="p">,</span> <span class="n">ABCMeta</span><span class="p">)</span> <span class="k">assert</span> <span class="n">rows</span> <span class="ow">is</span> <span class="kc">None</span> <span class="k">assert</span> <span class="n">polls</span> <span class="ow">is</span> <span class="kc">None</span> <span class="k">assert</span> <span class="n">stats</span> <span class="ow">is</span> <span class="kc">None</span> </code></pre></div> <p>As you can see, the way to accomplish this is to override <code>__abstractmethods__</code>. Basically tricking it into thinking that it doesn't have any. The last three <em>asserts</em> then verify that <code>None</code> is being returned.</p> <div class="highlight"><pre><span></span><code><span class="o">=============================</span> <span class="n">test</span> <span class="k">session</span> <span class="n">starts</span> <span class="o">==============================</span> <span class="n">platform</span> <span class="n">linux</span> <span class="c1">-- Python 3.7.3, pytest-4.4.0, py-1.8.0, pluggy-0.9.0 -- /home/mohh/anaconda3/envs/pybites/bin/python</span> <span class="n">cachedir</span><span class="p">:</span> <span class="p">.</span><span class="n">pytest_cache</span> <span class="n">rootdir</span><span class="p">:</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">mohh</span><span class="o">/</span><span class="n">Projects</span><span class="o">/</span><span class="n">team</span><span class="o">-</span><span class="n">charlie</span><span class="o">/</span><span class="mi">020</span> <span class="n">plugins</span><span class="p">:</span> <span class="n">cov</span><span class="o">-</span><span class="mi">2</span><span class="p">.</span><span class="mi">8</span><span class="p">.</span><span class="mi">1</span><span class="p">,</span> <span class="n">asyncio</span><span class="o">-</span><span class="mi">0</span><span class="p">.</span><span class="mi">10</span><span class="p">.</span><span class="mi">0</span> <span class="n">collected</span> <span class="mi">8</span> <span class="n">items</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_file_class</span> <span class="n">PASSED</span> <span class="p">[</span> <span class="mi">12</span><span class="o">%</span><span class="p">]</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_web</span> <span class="n">PASSED</span> <span class="p">[</span> <span class="mi">25</span><span class="o">%</span><span class="p">]</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_web_bad_url</span> <span class="n">PASSED</span> <span class="p">[</span> <span class="mi">37</span><span class="o">%</span><span class="p">]</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_poll</span> <span class="n">PASSED</span> <span class="p">[</span> <span class="mi">50</span><span class="o">%</span><span class="p">]</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_leaderboard</span> <span class="n">PASSED</span> <span class="p">[</span> <span class="mi">62</span><span class="o">%</span><span class="p">]</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_rcp_stats</span> <span class="n">PASSED</span> <span class="p">[</span> <span class="mi">75</span><span class="o">%</span><span class="p">]</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_nyt</span> <span class="n">PASSED</span> <span class="p">[</span> <span class="mi">87</span><span class="o">%</span><span class="p">]</span> <span class="n">test_composition</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_site</span> <span class="n">PASSED</span> <span class="p">[</span><span class="mi">100</span><span class="o">%</span><span class="p">]</span> <span class="c1">----------- coverage: platform linux, python 3.7.3-final-0 -----------</span> <span class="n">Name</span> <span class="n">Stmts</span> <span class="n">Miss</span> <span class="n">Cover</span> <span class="n">Missing</span> <span class="c1">----------------------------------------------</span> <span class="n">composition</span><span class="p">.</span><span class="n">py</span> <span class="mi">107</span> <span class="mi">0</span> <span class="mi">100</span><span class="o">%</span> <span class="o">===========================</span> <span class="mi">8</span> <span class="n">passed</span> <span class="k">in</span> <span class="mi">7</span><span class="p">.</span><span class="mi">22</span> <span class="n">seconds</span> <span class="o">===========================</span> </code></pre></div> <p>Success!</p> <h2>Conclusion</h2> <p>If you're ever faced with having to write test for abstract methods, simply override the <code>__abstractmethods__</code> of the ABC class! Definitely something to keep in your toolbox.</p> <p>Until next time, happy coding!</p>Playing around with Jupyter notebooks2020-03-09T19:43:00-05:002020-03-13T06:54:00-05:00Martin Uribetag:clamytoe.github.io,2020-03-09:/articles/2020/Mar/09/playing-around-with-jupyter-notebooks<p>Just giving the notebook plugin a test run to see how things render</p><div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h1 id="Seeing-how-notebooks-compare-to-just-plain-Markdown">Seeing how notebooks compare to just plain Markdown<a class="anchor-link" href="proxy.php?url=#Seeing-how-notebooks-compare-to-just-plain-Markdown">&#182;</a></h1> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h2 id="What-is-a-Jupyter-Notebook">What is a Jupyter Notebook<a class="anchor-link" href="proxy.php?url=#What-is-a-Jupyter-Notebook">&#182;</a></h2><p>Well it's a document that lets you render markdown code, display code blocks, and also allows you to execute those code blocks and display their results interactively! Needless to say, they are definitely game changers. It's like the Python interpreter on steroids!</p> <p>They are perfect for academia, data scientist, and anyone else that wants to explore data and code and be able to reproduce and/or show how they did it all in the same document. There is a large movement in the scientific community to include the code that was used on research papers, and Notebooks are filling that niche perfectly!</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h2 id="Update-to-my-newpost-script">Update to my newpost script<a class="anchor-link" href="proxy.php?url=#Update-to-my-newpost-script">&#182;</a></h2><p>To show a little bit about what Notebooks can do, I'll demonstrate by talking about my <code>newpost.py</code> script. I had orginally written it to create the basic structure of new blog posts, but in <a href="proxy.php?url=https://daringfireball.net/projects/markdown/">Markdown</a>. Since I'm going to start using <a href="proxy.php?url=https://jupyter.org/install.html">Jupyter Notebooks</a> as well, I decided to spruce it up a bit and add support for them. I want to keep my notebooks, vanilla. Meaning that I don't want to shove the <em>metadata</em> required by <a href="proxy.php?url=https://blog.getpelican.com/">Pelican</a> into them. Since I didn't know how much code went into a blank notebook, I decided to just dump the <em>metadata</em> into an <code>.nbdata</code> file. All that's left for me to do is to fire up JupyterLab, create a new book, and name it exactly like the <code>.nbdata</code> file. I didn't want to bother with typing it all out, so I used <a href="proxy.php?url=https://github.com/asweigart/pyperclip">Pyperclip</a> to copy the <em>slug</em> name into my clipboard. Now a simple <em>paste</em> gets the job done!</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h3 id="Imports-and-constants">Imports and constants<a class="anchor-link" href="proxy.php?url=#Imports-and-constants">&#182;</a></h3> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>The first thing any Python scripts starts with is importing all of the libraries that are going to be used at the beginning of the file, followed by any global variables.</p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[14]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span> <span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span> <span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span> <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span> <span class="kn">import</span> <span class="nn">pyperclip</span> <span class="n">AUTHOR</span> <span class="o">=</span> <span class="s2">&quot;Martin Uribe&quot;</span> <span class="n">ARTICLE_LOCATION</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">&quot;content&quot;</span><span class="p">,</span> <span class="s2">&quot;articles&quot;</span><span class="p">)</span> </pre></div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>As you can see, that looks like any other code block that you normally see on blog posts, but this one is actually "active". Let me demonstrate. The following will be a simple Python <code>print()</code> command, and the output that follows will be generated and not typed in by me: <a href='javascript:toggle_code()'>[on/off]</a></p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[18]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">AUTHOR</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s2"> is storing his blog articles in </span><span class="si">{</span><span class="n">ARTICLE_LOCATION</span><span class="o">.</span><span class="n">absolute</span><span class="p">()</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> </pre></div> </div> </div> </div> <div class="output_wrapper"> <div class="output"> <div class="output_area"> <div class="prompt"></div> <div class="output_subarea output_stream output_stdout output_text"> <pre>Martin is storing his blog articles in /home/mohh/Projects/ghpages/content/articles/content/articles </pre> </div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>Pretty nice, right?</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h3 id="Article-class">Article class<a class="anchor-link" href="proxy.php?url=#Article-class">&#182;</a></h3><p>I think you're starting to see how powerful working in notebooks really is. Let's continue with the rest of the script. Here is the <code>Article</code> class that I created for it: <a href='javascript:toggle_code()'>[on/off]</a></p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[19]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="nd">@dataclass</span> <span class="k">class</span> <span class="nc">Article</span><span class="p">:</span> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="n">category</span><span class="p">:</span> <span class="nb">str</span> <span class="n">tags</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="n">summary</span><span class="p">:</span> <span class="nb">str</span> <span class="n">markdown</span><span class="p">:</span> <span class="nb">bool</span> <span class="k">def</span> <span class="nf">__post_init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">category</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">category</span><span class="o">.</span><span class="n">title</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">today</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">&quot;%Y-%m-</span><span class="si">%d</span><span class="s2"> %H:%M&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">slug</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">author</span> <span class="o">=</span> <span class="n">AUTHOR</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">markdown</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="n">ARTICLE_LOCATION</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">slug</span><span class="si">}</span><span class="s2">.md&quot;</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="n">ARTICLE_LOCATION</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">slug</span><span class="si">}</span><span class="s2">.nbdata&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">create_file</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">header</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;title: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="n">header</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;date: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">date</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="n">header</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;category: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">category</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="n">header</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;tags: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="n">header</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;slug: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">slug</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="n">header</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;author: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">author</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="n">header</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;summary: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">summary</span><span class="si">}</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">markdown</span><span class="p">:</span> <span class="n">header</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2"># </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="si">}</span><span class="se">\n\n</span><span class="s2">&quot;</span> <span class="k">else</span><span class="p">:</span> <span class="n">pyperclip</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">slug</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">header</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Generated new article: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> </pre></div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>Once again, this is all code that I can already start to interact with, but let me add the rest of the helper functions.</p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[20]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="k">def</span> <span class="nf">get_bool</span><span class="p">(</span><span class="n">subject</span><span class="p">):</span> <span class="n">value</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">subject</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span><span class="si">:</span><span class="s2">&gt;10</span><span class="si">}</span><span class="s2"> [y]/n? &quot;</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">value</span><span class="p">:</span> <span class="k">return</span> <span class="kc">True</span> <span class="k">return</span> <span class="kc">False</span> <span class="k">def</span> <span class="nf">get_input</span><span class="p">(</span><span class="n">subject</span><span class="p">):</span> <span class="n">value</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">subject</span><span class="si">:</span><span class="s2">&gt;10</span><span class="si">}</span><span class="s2">: &quot;</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">value</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;No subject provided!&quot;</span><span class="p">)</span> <span class="n">exit</span> <span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">def</span> <span class="nf">get_tags</span><span class="p">():</span> <span class="n">tags</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="n">tag</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">&quot; tag: &quot;</span><span class="p">)</span> <span class="k">if</span> <span class="n">tag</span><span class="p">:</span> <span class="n">tags</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">tag</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span> <span class="k">else</span><span class="p">:</span> <span class="k">break</span> <span class="k">return</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">tags</span><span class="p">)</span> </pre></div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>You can add functions in separate cells or all together like I did here, it really doesn't matter. The one thing that you have to keep in mind though is that these cell blocks can all be executed in any order, so it's easy to have "old" code in memory that can still be used when working on other parts. In cases like that where I've changed a lot of code, I like to just <em>Restart the Kernel and Rerun All Cells..</em> from the <strong>Run</strong> menu command from the JupyterLab interface.</p> <p><img src="proxy.php?url=https://clamytoe.github.io/images/kernel-reset.png" alt="kernel-reset"></p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h3 id="Playing-around-with-the-code">Playing around with the code<a class="anchor-link" href="proxy.php?url=#Playing-around-with-the-code">&#182;</a></h3><p>So the core of the script is now loaded. Let's see what we can do. As you can tell from looking at the class, we have to instantiate an instance by providing the following:</p> <ul> <li><strong>title</strong>: <em>str</em>, A title for the new blog post</li> <li><strong>category</strong>: <em>str</em>, A category to file it under</li> <li><strong>tags</strong>: <em>List[str]</em>, A list of strings for the tags</li> <li><strong>summary</strong>: <em>str</em>, A summary of of what the blog is going to be about</li> <li><strong>markdown</strong>: <em>bool</em>, True or False if this is going to be a markdown file, if it's not it'll set it up for a notebook</li> </ul> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>This is where the helper functions come in handy. I have the following execute when the script is ran: <a href='javascript:toggle_code()'>[on/off]</a></p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[23]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="k">def</span> <span class="nf">main</span><span class="p">():</span> <span class="n">title</span> <span class="o">=</span> <span class="n">get_input</span><span class="p">(</span><span class="s2">&quot;title&quot;</span><span class="p">)</span> <span class="n">category</span> <span class="o">=</span> <span class="n">get_input</span><span class="p">(</span><span class="s2">&quot;category&quot;</span><span class="p">)</span> <span class="n">tags</span> <span class="o">=</span> <span class="n">get_tags</span><span class="p">()</span> <span class="n">summary</span> <span class="o">=</span> <span class="n">get_input</span><span class="p">(</span><span class="s2">&quot;summary&quot;</span><span class="p">)</span> <span class="n">markdown</span> <span class="o">=</span> <span class="n">get_bool</span><span class="p">(</span><span class="s2">&quot;markdown&quot;</span><span class="p">)</span> <span class="n">post</span> <span class="o">=</span> <span class="n">Article</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">category</span><span class="p">,</span> <span class="n">tags</span><span class="p">,</span> <span class="n">summary</span><span class="p">,</span> <span class="n">markdown</span><span class="p">)</span> <span class="c1"># post.create_file()</span> </pre></div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>I've commented out the last line that actually creates the file because I really don't want to create it at the moment, but let's the see the rest in action:</p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[24]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="n">main</span><span class="p">()</span> </pre></div> </div> </div> </div> <div class="output_wrapper"> <div class="output"> <div class="output_area"> <div class="prompt"></div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>Since the default is to create a markdown file, no answer was needed for the last question.</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h3 id="Exploring-the-Article-object">Exploring the Article object<a class="anchor-link" href="proxy.php?url=#Exploring-the-Article-object">&#182;</a></h3><p>Ok, so what did that actually do? Let's see!</p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[25]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="n">post</span> </pre></div> </div> </div> </div> <div class="output_wrapper"> <div class="output"> <div class="output_area"> <div class="prompt"></div> <div class="output_subarea output_text output_error"> <pre> <span class="ansi-red-fg">---------------------------------------------------------------------------</span> <span class="ansi-red-fg">NameError</span> Traceback (most recent call last) <span class="ansi-green-fg">&lt;ipython-input-25-9414b608df81&gt;</span> in <span class="ansi-cyan-fg">&lt;module&gt;</span> <span class="ansi-green-fg">----&gt; 1</span><span class="ansi-red-fg"> </span>post <span class="ansi-red-fg">NameError</span>: name &#39;post&#39; is not defined</pre> </div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>Oh no, what happened? Well when I normally run the script, it automatically creates the new file for me. In this instance, it created the new <code>post</code> but it was lost in the <code>main</code> function because I didn't save its state! I'll leave it up to you to explore what it contains, but it's pretty easy to see that it just filled in everything that I need to boilerplate a new pelican blog post.</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h2 id="How-can-you-do-the-same">How can you do the same<a class="anchor-link" href="proxy.php?url=#How-can-you-do-the-same">&#182;</a></h2><p>Make sure that you're working in your virtual environment and go into the directory where you have your blog project. Create a plugins directory if you don't already have one and go into it. Then clone the plugin's repos directly into it.</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <div class="highlight"><pre><span></span><span class="c1"># from inside your project folder</span> mkdir plugins, <span class="nb">cd</span> <span class="nv">$_</span> git clone https://github.com/danielfrg/pelican-ipynb ipynb </pre></div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>Open up your <code>pelicanconf.py</code> file and make the following changes/additions:</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <div class="highlight"><pre><span></span><span class="n">MARKUP</span> <span class="o">=</span> <span class="p">(</span><span class="s2">&quot;md&quot;</span><span class="p">,</span> <span class="s2">&quot;ipynb&quot;</span><span class="p">)</span> <span class="n">PLUGIN_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;./plugins&quot;</span><span class="p">]</span> <span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;ipynb.markup&quot;</span><span class="p">]</span> <span class="n">IGNORE_FILES</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;.ipynb_checkpoints&quot;</span><span class="p">]</span> <span class="n">IPYNB_USE_METACELL</span> <span class="o">=</span> <span class="kc">False</span> <span class="c1"># set to True if you want to embedd metadata into notebook</span> <span class="n">IPYNB_SKIP_CSS</span> <span class="o">=</span> <span class="kc">True</span> </pre></div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>Now, if you want to do what I'm doing and keep the <em>metadata</em> in its own file and out of the notebook, you'll have to create a file with the exact same name as the notebook but with the <code>.nbdata</code> extension. The one for this article looks like this:</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <pre><code>title: Playing around with Jupyter notebooks date: 2020-03-09 19:43 modified: 2020-03-10 08:17 category: Blog tags: jupyter, notebook, python slug: playing-around-with-jupyter-notebooks author: Martin Uribe summary: Just giving the notebook plugin a test run to see how things render</code></pre> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <p>Make sure there is an empty line at the end of the file. To embed directly into the notebook, modify it like this and put it in the first cell:</p> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <pre><code>- title: Playing around with Jupyter notebooks - date: 2020-03-09 19:43 - modified: 2020-03-10 08:17 - category: Blog - tags: jupyter, notebook, python - slug: playing-around-with-jupyter-notebooks - author: Martin Uribe - summary: Just giving the notebook plugin a test run to see how things render</code></pre> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h3 id="Making-it-look-pretty">Making it look pretty<a class="anchor-link" href="proxy.php?url=#Making-it-look-pretty">&#182;</a></h3><p>Originally when the notebook was being converted into html, it was putting anchor tags at the end of headings that I didn't like it. <img src="proxy.php?url=https://clamytoe.github.io/images/nb-anchor.png" alt="nb-anchor"> Also, each code cell was getting a <strong>In[1]</strong> placed in front of it, indicating in which order it was run. I don't need or want any of those, any code that I post will be expected to be ran in order. To get rid of them, I created these custom CSS entries into the style sheet of my theme:</p> <div class="highlight"><pre><span></span><span class="o">//</span> <span class="nt">jupyter</span> <span class="nt">notebook</span> <span class="nt">a</span><span class="p">.</span><span class="nc">anchor-link</span> <span class="p">{</span> <span class="k">visibility</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span> <span class="p">}</span> <span class="p">.</span><span class="nc">prompt</span><span class="o">,</span> <span class="p">.</span><span class="nc">output_prompt</span> <span class="p">{</span> <span class="k">visibility</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span> <span class="p">}</span> </pre></div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h2 id="Sample-chart">Sample chart<a class="anchor-link" href="proxy.php?url=#Sample-chart">&#182;</a></h2><p>I can't showcase a notebook without throwing in a bonus chart! It's nothing fancy, but wanted to illustrate what is possible to do in these notebooks. I snagged this example from the excellent book "Data Science from Scratch, Second Edition, by Joel Grus (O'Reilly). (C) 2019 Jeol Grus, 978-1-149-04113-9"</p> </div> </div> </div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In&nbsp;[9]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span> <span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span> <span class="o">%</span><span class="k">matplotlib</span> inline <span class="n">variance</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">256</span><span class="p">]</span> <span class="n">bias_squared</span> <span class="o">=</span> <span class="p">[</span><span class="mi">256</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span> <span class="n">total_error</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">variance</span><span class="p">,</span> <span class="n">bias_squared</span><span class="p">)]</span> <span class="n">xs</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">variance</span><span class="p">)]</span> <span class="c1"># We can make multiple calls to plt.plot</span> <span class="c1"># to show multiple series on the same chart</span> <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">variance</span><span class="p">,</span> <span class="s1">&#39;g-&#39;</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s1">&#39;variance&#39;</span><span class="p">)</span> <span class="c1"># green solid line</span> <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">bias_squared</span><span class="p">,</span> <span class="s1">&#39;r-.&#39;</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s1">&#39;bias^2&#39;</span><span class="p">)</span> <span class="c1"># red dot-dashed line</span> <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">total_error</span><span class="p">,</span> <span class="s1">&#39;b:&#39;</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s1">&#39;total error&#39;</span><span class="p">)</span> <span class="c1"># blue dotted line</span> <span class="c1"># Because we&#39;ve assigned labels to each series,</span> <span class="c1"># we can get a legend for free (loc=9 means &quot;top center&quot;)</span> <span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="mi">9</span><span class="p">)</span> <span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s2">&quot;model complexity&quot;</span><span class="p">)</span> <span class="n">plt</span><span class="o">.</span><span class="n">xticks</span><span class="p">([])</span> <span class="n">plt</span><span class="o">.</span><span class="n">title</span><span class="p">(</span><span class="s2">&quot;The Bias-Variance Tradeoff&quot;</span><span class="p">)</span> <span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> </pre></div> </div> </div> </div> <div class="output_wrapper"> <div class="output"> <div class="output_area"> <div class="prompt"></div> <div class="output_png output_subarea "> <img src="data:image/png;base64,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 " > </div> </div> </div> </div> </div> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div><div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> <h2 id="Conclusion">Conclusion<a class="anchor-link" href="proxy.php?url=#Conclusion">&#182;</a></h2><p>So far, I can't complain. This interface does seem a little nicer for some reason. Perhaps I need to change the black theme of my <a href="proxy.php?url=https://code.visualstudio.com/">VSCode</a> back to normal and see if that makes a difference in the "feels" department. The notebook definitely gives me more options, but also takes some away. For instance, I can't use the <a href="proxy.php?url=https://github.com/getpelican/pelican-plugins/tree/master/liquid_tags">liquid_tags</a> Pelican plugin!</p> <p>Like all things, it's going to take some more experimentation to see what works and what doesn't.</p> </div> </div> </div> <script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) { var mathjaxscript = document.createElement('script'); mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#'; mathjaxscript.type = 'text/javascript'; mathjaxscript.src = '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML'; mathjaxscript[(window.opera ? "innerHTML" : "text")] = "MathJax.Hub.Config({" + " config: ['MMLorHTML.js']," + " TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," + " jax: ['input/TeX','input/MathML','output/HTML-CSS']," + " extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," + " displayAlign: 'center'," + " displayIndent: '0em'," + " showMathMenu: true," + " tex2jax: { " + " inlineMath: [ ['$','$'] ], " + " displayMath: [ ['$$','$$'] ]," + " processEscapes: true," + " preview: 'TeX'," + " }, " + " 'HTML-CSS': { " + " linebreaks: { automatic: true, width: '95% container' }, " + " styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'black ! important'} }" + " } " + "}); "; (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript); } </script> MongoDB CLI Intro2020-03-07T12:27:00-06:002020-03-12T23:08:00-05:00Martin Uribetag:clamytoe.github.io,2020-03-07:/articles/2020/Mar/07/mongodb-cli-intro<p>Quick introduction on how to connect to a mondoDB database through the built in mongo cli application</p><h1>Why another article on MongoDB</h1> <p>After publishing my article on <a href="proxy.php?url=https://clamytoe.github.io/articles/2020/Mar/06/how-to-install-mongodb-on-linux">How to install MongoDB on Linux</a> I realized that I should have covered how to access it through the command line! Didn't occur to me that anyone reading it might not want to download and install <a href="proxy.php?url=https://robomongo.org/">Robo 3T</a>!</p> <p>Sorry about that!</p> <h2>Intro into the mongo cli app</h2> <p>It's simple enough. Now I'm no expert but this is what I discovered just playing around with it.</p> <div class="highlight"><pre><span></span><code>(<span class="nb">base</span>) ➜ ~ <span class="n">mongo</span> <span class="n">MongoDB</span> <span class="nb">shell</span> <span class="nb">version</span> <span class="n">v4</span><span class="mf">.2.3</span> <span class="n">connecting</span> <span class="n">to:</span> <span class="n">mongodb:</span>//<span class="mf">127.0.0.1</span>:<span class="mi">27017</span>/?<span class="n">compressors</span>=<span class="n">disabled</span><span class="nv">&amp;gssapiServiceName</span>=<span class="n">mongodb</span> <span class="n">Implicit</span> <span class="n">session:</span> <span class="n">session</span> { <span class="s">&quot;id&quot;</span> : <span class="n">UUID</span>(<span class="s">&quot;39eaf67e-7de5-4874-ba3e-a6673080cfbc&quot;</span>) } <span class="n">MongoDB</span> <span class="n">server</span> <span class="n">version:</span> <span class="mf">4.2.3</span> <span class="n">Welcome</span> <span class="nb">to</span> <span class="n">the</span> <span class="n">MongoDB</span> <span class="nb">shell</span>. <span class="n">For</span> <span class="n">interactive</span> <span class="n">help</span>, <span class="nb">type</span> <span class="s">&quot;help&quot;</span>. <span class="n">For</span> <span class="n">more</span> <span class="n">comprehensive</span> <span class="n">documentation</span>, <span class="n">see</span> <span class="n">http:</span>//<span class="n">docs</span>.<span class="n">mongodb</span>.<span class="n">org</span>/ <span class="n">Questions</span>? <span class="n">Try</span> <span class="n">the</span> <span class="n">support</span> <span class="n">group</span> <span class="n">http:</span>//<span class="n">groups</span>.<span class="n">google</span>.<span class="n">com</span><span class="o">/</span><span class="n">group</span><span class="o">/</span><span class="n">mongodb-user</span> <span class="n">Server</span> <span class="k">has</span> <span class="n">startup</span> <span class="n">warnings:</span> <span class="mi">2020</span><span class="o">-</span><span class="mo">03</span><span class="o">-</span><span class="mo">06</span><span class="n">T20:35:19</span><span class="mf">.314</span><span class="o">-</span><span class="mo">0600</span> <span class="n">I</span> <span class="n">STORAGE</span> [<span class="n">initandlisten</span>] <span class="mi">2020</span><span class="o">-</span><span class="mo">03</span><span class="o">-</span><span class="mo">06</span><span class="n">T20:35:19</span><span class="mf">.314</span><span class="o">-</span><span class="mo">0600</span> <span class="n">I</span> <span class="n">STORAGE</span> [<span class="n">initandlisten</span>] ** <span class="n">WARNING:</span> <span class="n">Using</span> <span class="n">the</span> <span class="n">XFS</span> <span class="n">filesystem</span> <span class="k">is</span> <span class="n">strongly</span> <span class="n">recommended</span> <span class="k">with</span> <span class="n">the</span> <span class="n">WiredTiger</span> <span class="n">storage</span> <span class="n">engine</span> <span class="mi">2020</span><span class="o">-</span><span class="mo">03</span><span class="o">-</span><span class="mo">06</span><span class="n">T20:35:19</span><span class="mf">.314</span><span class="o">-</span><span class="mo">0600</span> <span class="n">I</span> <span class="n">STORAGE</span> [<span class="n">initandlisten</span>] ** <span class="n">See</span> <span class="n">http:</span>//<span class="n">dochub</span>.<span class="n">mongodb</span>.<span class="n">org</span><span class="o">/</span><span class="n">core</span><span class="o">/</span><span class="n">prodnotes-filesystem</span> <span class="mi">2020</span><span class="o">-</span><span class="mo">03</span><span class="o">-</span><span class="mo">06</span><span class="n">T20:35:22</span><span class="mf">.538</span><span class="o">-</span><span class="mo">0600</span> <span class="n">I</span> <span class="k">CONTROL</span> [<span class="n">initandlisten</span>] <span class="mi">2020</span><span class="o">-</span><span class="mo">03</span><span class="o">-</span><span class="mo">06</span><span class="n">T20:35:22</span><span class="mf">.539</span><span class="o">-</span><span class="mo">0600</span> <span class="n">I</span> <span class="k">CONTROL</span> [<span class="n">initandlisten</span>] ** <span class="n">WARNING:</span> <span class="n">Access</span> <span class="n">control</span> <span class="k">is</span> <span class="nb">not</span> <span class="n">enabled</span> <span class="k">for</span> <span class="n">the</span> <span class="n">database</span>. <span class="mi">2020</span><span class="o">-</span><span class="mo">03</span><span class="o">-</span><span class="mo">06</span><span class="n">T20:35:22</span><span class="mf">.539</span><span class="o">-</span><span class="mo">0600</span> <span class="n">I</span> <span class="k">CONTROL</span> [<span class="n">initandlisten</span>] ** <span class="n">Read</span> <span class="o">and</span> <span class="nb">write</span> <span class="n">access</span> <span class="nb">to</span> <span class="n">data</span> <span class="o">and</span> <span class="n">configuration</span> <span class="k">is</span> <span class="n">unrestricted</span>. <span class="mi">2020</span><span class="o">-</span><span class="mo">03</span><span class="o">-</span><span class="mo">06</span><span class="n">T20:35:22</span><span class="mf">.539</span><span class="o">-</span><span class="mo">0600</span> <span class="n">I</span> <span class="k">CONTROL</span> [<span class="n">initandlisten</span>] --- <span class="n">Enable</span> <span class="n">MongoDB&#39;s</span> <span class="n">free</span> <span class="n">cloud-based</span> <span class="n">monitoring</span> <span class="n">service</span>, <span class="n">which</span> <span class="n">will</span> <span class="nb">then</span> <span class="nb">receive</span> <span class="o">and</span> <span class="n">display</span> <span class="n">metrics</span> <span class="n">about</span> <span class="n">your</span> <span class="n">deployment</span> (<span class="n">disk</span> <span class="n">utilization</span>, <span class="n">CPU</span>, <span class="nb">operation</span> <span class="n">statistics</span>, <span class="n">etc</span>). <span class="n">The</span> <span class="n">monitoring</span> <span class="n">data</span> <span class="n">will</span> <span class="n">be</span> <span class="n">available</span> <span class="n">on</span> <span class="n">a</span> <span class="n">MongoDB</span> <span class="n">website</span> <span class="k">with</span> <span class="n">a</span> <span class="nb">unique</span> <span class="n">URL</span> <span class="n">accessible</span> <span class="nb">to</span> <span class="n">you</span> <span class="o">and</span> <span class="n">anyone</span> <span class="n">you</span> <span class="nb">share</span> <span class="n">the</span> <span class="n">URL</span> <span class="k">with</span>. <span class="n">MongoDB</span> <span class="n">may</span> <span class="k">use</span> <span class="n">this</span> <span class="n">information</span> <span class="nb">to</span> <span class="k">make</span> <span class="n">product</span> <span class="n">improvements</span> <span class="o">and</span> <span class="nb">to</span> <span class="n">suggest</span> <span class="n">MongoDB</span> <span class="n">products</span> <span class="o">and</span> <span class="n">deployment</span> <span class="n">options</span> <span class="nb">to</span> <span class="n">you</span>. <span class="n">To</span> <span class="n">enable</span> <span class="n">free</span> <span class="n">monitoring</span>, <span class="nb">run</span> <span class="n">the</span> <span class="n">following</span> <span class="n">command:</span> <span class="n">db</span>.<span class="n">enableFreeMonitoring</span>() <span class="n">To</span> <span class="n">permanently</span> <span class="n">disable</span> <span class="n">this</span> <span class="n">reminder</span>, <span class="nb">run</span> <span class="n">the</span> <span class="n">following</span> <span class="n">command:</span> <span class="n">db</span>.<span class="n">disableFreeMonitoring</span>() --- &gt; </code></pre></div> <p>As you can tell, I haven't secured the database yet. Let's continue exploring though and see what the <em>help</em> command will show us.</p> <div class="highlight"><pre><span></span><code><span class="o">&gt;</span><span class="w"> </span><span class="n">help</span><span class="w"></span> <span class="w"> </span><span class="n">db</span><span class="p">.</span><span class="n">help</span><span class="p">()</span><span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">db</span><span class="w"> </span><span class="n">methods</span><span class="w"></span> <span class="w"> </span><span class="n">db</span><span class="p">.</span><span class="n">mycoll</span><span class="p">.</span><span class="n">help</span><span class="p">()</span><span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">collection</span><span class="w"> </span><span class="n">methods</span><span class="w"></span> <span class="w"> </span><span class="n">sh</span><span class="p">.</span><span class="n">help</span><span class="p">()</span><span class="w"> </span><span class="n">sharding</span><span class="w"> </span><span class="n">helpers</span><span class="w"></span> <span class="w"> </span><span class="n">rs</span><span class="p">.</span><span class="n">help</span><span class="p">()</span><span class="w"> </span><span class="n">replica</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="n">helpers</span><span class="w"></span> <span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="k">admin</span><span class="w"> </span><span class="n">administrative</span><span class="w"> </span><span class="n">help</span><span class="w"></span> <span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="k">connect</span><span class="w"> </span><span class="n">connecting</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">db</span><span class="w"> </span><span class="n">help</span><span class="w"></span> <span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="n">keys</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="n">shortcuts</span><span class="w"></span> <span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="n">misc</span><span class="w"> </span><span class="n">misc</span><span class="w"> </span><span class="n">things</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">know</span><span class="w"></span> <span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="n">mr</span><span class="w"> </span><span class="n">mapreduce</span><span class="w"></span> <span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">dbs</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="k">database</span><span class="w"> </span><span class="k">names</span><span class="w"></span> <span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">collections</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">collections</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="k">current</span><span class="w"> </span><span class="k">database</span><span class="w"></span> <span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">users</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">users</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="k">current</span><span class="w"> </span><span class="k">database</span><span class="w"></span> <span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">profile</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">most</span><span class="w"> </span><span class="n">recent</span><span class="w"> </span><span class="k">system</span><span class="p">.</span><span class="n">profile</span><span class="w"> </span><span class="n">entries</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="nc">time</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="n">ms</span><span class="w"></span> <span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">logs</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">accessible</span><span class="w"> </span><span class="n">logger</span><span class="w"> </span><span class="k">names</span><span class="w"></span> <span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="nf">log</span><span class="w"> </span><span class="o">[</span><span class="n">name</span><span class="o">]</span><span class="w"> </span><span class="n">prints</span><span class="w"> </span><span class="k">out</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">last</span><span class="w"> </span><span class="n">segment</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nf">log</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">memory</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;global&#39;</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="k">default</span><span class="w"></span> <span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="o">&lt;</span><span class="nf">db_name</span><span class="o">&gt;</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="k">current</span><span class="w"> </span><span class="k">database</span><span class="w"></span> <span class="w"> </span><span class="n">db</span><span class="p">.</span><span class="n">foo</span><span class="p">.</span><span class="n">find</span><span class="p">()</span><span class="w"> </span><span class="n">list</span><span class="w"> </span><span class="n">objects</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">collection</span><span class="w"> </span><span class="n">foo</span><span class="w"></span> <span class="w"> </span><span class="n">db</span><span class="p">.</span><span class="n">foo</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="w"> </span><span class="err">{</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="err">:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="err">}</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="n">list</span><span class="w"> </span><span class="n">objects</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="w"></span> <span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="k">result</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">last</span><span class="w"> </span><span class="n">line</span><span class="w"> </span><span class="n">evaluated</span><span class="p">;</span><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">further</span><span class="w"> </span><span class="k">iterate</span><span class="w"></span> <span class="w"> </span><span class="n">DBQuery</span><span class="p">.</span><span class="n">shellBatchSize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">items</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">display</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">shell</span><span class="w"></span> <span class="w"> </span><span class="k">exit</span><span class="w"></span> <span class="o">&gt;</span><span class="w"></span> </code></pre></div> <p>Ok, looks simple enough. That <em>show dbs</em> sounds interesting.</p> <div class="highlight"><pre><span></span><code><span class="err">&gt; show dbs</span> <span class="err">admin 0.000GB</span> <span class="err">config 0.000GB</span> <span class="err">local 0.000GB</span> <span class="err">snake_bnb 0.000GB</span> <span class="err">&gt;</span> </code></pre></div> <p>Ok, I see the <em>snake_bnb</em> database that we created during the <a href="proxy.php?url=https://freemongodbcourse.com/">MongoDB Quickstart</a> tutorial. Let's see what's in it with that <code>use &lt;db_name&gt;</code> command, followed by <code>show collections</code>.</p> <div class="highlight"><pre><span></span><code><span class="err">&gt; use snake_bnb</span> <span class="err">switched to db snake_bnb</span> <span class="err">&gt; show collections</span> <span class="err">owners</span> <span class="err">&gt;</span> </code></pre></div> <p>Now if I reference the <em>help</em> command again, I see that I can use <code>db.foo.find()</code> to do a dump of everything in it.</p> <div class="highlight"><pre><span></span><code><span class="err">&gt; db.owners.find()</span> <span class="err">{ &quot;_id&quot; : ObjectId(&quot;5e6243ca45ed06e7f09781fc&quot;), &quot;registered_date&quot; : ISODate(&quot;2020-03-06T06:36:26.108Z&quot;), &quot;name&quot; : &quot;clamytoe&quot;, &quot;email&quot; : &quot;[email protected]&quot;, &quot;snake_ids&quot; : [ ], &quot;cage_ids&quot; : [ ] }</span> <span class="err">&gt;</span> </code></pre></div> <p>If I had more entries, it looks like I could search for specific entries with <code>db.foo.find( { a : 1 } )</code>. I only have one record, so I should just get the same result as the previous command.</p> <div class="highlight"><pre><span></span><code><span class="err">&gt; db.owners.find( {&quot;name&quot;: &quot;clamytoe&quot;} )</span> <span class="err">{ &quot;_id&quot; : ObjectId(&quot;5e6243ca45ed06e7f09781fc&quot;), &quot;registered_date&quot; : ISODate(&quot;2020-03-06T06:36:26.108Z&quot;), &quot;name&quot; : &quot;clamytoe&quot;, &quot;email&quot; : &quot;[email protected]&quot;, &quot;snake_ids&quot; : [ ], &quot;cage_ids&quot; : [ ] }</span> <span class="err">&gt;</span> </code></pre></div> <p>To exit, we just use the <em>exit</em> command.</p> <div class="highlight"><pre><span></span><code><span class="err">&gt; exit</span> <span class="err">bye</span> <span class="err">(base) ➜ ~</span> </code></pre></div> <h2>Conclusion</h2> <p>There you have it. A quick intro into using the <em>mongo</em> cli app. Simple enough for quick exploration of your databases. I'll probably be spending more time in <em>Robo 3T</em> though. Not only can your explore the databases through a GUI, but you can also easily add, delete, and update and entries with it.</p> <p><img alt="robo-edit" src="proxy.php?url=https://clamytoe.github.io/images/robo-edit.png"></p>How to install MongoDB on Linux2020-03-06T12:53:00-06:002020-03-12T21:52:00-05:00Martin Uribetag:clamytoe.github.io,2020-03-06:/articles/2020/Mar/06/how-to-install-mongodb-on-linux<p>How to prepare to start working with MongoDB</p><h1>Learning about MongoDB</h1> <p>So the other day I started going through Michael Kennedy's free <a href="proxy.php?url=https://training.talkpython.fm/courses/details/mongodb-python-quickstart-mongoengine">MongoDB Quickstart</a> course. Although he covered the Python code portions, he left getting everything setup, up to you. If you're on a Linux machine that uses APT, then keep on reading.</p> <h2>Getting MongoDB</h2> <p>I could have gone over to MongoDB's <a href="proxy.php?url=https://www.mongodb.com/download-center/community">download page</a>, and selected the correct version for my setup, but I wanted to stick with the package manager on my machine. The latest version as of this writing was <em>4.2</em>. The first thing I had to do was find their <a href="proxy.php?url=https://www.mongodb.org/static/pgp/server-4.2.asc">public key</a> and import it to APT.</p> <h3>Importing MongoDB's security key</h3> <p>To make sure that I had the required gnupg package installed, I ran the following command: <code>sudo apt-get install gnupg</code>. Once that was taken care off, I imported the key:</p> <div class="highlight"><pre><span></span><code>wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc <span class="p">|</span> sudo apt-key add - </code></pre></div> <h3>Creating a new source for APT</h3> <p>The next thing I did was to find the codename for my system's release. I did that with the following command: <code>grep -i /etc/os-release</code></p> <div class="highlight"><pre><span></span><code><span class="nv">VERSION_CODENAME</span><span class="o">=</span>tricia <span class="nv">UBUNTU_CODENAME</span><span class="o">=</span>bionic </code></pre></div> <p>There it was. My Linux Mint system is based on <em>bionic</em>. Now to create an entry for MongoDB in APT's source list directory <code>/etc/apt/sources.list.d/</code>, I created <code>mongodb-org.4.2.list</code> with the following content:</p> <div class="highlight"><pre><span></span><code>deb <span class="o">[</span><span class="nv">arch</span><span class="o">=</span>amd64<span class="o">]</span> https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse </code></pre></div> <blockquote> <p>I had to look at other entries in there until I got the format correct. Don't worry, if you get it wrong, it will let you know when you try to do an update!</p> </blockquote> <h3>Update APT</h3> <p>At this point it was time to update APT and install mongoDB:</p> <div class="highlight"><pre><span></span><code>sudo apt-get update sudo apt-get install -y mongodb-org </code></pre></div> <h2>Starting MongoDB</h2> <p>Once everything was installed, it was time to fire up the service so that I could connect to it.</p> <div class="highlight"><pre><span></span><code>sudo systemctl start mongod </code></pre></div> <p>Now, that command will start the server for this session only. Which means that the next time that I reboot, I will have to start it again. I wanted the service to always be available, so I did the following:</p> <div class="highlight"><pre><span></span><code>sudo systemctl <span class="nb">enable</span> mongod.service sudo systemctl daemon-reload sudo systemctl restart mongod.service </code></pre></div> <p>To verify that it was running, I used this command: <code>systemctl status mongod</code></p> <div class="highlight"><pre><span></span><code>● mongod.service - MongoDB Database Server Loaded: loaded <span class="o">(</span>/lib/systemd/system/mongod.service<span class="p">;</span> enabled<span class="p">;</span> vendor preset: e Active: active <span class="o">(</span>running<span class="o">)</span> since Fri <span class="m">2020</span>-03-06 <span class="m">20</span>:35:16 CST<span class="p">;</span> 12s ago Docs: https://docs.mongodb.org/manual Main PID: <span class="m">8569</span> <span class="o">(</span>mongod<span class="o">)</span> CGroup: /system.slice/mongod.service └─8569 /usr/bin/mongod --config /etc/mongod.conf Mar <span class="m">06</span> <span class="m">20</span>:35:16 SERENiTY systemd<span class="o">[</span><span class="m">1</span><span class="o">]</span>: Started MongoDB Database Server. </code></pre></div> <p>If you want the server to restart after a crash, you would need to add <code>Restart=always</code> under the <em>[Service]</em> section of <code>/lib/systemd/system/mongod.service</code>. I didn't add that.</p> <blockquote> <p>At this point, a reboot would be useful to make sure that it starts up at boot.</p> </blockquote> <p>If you decide that you don't want it running all of the time, just disable it.</p> <div class="highlight"><pre><span></span><code>sudo systemctl stop mongod sudo systemctl disable mongod </code></pre></div> <h2>Installing Robo 3T</h2> <p>Michael recommended <a href="proxy.php?url=https://robomongo.org">Robo 3t</a> so I decided to give it a try. I went to their <a href="proxy.php?url=https://robomongo.org/download">download page</a> and selected the linux version. My connection at home is terrible, so I try not to download anything through the browser. I copied the link and used <code>wget</code> instead.</p> <div class="highlight"><pre><span></span><code><span class="nb">cd</span> ~/Downloads wget -c https://download-test.robomongo.org/linux/robo3t-1.3.1-linux-x86_64-7419c406.tar.gz </code></pre></div> <p>Once it had finished downloading, I extracted the file to my home directory:</p> <div class="highlight"><pre><span></span><code>mkdir ~/robo3t tar -xvzf robo3t-1.3.1-linux-x86_64-7419c406.tar.gz -C ~/robo3t </code></pre></div> <h3>Adding a custom menu entry</h3> <p>I like to add all of my programs to the "start" menu, so I then did the following:</p> <div class="highlight"><pre><span></span><code><span class="nb">cd</span> ~/robo3t wget https://robomongo.org/static/robomongo-128x128-129df2f1.png -O robomongo.png ln -s robo3t-1.3.1-linux-x86_64-7419c406/bin/robo3t robo3t </code></pre></div> <ul> <li> <p>Right-click the Mint start button</p> <p><img alt="mint" src="proxy.php?url=https://clamytoe.github.io/images/linux-menu.png"></p> </li> <li> <p>Click on <em>Configure...</em></p> <p><img alt="configure" src="proxy.php?url=https://clamytoe.github.io/images/right-click-menu.png"></p> </li> <li> <p>Click on the <em>Menu</em> button from the dialog window and then click on <strong>Open the menu editor</strong></p> <p><img alt="menu-widget" src="proxy.php?url=https://clamytoe.github.io/images/menu-widget.png"></p> </li> <li> <p>From the <em>Main Menu</em> widget, click on the <strong>Programming</strong> section and then click on the <em>New Item</em> button</p> <p><img alt="programming" src="proxy.php?url=https://clamytoe.github.io/images/main-menu-widget.png"></p> <p><img alt="launcher" src="proxy.php?url=https://clamytoe.github.io/images/launcher-properties.png"></p> <ul> <li>Fill in the <em>Launcher Properties</em>.</li> <li>Select the <code>robomongo.png</code> image that we downloaded previously.</li> <li>Give it a <em>Name</em>, <strong>Robo 3T</strong>.</li> <li>For the <em>Command</em> select the symlink that we created previously.</li> <li>Enter whatever you want under <em>Comment</em> and click <strong>OK</strong>.</li> </ul> </li> <li> <p>Close all of the open dialogs.</p> </li> <li> <p>Admire you're awesomeness</p> <p><img alt="new-entry" src="proxy.php?url=https://clamytoe.github.io/images/new-entry.png"></p> </li> </ul> <blockquote> <p>Before you can start using <em>Robo 3T</em>, you will have to get to the point of the tutorial where you add a user.</p> </blockquote> <p>Once you've made an entry, go ahead and start it <em>Robo 3T</em>. You will be presented with a <em>MongoDB Connections</em> dialog. Click on the <em>Create</em> link to open up the <em>Connection Settings</em> dialog.</p> <p><img alt="connections" src="proxy.php?url=https://clamytoe.github.io/images/connection-settings.png"></p> <p>For the <em>Name</em> enter <em>snake_bnb</em>, the name of the database that was created in the tutorial and <em>Save</em>.</p> <p><img alt="db-connections" src="proxy.php?url=https://clamytoe.github.io/images/robo3t-db-connections.png"></p> <p>Click on the <em>Connect</em> button to access it.</p> <h2>Conclusion</h2> <p>Quite a few steps to get here, but now I am setup and ready to continue on with the course and hopefully you will be too!</p>Playing around with adding plugins to Pelican2020-03-03T13:20:00-06:002020-03-03T13:20:00-06:00Martin Uribetag:clamytoe.github.io,2020-03-03:/articles/2020/Mar/03/pelican-plugins<p>Wanted to add some extra styling to my blog posts</p><h1>Some parts of my blog were looking pretty <em>blah</em></h1> <h2>Math syntax</h2> <p>I've been tweaking my blog like crazy. Just making minor changes here and there. I wanted to make things look a little better.</p> <p>For instance, for my <a href="proxy.php?url=https://clamytoe.github.io/articles/2020/Feb/13/rescaling-values">rescaling</a> article, I had the following mathematical syntax:</p> <blockquote> <p><strong>ScaledQuantity = (ActualQuantity-MinQuantity)/(MaxQuantity-MinQuantity)</strong></p> </blockquote> <p>What the hell is that? That looks horrible! I know that markdown supports <a href="proxy.php?url=https://www.latex-project.org/">Latex</a>, so I looked and found this awesome resource: <a href="proxy.php?url=http://csrgxtu.github.io/2015/03/20/Writing-Mathematic-Fomulars-in-Markdown/">Writing Mathematic Formulars in Markdown</a></p> <p>ScaledQuantity = \frac{ActualQuantity-MinQuantity}{MaxQuantity-MinQuantity}</p> <p>Um, not quite there yet. Something was up. I searched to see if Pelican had any plugins for rendering math and sure enough, found that <a href="proxy.php?url=https://github.com/getpelican/pelican-plugins">pelican-plugins</a> included <em>render_math</em>. I followed the instructions and cloned the repo into my blog directory. Probably should have dropped it somewhere outside of it, but I added it to my <code>.gitignore</code> and it's fine...</p> <p>I added the following to my <code>pelicanconf.py</code> file:</p> <div class="highlight"><pre><span></span><code><span class="n">PLUGIN_PATHS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;./pelican-plugins&#39;</span><span class="p">]</span> <span class="n">PLUGINS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;render_math&#39;</span><span class="p">]</span> </code></pre></div> <p>I tried the previous code once again and was still getting the same results. After some more reading I found that you have to enclose the mathematical equation within double $.</p> <div class="math">$$ScaledQuantity = \frac{ActualQuantity-MinQuantity}{MaxQuantity-MinQuantity}$$</div> <blockquote> <p><strong>NICE!</strong></p> </blockquote> <h2>Markdown tables</h2> <p>My original tables were so bland that it was almost hard to tell that they were even tables! Unfortunately I didn't save a screenshot of it, so I can't show you what they looked like, but I can definitely show you the end result! Thanks to <a href="proxy.php?url=https://www.mockaroo.com/">mockaroo</a> I was able to generate some mock data relatively quickly.</p> <table> <thead> <tr> <th align="right">id</th> <th align="left">first_name</th> <th align="left">last_name</th> <th align="left">email</th> <th align="center">gender</th> <th align="right">ip_address</th> </tr> </thead> <tbody> <tr> <td align="right">1</td> <td align="left">Odilia</td> <td align="left">O'Meara</td> <td align="left">[email protected]</td> <td align="center">Female</td> <td align="right">244.53.86.185</td> </tr> <tr> <td align="right">2</td> <td align="left">Marcelo</td> <td align="left">Tunna</td> <td align="left">[email protected]</td> <td align="center">Male</td> <td align="right">128.140.17.56</td> </tr> <tr> <td align="right">3</td> <td align="left">Farris</td> <td align="left">Roston</td> <td align="left">[email protected]</td> <td align="center">Male</td> <td align="right">111.104.78.209</td> </tr> <tr> <td align="right">4</td> <td align="left">Gene</td> <td align="left">Benzies</td> <td align="left">[email protected]</td> <td align="center">Male</td> <td align="right">67.169.248.21</td> </tr> <tr> <td align="right">5</td> <td align="left">Ellsworth</td> <td align="left">Goodered</td> <td align="left">[email protected]</td> <td align="center">Male</td> <td align="right">227.48.46.133</td> </tr> <tr> <td align="right">6</td> <td align="left">Jan</td> <td align="left">Garnsworth</td> <td align="left">[email protected]</td> <td align="center">Male</td> <td align="right">138.13.211.142</td> </tr> <tr> <td align="right">7</td> <td align="left">Robby</td> <td align="left">Rosenstengel</td> <td align="left">[email protected]</td> <td align="center">Female</td> <td align="right">105.76.137.198</td> </tr> <tr> <td align="right">8</td> <td align="left">Granthem</td> <td align="left">Rymer</td> <td align="left">[email protected]</td> <td align="center">Male</td> <td align="right">15.60.66.25</td> </tr> <tr> <td align="right">9</td> <td align="left">Claire</td> <td align="left">Stuther</td> <td align="left">[email protected]</td> <td align="center">Female</td> <td align="right">5.36.242.109</td> </tr> <tr> <td align="right">10</td> <td align="left">Maggy</td> <td align="left">Jearum</td> <td align="left">[email protected]</td> <td align="center">Female</td> <td align="right">179.12.188.75</td> </tr> </tbody> </table> <h2>CSS changes</h2> <p>I hope it doesn't bite me in the butt later on, but I'm forcing the table headers into uppercase. In case anyone is curious, this are the styles that I added to the theme:</p> <div class="highlight"><pre><span></span><code><span class="nt">table</span> <span class="p">{</span> <span class="k">border-collapse</span><span class="p">:</span> <span class="kc">collapse</span><span class="p">;</span> <span class="k">margin-left</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span> <span class="k">margin-right</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span> <span class="k">width</span><span class="p">:</span> <span class="mi">90</span><span class="kt">%</span><span class="p">;</span> <span class="p">}</span> <span class="nt">td</span><span class="o">,</span> <span class="nt">th</span> <span class="p">{</span> <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#ddd</span><span class="p">;</span> <span class="k">padding</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span> <span class="p">}</span> <span class="nt">tr</span><span class="p">:</span><span class="nd">nth-child</span><span class="o">(</span><span class="nt">even</span><span class="o">)</span><span class="p">{</span><span class="k">background-color</span><span class="p">:</span> <span class="mh">#f2f2f2</span><span class="p">;}</span> <span class="nt">tr</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span><span class="k">background-color</span><span class="p">:</span> <span class="mh">#ddd</span><span class="p">;}</span> <span class="nt">th</span> <span class="p">{</span> <span class="k">padding-top</span><span class="p">:</span> <span class="mi">12</span><span class="kt">px</span><span class="p">;</span> <span class="k">padding-bottom</span><span class="p">:</span> <span class="mi">12</span><span class="kt">px</span><span class="p">;</span> <span class="k">text-transform</span><span class="p">:</span> <span class="kc">uppercase</span><span class="p">;</span> <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#607D8B</span><span class="p">;</span> <span class="k">color</span><span class="p">:</span> <span class="kc">white</span><span class="p">;</span> <span class="p">}</span> <span class="p">.</span><span class="nc">math</span> <span class="p">{</span> <span class="k">font-size</span><span class="p">:</span> <span class="kc">medium</span><span class="p">;</span> <span class="p">}</span> </code></pre></div> <blockquote> <p>The default <em>math</em> syntax rendering was kind of small, so I changed the <code>font-size</code> to <em>medium</em>.</p> </blockquote> <h2>On a side note</h2> <p>I noticed that some of my changes wouldn't take effect until after I removed the <em><strong>pycache</strong></em> and <em>.pytest_cache</em> directories. In order to automate this, I modified my <em>Makefile</em>'s <code>clean</code> code to the following:</p> <div class="highlight"><pre><span></span><code><span class="nf">clean</span><span class="o">:</span> <span class="o">[</span> ! -d <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> <span class="o">]</span> <span class="o">||</span> rm -rf <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> rm -rf __pycache__ rm -rf .pytest_cache </code></pre></div> <h2>Conclusion</h2> <p>This web development stuff is pretty complex. There are lots of moving parts, so I give all of my fellow web developers a high-five for being so awesome! I can see how this can be pretty overwhelming if you're new to this, but just keep at it and you'll get there.</p> <p>This old dog can still learn some new tricks!</p> <script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) { var align = "left", indent = "0em", linebreak = "true"; if (true) { align = (screen.width < 1125) ? "left" : align; indent = (screen.width < 1125) ? "0em" : indent; linebreak = (screen.width < 1125) ? 'true' : linebreak; } var mathjaxscript = document.createElement('script'); mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#'; mathjaxscript.type = 'text/javascript'; mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML'; var configscript = document.createElement('script'); configscript.type = 'text/x-mathjax-config'; configscript[(window.opera ? "innerHTML" : "text")] = "MathJax.Hub.Config({" + " config: ['MMLorHTML.js']," + " TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," + " jax: ['input/TeX','input/MathML','output/HTML-CSS']," + " extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," + " displayAlign: '"+ align +"'," + " displayIndent: '"+ indent +"'," + " showMathMenu: true," + " messageStyle: 'normal'," + " tex2jax: { " + " inlineMath: [ ['\\\\(','\\\\)'] ], " + " displayMath: [ ['$$','$$'] ]," + " processEscapes: true," + " preview: 'TeX'," + " }, " + " 'HTML-CSS': { " + " availableFonts: ['STIX', 'TeX']," + " preferredFont: 'STIX'," + " styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," + " linebreaks: { automatic: "+ linebreak +", width: '90% container' }," + " }, " + "}); " + "if ('default' !== 'default') {" + "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" + "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" + "VARIANT['normal'].fonts.unshift('MathJax_default');" + "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" + "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" + "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" + "});" + "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" + "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" + "VARIANT['normal'].fonts.unshift('MathJax_default');" + "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" + "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" + "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" + "});" + "}"; (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript); (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript); } </script>Finally getting the hang of working with Pelican!2020-02-28T09:50:00-06:002020-03-09T09:20:00-05:00Martin Uribetag:clamytoe.github.io,2020-02-28:/articles/2020/Feb/28/pelican<p>Nothing beats learning something new than just jumping in and playing around with it!</p><h1>Stumbling into a nice Pelican workflow</h1> <p>If you've seen my <a href="proxy.php?url=https://github.com/clamytoe/clamytoe.github.io">source repo</a> for this site, you know that I followed <a href="proxy.php?url=https://opensource.com/users/jnyjny">Erik O'Shaughnessy's</a> <em>Run your blog on GitHub Pages with Python</em> <a href="proxy.php?url=https://opensource.com/article/19/5/run-your-blog-github-pages-python">tutorial</a>. It's a great tutorial, I specifically love his <strong>one weird trick</strong>, but it leaves so many unanswered questions. Go read it now if you haven't and we'll pick up where he leaves off.</p> <p>Luckily he does provide the link to the official <a href="proxy.php?url=https://docs.getpelican.com/">documentation</a>. It was through that link that I found Pelican's tutorials <a href="proxy.php?url=https://github.com/getpelican/pelican/wiki/Tutorials">repo</a>. Specifically <a href="proxy.php?url=https://www.fullstackpython.com/about-author.html">Matt Makai's</a> introductory <a href="proxy.php?url=https://www.fullstackpython.com/blog/generating-static-websites-pelican-jinja2-markdown.html">tutorial</a>. I'll admit, that I only skimmed over it, it's kind of long. I was looking for anything that would jump out at me. What caught my eye was how simple it all was to automate things with <code>make</code>!</p> <p>I took a quick look at mine and saw what commands were defined.</p> <div class="highlight"><pre><span></span><code><span class="err">Makefile</span> <span class="err">for</span> <span class="err">a</span> <span class="err">pelican</span> <span class="err">Web</span> <span class="err">site</span> <span class="nf">Usage</span><span class="o">:</span> make html <span class="o">(</span>re<span class="o">)</span>generate the web site make clean remove the generated files make regenerate regenerate files upon modification make publish generate using production settings make serve <span class="o">[</span><span class="nv">PORT</span><span class="o">=</span><span class="m">8000</span><span class="o">]</span> serve site at http://localhost:8000 make serve-global <span class="o">[</span><span class="nv">SERVER</span><span class="o">=</span><span class="m">0</span>.0.0.0<span class="o">]</span> serve <span class="o">(</span>as root<span class="o">)</span> to :80 make devserver <span class="o">[</span><span class="nv">PORT</span><span class="o">=</span><span class="m">8000</span><span class="o">]</span> serve and regenerate together make ssh_upload upload the web site via SSH make rsync_upload upload the web site via rsync+ssh make github upload the web site via gh-pages <span class="err">Set</span> <span class="err">the</span> <span class="err">DEBUG</span> <span class="err">variable</span> <span class="err">to</span> <span class="err">1</span> <span class="err">to</span> <span class="err">enable</span> <span class="err">debugging,</span> <span class="err">e.g.</span> <span class="err">make</span> <span class="nv">DEBUG</span><span class="o">=</span><span class="m">1</span> html <span class="err">Set</span> <span class="err">the</span> <span class="err">RELATIVE</span> <span class="err">variable</span> <span class="err">to</span> <span class="err">1</span> <span class="err">to</span> <span class="err">enable</span> <span class="err">relative</span> <span class="err">urls</span> </code></pre></div> <p>Looking at the <code>Makefile</code> source is where I noticed that for local development, it just uses the <code>pelicanconf.py</code> script, but for publishing it uses <code>publishconf.py</code>.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">Makefile</span><span class="liquid-tags-code-lines">[Lines 8-9]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="nv">CONFFILE</span><span class="o">=</span><span class="k">$(</span>BASEDIR<span class="k">)</span>/pelicanconf.py <span class="nv">PUBLISHCONF</span><span class="o">=</span><span class="k">$(</span>BASEDIR<span class="k">)</span>/publishconf.py </code></pre></div> </figure> <p>Looking at <code>publishconf.py</code> I saw that it actually imports <code>pelicanconf.py</code> and then overrides some of the variables.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">publishconf.py</span><span class="liquid-tags-code-lines">[Lines 12-19]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">pelicanconf</span> <span class="kn">import</span> <span class="o">*</span> <span class="c1"># If your site is available via HTTPS, make sure SITEURL begins with https://</span> <span class="n">SITEURL</span> <span class="o">=</span> <span class="s2">&quot;https://clamytoe.github.io&quot;</span> <span class="n">RELATIVE_URLS</span> <span class="o">=</span> <span class="kc">False</span> <span class="n">FEED_ALL_ATOM</span> <span class="o">=</span> <span class="s2">&quot;feeds/all.atom.xml&quot;</span> <span class="n">CATEGORY_FEED_ATOM</span> <span class="o">=</span> <span class="s2">&quot;feeds/</span><span class="si">{slug}</span><span class="s2">.atom.xml&quot;</span> </code></pre></div> </figure> <blockquote> <p><strong>AHA!</strong> This is were I had a eurika moment!</p> </blockquote> <p>I had noticed a problem with my site. Whenever I clicked on the header link, instead of taking me back to the home page it would just reload whatever page I was on. I was able to get around that by just specifying the <code>SITE_URL</code> variable in <code>pelicanconf.py</code>. Unfortunately, that broke testing the blog locally because all the links would point to the live site on GitHub!</p> <p>What I'm trying to say here is that in I was able to resolve both of these issues by making the following changes.</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">pelicanconf.py</span><span class="liquid-tags-code-lines">[Lines 16-16]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="n">SITEURL</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span> </code></pre></div> </figure> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">publishconf.py</span><span class="liquid-tags-code-lines">[Lines 12-12]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">pelicanconf</span> <span class="kn">import</span> <span class="o">*</span> </code></pre></div> </figure> <p>Another issue that I was having was that I couldn't figure out how to get my <code>favicon.ico</code> to be treated as a static file and automatically dumped into the <code>output</code> directory. My solution had been to write a little bash script to copy it over for me, but that was kind of tedious. But now that I was going to use the <code>Makefile</code> I decided to automate it in there!</p> <p>I added the following lines to it:</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">Makefile</span><span class="liquid-tags-code-lines">[Lines 10-10]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="nv">FAVICON</span><span class="o">=</span><span class="k">$(</span>INPUTDIR<span class="k">)</span>/static/favicon.ico </code></pre></div> </figure> <p>Then after the <code>html</code>, <code>regenerate</code>, and <code>publish</code> commands, I added:</p> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">Makefile</span><span class="liquid-tags-code-lines">[Lines 44-46]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="nf">html</span><span class="o">:</span> <span class="k">$(</span>PELICAN<span class="k">)</span> <span class="k">$(</span>INPUTDIR<span class="k">)</span> -o <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> -s <span class="k">$(</span>CONFFILE<span class="k">)</span> <span class="k">$(</span>PELICANOPTS<span class="k">)</span> cp <span class="k">$(</span>FAVICON<span class="k">)</span> <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> </code></pre></div> </figure> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">Makefile</span><span class="liquid-tags-code-lines">[Lines 53-55]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="nf">regenerate</span><span class="o">:</span> <span class="k">$(</span>PELICAN<span class="k">)</span> -r <span class="k">$(</span>INPUTDIR<span class="k">)</span> -o <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> -s <span class="k">$(</span>CONFFILE<span class="k">)</span> <span class="k">$(</span>PELICANOPTS<span class="k">)</span> cp <span class="k">$(</span>FAVICON<span class="k">)</span> <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> </code></pre></div> </figure> <figure class='code'> <figcaption><span class="liquid-tags-code-filename">Makefile</span><span class="liquid-tags-code-lines">[Lines 79-81]</span></figcaption> <div class="highlight"><pre><span></span><code><span class="nf">publish</span><span class="o">:</span> <span class="k">$(</span>PELICAN<span class="k">)</span> <span class="k">$(</span>INPUTDIR<span class="k">)</span> -o <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> -s <span class="k">$(</span>PUBLISHCONF<span class="k">)</span> <span class="k">$(</span>PELICANOPTS<span class="k">)</span> cp <span class="k">$(</span>FAVICON<span class="k">)</span> <span class="k">$(</span>OUTPUTDIR<span class="k">)</span> </code></pre></div> </figure> <blockquote> <p>NOTE: I had initially added that to all of the commands, but the ones that run the server, don't actually get to that step.</p> </blockquote> <p>When I <em>Ctrl+C</em> out of it, it just exists...</p> <h2>New workflow</h2> <p>With that all set, things are now much easier and make more sense. What I would recommend, is to run the <code>make clean</code> command and commit it to your <code>content</code> branch. Now you will have a <em>clean</em> slate to start with.</p> <p>You can now add any new content and do the following commands:</p> <div class="highlight"><pre><span></span><code><span class="err">git add .</span> <span class="err">git commit -m &quot;Your description...&quot;</span> <span class="err">git push origin content</span> </code></pre></div> <blockquote> <p>NOTE: Of course, only push your files when you're ready to, but you get the idea.</p> </blockquote> <p>Now you're ready to generate the actual site, here's how that's now done.</p> <h3>Generate the site</h3> <p>If you just want to generate the site this is the command that you want:</p> <div class="highlight"><pre><span></span><code><span class="err">make</span> <span class="err">html</span> </code></pre></div> <p>Then you can either run the Pelican server or the Python one.</p> <h4>Pelican server</h4> <div class="highlight"><pre><span></span><code><span class="err">pelican -r --listen</span> </code></pre></div> <h4>Python server</h4> <div class="highlight"><pre><span></span><code><span class="nb">cd</span> output python -m http.server </code></pre></div> <p>Both of those will make the site accessible at <code>http://localhost:8000</code></p> <h3>Generate site and preview</h3> <p>Now, if you know that you will be wanting to preview your site, a much better way is to use this command:</p> <div class="highlight"><pre><span></span><code><span class="err">make</span> <span class="err">devserver</span> </code></pre></div> <p>Not only will this generate the site, but will also start the Pelican server automagically for you! This option comes in handy when you are tweaking your site's theme. I wanted to change the colors on mine and doing it like this automatically detected the changes and served them up.</p> <h3>Publish the site</h3> <p>This command generates the site using the <code>publishconf.py</code> script, so if you specified your site's url in there, it will add it to the generated html pages. It's nice for making sure all is working correctly and enabling your Google Analytics; if you're into that sort of thing.</p> <div class="highlight"><pre><span></span><code><span class="err">make</span> <span class="err">publish</span> </code></pre></div> <h3>Publish and push to github</h3> <p>Now, we're cooking with Crisco! This command will generate the final site using <code>publishconf.py</code>, merge the output into your <code>master</code> branch, and push it up the hubs; all in one step!</p> <div class="highlight"><pre><span></span><code><span class="err">make</span> <span class="err">github</span> </code></pre></div> <h3>Cleaning up</h3> <p>Now that you've pushed to the hubs, removed the generated files:</p> <div class="highlight"><pre><span></span><code><span class="err">make</span> <span class="err">clean</span> </code></pre></div> <p>If you run <code>git status</code>, you should see that there are no changes and that you are ready to add more content. Nice!</p> <h2>Conclusion</h2> <p>This was a great learning experience for me. Not only did I update the color scheme of my site, I also fixed some annoying issues that I was having. Now I don't have to look up any <code>ghp-import ...</code> command and things just got a heck of a lot more pleasant.</p> <p><strong>In summary</strong>:</p> <div class="highlight"><pre><span></span><code>touch content/posts/new-blog-post.md vi <span class="nv">$_</span> <span class="c1"># write the blog content</span> git status git add . git commit -m <span class="s2">&quot;New blog post.&quot;</span> git push origin content make github make clean </code></pre></div> <h3>On a side note</h3> <p>I was also having a weird problem with my search feature. I had to remove the machine code from my <a href="proxy.php?url=https://clamytoe.github.io/category/cryptography.html">cryptography</a> blog post because it was completely screwing up the JavaScript. Once I got that working, I then noticed that whenever I tried to do a search from any of the category pages, that it was looking for <code>/category/search.html</code>. That page doesn't exists, so I was baffled as to why that was going on. I took a look at the <a href="proxy.php?url=https://github.com/lucachr/pelican-mg">pelican-mg</a> theme's <a href="proxy.php?url=https://palletsprojects.com/p/jinja/">Jinja</a> templates and found the issue in <code>base.html</code>. I simply had to add <code>{{ SITEURL }}</code> in the <code>action</code> part of the form:</p> <div class="highlight"><pre><span></span><code><span class="p">&lt;</span><span class="nt">form</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;uk-search&quot;</span> <span class="na">action</span><span class="o">=</span><span class="s">&quot;{{ SITEURL }}/search.html&quot;</span> <span class="na">data-uk-search</span><span class="p">&gt;</span> </code></pre></div> <p>Now it works beautifully!</p> <p>Of course, with me being an upstanding netcitizen, I submitted a <a href="proxy.php?url=https://github.com/lucachr/pelican-mg/pull/11">pull request</a> to fix the issue :).</p>Pytest fixtures with tear down2020-02-19T17:40:00-06:002020-03-12T21:55:00-05:00Martin Uribetag:clamytoe.github.io,2020-02-19:/articles/2020/Feb/19/pytest-fixtures<p>Figured out how to tear down a pytest fixture</p><h1>Setting up pytest fixtures with teardown code</h1> <p>Today after listening to <a href="proxy.php?url=https://twitter.com/brianokken">Brian Okken's</a> <a href="proxy.php?url=https://testandcode.com/">Test &amp; Code</a> podcast with <a href="proxy.php?url=https://twitter.com/anthonypjshaw">Anthony Shaw</a>, I was reminded that I wanted to install Anthony's <a href="proxy.php?url=https://github.com/tonybaloney/pycharm-security">Python Security Plugin</a> for <a href="proxy.php?url=https://www.jetbrains.com/pycharm/">JetBrain's PyCharm</a> IDE. That was a breeze to install and it just worked out of the box. I fired it up and it automatically checked my code and called it good.</p> <p>I then started to write some tests and while checking a password, it alerted me to an issue. It made a suggestion to fix it, so I accepted and it updated my code and even added the proper import statement!</p> <p><em>FROM</em>:</p> <div class="highlight"><pre><span></span><code> <span class="k">assert</span> <span class="n">dummy</span><span class="o">.</span><span class="n">password</span> <span class="o">==</span> <span class="s2">&quot;admin&quot;</span> </code></pre></div> <p><em>TO</em>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">secrets</span> <span class="kn">import</span> <span class="n">compare_digest</span> <span class="kn">import</span> <span class="nn">pytest</span> <span class="kn">from</span> <span class="nn">passlock.entry</span> <span class="kn">import</span> <span class="n">Entry</span> <span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&quot;module&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">dummy</span><span class="p">():</span> <span class="k">return</span> <span class="n">Entry</span><span class="p">(</span><span class="s2">&quot;router&quot;</span><span class="p">,</span> <span class="s2">&quot;http://192.168.2.1&quot;</span><span class="p">,</span> <span class="s2">&quot;admin&quot;</span><span class="p">,</span> <span class="s2">&quot;admin&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">test_entry</span><span class="p">(</span><span class="n">dummy</span><span class="p">):</span> <span class="k">assert</span> <span class="n">dummy</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">&quot;router&quot;</span> <span class="k">assert</span> <span class="n">dummy</span><span class="o">.</span><span class="n">url</span> <span class="o">==</span> <span class="s2">&quot;http://192.168.2.1&quot;</span> <span class="k">assert</span> <span class="n">dummy</span><span class="o">.</span><span class="n">username</span> <span class="o">==</span> <span class="s2">&quot;admin&quot;</span> <span class="k">assert</span> <span class="n">compare_digest</span><span class="p">(</span><span class="n">dummy</span><span class="o">.</span><span class="n">password</span><span class="p">,</span> <span class="s2">&quot;admin&quot;</span><span class="p">)</span> </code></pre></div> <p>Although, this is just a dummy password for testing, it doesn't hurt to build up some good habits!</p> <h2>On to the fixture tear down</h2> <p>This is when I got to the part where I had to test the creation of my private and public keys. Since I was creating them for testing and didn't want them around afterwards, I decided that I needed to make them go away after the tests were done.</p> <p>I was already using fixtures so that I didn't have to recreate the instances of my classes for each test; generating the keys take a bit of time! I went crazy an opted to make them future proof with <strong>4096-bits</strong>...</p> <p>The challenge was figuring out how to tear them down. I did some initial searches and was coming up blank. I then looked up the pytest guru, <a href="proxy.php?url=https://pythontesting.net/framework/pytest/pytest-fixtures-easy-example/">Brian's</a> blog about the topic, but his examples were too simplistic for what I was trying to do. All they demonstrated was how to print a message, it didn't actually show how to return anything, and then have it torn down afterwards.</p> <p><em>Brian's example</em>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">print_function</span> <span class="kn">import</span> <span class="nn">pytest</span> <span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s1">&#39;module&#39;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">resource_a_setup</span><span class="p">(</span><span class="n">request</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">resources_a_setup()&#39;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">resource_a_teardown</span><span class="p">():</span> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">resources_a_teardown()&#39;</span><span class="p">)</span> <span class="n">request</span><span class="o">.</span><span class="n">addfinalizer</span><span class="p">(</span><span class="n">resource_a_teardown</span><span class="p">)</span> <span class="k">def</span> <span class="nf">test_1_that_needs_resource_a</span><span class="p">(</span><span class="n">resource_a_setup</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;test_1_that_needs_resource_a()&#39;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">test_2_that_does_not</span><span class="p">():</span> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">test_2_that_does_not()&#39;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">test_3_that_does</span><span class="p">(</span><span class="n">resource_a_setup</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">test_3_that_does()&#39;</span><span class="p">)</span> </code></pre></div> <p>I ended up reading the actual <a href="proxy.php?url=https://docs.pytest.org/en/latest/fixture.html">pytest documentation</a>; who would have thunk?! I'll do you guys a solid and cut out the noise and show you what I did:</p> <div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">pytest</span> <span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&quot;module&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">custom_locker</span><span class="p">():</span> <span class="n">v</span> <span class="o">=</span> <span class="n">Vault</span><span class="p">(</span><span class="s2">&quot;.toe&quot;</span><span class="p">,</span> <span class="s2">&quot;tlock&quot;</span><span class="p">)</span> <span class="k">yield</span> <span class="n">v</span> <span class="n">v</span><span class="o">.</span><span class="n">plock</span><span class="o">.</span><span class="n">unlink</span><span class="p">()</span> <span class="n">v</span><span class="o">.</span><span class="n">pub</span><span class="o">.</span><span class="n">unlink</span><span class="p">()</span> <span class="n">v</span><span class="o">.</span><span class="n">base</span><span class="o">.</span><span class="n">rmdir</span><span class="p">()</span> <span class="k">def</span> <span class="nf">test_vault_custom</span><span class="p">(</span><span class="n">custom_locker</span><span class="p">):</span> <span class="k">assert</span> <span class="n">custom_locker</span><span class="o">.</span><span class="n">loc</span> <span class="o">==</span> <span class="s2">&quot;.toe&quot;</span> <span class="k">assert</span> <span class="n">custom_locker</span><span class="o">.</span><span class="n">key_name</span> <span class="o">==</span> <span class="s2">&quot;tlock&quot;</span> </code></pre></div> <p>Setting the <code>scope</code> of the fixture to <em>module</em> makes it so that it doesn't actually do the tearing down until after all of the tests are completed. <em>Perfect!</em></p> <p>The variables in the <code>Vault</code> instance are Path objects, so it was simple enough to just <code>unlink()</code> the files and then <code>rmdir()</code> the directory after it's been emptied. Of course, I tried removing the directory first, but it wasn't having any of that until it was empty...</p>Into the cryptography rabbit hole2020-02-14T07:00:00-06:002020-03-01T00:44:00-06:00Martin Uribetag:clamytoe.github.io,2020-02-14:/articles/2020/Feb/14/pycryptodome<p>Learning about how to encrypt files</p><h1>Venture Into Cryptography with PyCryptodome</h1> <h2>Learning about how to encrypt files</h2> <p>As dumb as it is, I have an unsecured text file that I use to keep track of all my username and passwords for all of the sites that I frequent. In order to not be so "hackable", I've decided to encrypt the file.</p> <p>I had recently written a couple of coding challenges for <a href="proxy.php?url=https://codechalleng.es/">Pybites Code Challenges</a> site, using the <a href="proxy.php?url=https://cryptography.io/en/latest/">cryptography</a> module. One was a coding challenge and the other a testing one. Unfortunately, that module is <strong>NOT</strong> part of Python's Standard Library!</p> <p>I didn't specifically install it, but it seems to come pre-installed with <a href="proxy.php?url=https://www.anaconda.com/distribution/">Anaconda</a>, which I use. I was looking through all of the modules that I had, when I came across the cryptography one. That's when I first got the idea for the challenges. It started as a single challenge, but I really liked how the tests came out and decided to split it up into coding and testing bites.</p> <p>I pushed them to the Pybites Github private repo and it sat there for a bit before <a href="proxy.php?url=https://codechalleng.es/profiles/pybob">Bob Belderbos</a> decided to add them. That's when the problems started! He discovered that the module was not available so he installed it. It worked perfectly for the coding challenge, but for the testing one, when the call to the <a href="proxy.php?url=https://docs.pytest.org/en/latest/">Pytest</a>/<a href="proxy.php?url=https://github.com/mutpy/mutpy">Mutpy</a> instance is made, it looses the path to the module!</p> <p>We were discussing the issue on the <a href="proxy.php?url=https://pybit.es/pages/community.html">Pybites</a> Slack channel when <a href="proxy.php?url=https://codechalleng.es/profiles/driscollis">Mike Driscoll</a> the creator of <a href="proxy.php?url=http://www.blog.pythonlibrary.org/">The Mouse vs Python</a>, mentioned the <a href="proxy.php?url=https://www.pycryptodome.org/en/latest/">PyCryptodome</a> module. He initially thought that the problem was with installing the cryptography module. In the end, Bob was not able to figure out how to get it to work, so the challenge had to be removed from the platform.</p> <p>Regardless, I was intrigued by pycryptodome, so I started to look into how it worked. It seemed pretty cool, so that's when I came up with the idea of encrypting my password file. It took a little bit of work, but I was able to encrypt the file into a binary. The problem came when I started to try and retrieve the data. The module complained about the data being too large!</p> <p>I decided to create an encrypted binary for each entry. So far that's worked, but now I have to figure out how to scan the directory and list the ones that are available in order to be able to retrieve/update them. I've been using the logging module as well, so now I have to figure out how to do some log rotation to keep from taking up too much drive space. I might even play around with creating a UI for it, to make it easier to use.</p> <p>Since I used my <a href="proxy.php?url=https://github.com/clamytoe/toepack">toepack</a> <a href="proxy.php?url=https://github.com/cookiecutter/cookiecutter">Cookiecutter</a> template, it's a package. It's gotten big enough that I've moved the two classes, (<code>Entry</code>, <code>Vault</code>) into their own files. It works out pretty good because the <code>app.py</code> file now only contains:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">.entry</span> <span class="kn">import</span> <span class="n">Entry</span> <span class="kn">from</span> <span class="nn">.vault</span> <span class="kn">import</span> <span class="n">Vault</span><span class="p">,</span> <span class="n">logger</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span> <span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&quot;Entering main.&quot;</span><span class="p">)</span> <span class="n">ip</span> <span class="o">=</span> <span class="n">Entry</span><span class="p">(</span><span class="s2">&quot;ip&quot;</span><span class="p">,</span> <span class="s2">&quot;http://192.168.2.1&quot;</span><span class="p">,</span> <span class="s2">&quot;admin&quot;</span><span class="p">,</span> <span class="s2">&quot;admin&quot;</span><span class="p">)</span> <span class="n">v</span> <span class="o">=</span> <span class="n">Vault</span><span class="p">()</span> <span class="n">v</span><span class="o">.</span><span class="n">encrypt_file</span><span class="p">(</span><span class="n">ip</span><span class="p">)</span> <span class="n">v</span><span class="o">.</span><span class="n">decrypt_file</span><span class="p">(</span><span class="n">ip</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span> <span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&quot;Running as a module.&quot;</span><span class="p">)</span> <span class="n">main</span><span class="p">()</span> </code></pre></div> <p>Separating the code like this will come in handy when it comes to either creating a <a href="proxy.php?url=https://palletsprojects.com/p/click/">Click</a> CLI interface or a GUI one.</p> <p>The log file for that run looks like this:</p> <div class="highlight"><pre><span></span><code>2020-02-13 14:05:17,812 - [INFO] - root - Creating vault: /home/mohh/Documents/.passlock/ip.bin 2020-02-13 14:05:17,812 - [INFO] - root - Importing public key: plock.pub 2020-02-13 14:05:17,812 - [INFO] - root - Public key accessed: /home/mohh/.passlock/plock.pub 2020-02-13 14:05:17,815 - [INFO] - root - Generating random byte for session key 2020-02-13 14:05:17,815 - [INFO] - root - Generating encrypted session key 2020-02-13 14:05:17,819 - [INFO] - root - Generating AES cipher 2020-02-13 14:05:17,820 - [INFO] - root - Encrypting data to create cipher text 2020-02-13 14:05:17,820 - [INFO] - root - Writing cipher text to: ip.bin 2020-02-13 14:05:17,820 - [WARNING] - root - /home/mohh/Documents/.passlock/ip.bin already exists! 2020-02-13 14:05:17,820 - [INFO] - root - Successfully wrote to: /home/mohh/Documents/.passlock/ip.bin 2020-02-13 14:05:17,821 - [INFO] - root - Opening file for reading: /home/mohh/Documents/.passlock/ip.bin 2020-02-13 14:05:17,821 - [INFO] - root - Importing private key: plock 2020-02-13 14:05:17,821 - [INFO] - root - Private key accessed: /home/mohh/.passlock/plock 2020-02-13 14:05:18,150 - [INFO] - root - Extracting encrypted session key 2020-02-13 14:05:18,151 - [INFO] - root - Generating RSA cipher key from imported private key 2020-02-13 14:05:18,151 - [INFO] - root - Decrypting extracted session key 2020-02-13 14:05:18,171 - [INFO] - root - Session key was successfully restored 2020-02-13 14:05:18,171 - [INFO] - root - Generating AES cipher from session key 2020-02-13 14:05:18,172 - [INFO] - root - Decrypting and verifying cipher text 2020-02-13 14:05:18,172 - [INFO] - root - Closing: ip.bin 2020-02-13 14:05:18,172 - [INFO] - root - Generating new Entry object from decrypted data 2020-02-13 14:05:18,172 - [INFO] - root - Returning decrypted text for: ip </code></pre></div> <p>Not sure if that's too much information but logging each step in the process really comes in handy when trying to figure out issues at work. I've tried to keep all sensitive data out of it unless the logging is switched to <code>debug</code>.</p> <p>Retrieving the encrypted data looks like this:</p> <div class="highlight"><pre><span></span><code>Successfully created: /home/mohh/Documents/.passlock/ip.bin [ip] INFO: http://192.168.2.1 USER: admin PASS: admin </code></pre></div> <blockquote> <p><strong>NOTE</strong>: These are not real username and passwords!</p> </blockquote>Rescaling values for DataScience2020-02-13T08:00:00-06:002020-03-01T01:02:00-06:00Martin Uribetag:clamytoe.github.io,2020-02-13:/articles/2020/Feb/13/rescaling-values<p>Quick discussion about rescaling values for data science</p><h1>Scaling values</h1> <p>Today while reading Data Science Algorithms in a Week, from <a href="proxy.php?url=https://www.packtpub.com">packt</a>, I came across the concept of rescaling values so that when measuring their distances they would be more relevant. The dataset consisted of "House Ownership":</p> <table> <thead> <tr> <th align="left">Age</th> <th align="left">Annual income in USD</th> <th align="left">House ownership status</th> </tr> </thead> <tbody> <tr> <td align="left">23</td> <td align="left">50,000</td> <td align="left">Non-owner</td> </tr> <tr> <td align="left">37</td> <td align="left">34,000</td> <td align="left">Non-owner</td> </tr> <tr> <td align="left">20</td> <td align="left">100,000</td> <td align="left">Owner</td> </tr> <tr> <td align="left">35</td> <td align="left">130,000</td> <td align="left">Owner</td> </tr> </tbody> </table> <p>etc..</p> <p>The aim being to predict whether a person that is 50 years old with an income of $80,000, would own a home so that he could be targeted for home insurance.</p> <p><a href="proxy.php?url=https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm">k-Nearest Neighbor</a>'s are currently being covered and for this exercise a <code>k = 1</code> is to be used. Using either a <a href="proxy.php?url=https://en.wikipedia.org/wiki/Euclidean_distance">Euclidean</a> or <a href="proxy.php?url=https://en.wiktionary.org/wiki/Manhattan_distance">Manhattan</a> distance wouldn't matter because the distances between these values are too great. In comes rescaling!</p> <p>The formula use is:</p> <div class="math">$$ScaledQuantity = \frac{ActualQuantity-MinQuantity}{MaxQuantity-MinQuantity}$$</div> <p>So for this dataset, both the <strong>Age</strong> and the <strong>Annual income</strong> wound have to be adjusted.</p> <div class="math">$$ScaledAge = \frac{ActualAge-MinAge}{MaxAge-MinAge}$$</div> <div class="math">$$ScaledIncome = \frac{ActualIncome-MinIncome}{MaxIncome-MinIncome}$$</div> <p>After scaling, the adjusted dataset would look something like this:</p> <table> <thead> <tr> <th align="left">Age</th> <th align="left">Scaled age</th> <th align="left">Annual income in USD</th> <th align="left">Scaled annual income</th> <th align="left">House ownership status</th> </tr> </thead> <tbody> <tr> <td align="left">23</td> <td align="left">09375</td> <td align="left">50,000</td> <td align="left">2</td> <td align="left">Non-owner</td> </tr> <tr> <td align="left">37</td> <td align="left">53125</td> <td align="left">34,000</td> <td align="left">4</td> <td align="left">Non-owner</td> </tr> <tr> <td align="left">20</td> <td align="left">0</td> <td align="left">100,000</td> <td align="left">7</td> <td align="left">Owner</td> </tr> <tr> <td align="left">35</td> <td align="left">46875</td> <td align="left">130,000</td> <td align="left">1</td> <td align="left">Owner</td> </tr> <tr> <td align="left">50</td> <td align="left">9375</td> <td align="left">80,000</td> <td align="left">5</td> <td align="left">?</td> </tr> </tbody> </table> <p>Now a 1-NN algorithm with a Euclidean metric could easily be used to find out if the last person is more than likely to own a home. Without the rescaling, the algorithm would have yielded different results.</p> <p>Keeping it short today, but hopefully it was a helpful tip!</p> <script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) { var align = "left", indent = "0em", linebreak = "true"; if (true) { align = (screen.width < 1125) ? "left" : align; indent = (screen.width < 1125) ? "0em" : indent; linebreak = (screen.width < 1125) ? 'true' : linebreak; } var mathjaxscript = document.createElement('script'); mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#'; mathjaxscript.type = 'text/javascript'; mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML'; var configscript = document.createElement('script'); configscript.type = 'text/x-mathjax-config'; configscript[(window.opera ? "innerHTML" : "text")] = "MathJax.Hub.Config({" + " config: ['MMLorHTML.js']," + " TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," + " jax: ['input/TeX','input/MathML','output/HTML-CSS']," + " extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," + " displayAlign: '"+ align +"'," + " displayIndent: '"+ indent +"'," + " showMathMenu: true," + " messageStyle: 'normal'," + " tex2jax: { " + " inlineMath: [ ['\\\\(','\\\\)'] ], " + " displayMath: [ ['$$','$$'] ]," + " processEscapes: true," + " preview: 'TeX'," + " }, " + " 'HTML-CSS': { " + " availableFonts: ['STIX', 'TeX']," + " preferredFont: 'STIX'," + " styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," + " linebreaks: { automatic: "+ linebreak +", width: '90% container' }," + " }, " + "}); " + "if ('default' !== 'default') {" + "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" + "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" + "VARIANT['normal'].fonts.unshift('MathJax_default');" + "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" + "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" + "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" + "});" + "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" + "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" + "VARIANT['normal'].fonts.unshift('MathJax_default');" + "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" + "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" + "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" + "});" + "}"; (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript); (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript); } </script>This is my first post here2020-02-12T12:00:00-06:002020-03-01T01:04:00-06:00Martin Uribetag:clamytoe.github.io,2020-02-12:/articles/2020/Feb/12/initial-post<p>Initial Post for my Ramblings of an autodidact Blog</p><h1>Getting my feet wet with this blogging stuff</h1> <p>It's not much to look at, but my goal is to show what I'm learning as I go. There's no real "topic" but it will mostly be about what I'm working on and what I have to learn to get it done. Hope that it's of interest to someone...</p>