<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://ayyjohn.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ayyjohn.com/" rel="alternate" type="text/html" /><updated>2024-10-21T18:50:35+00:00</updated><id>https://ayyjohn.com/feed.xml</id><title type="html">The Blog at Ayyjohn</title><subtitle>Someone once told me that names like &quot;the x at y&quot; give apartments a ritzy feel. Welcome to my blog!</subtitle><entry><title type="html">APIs: What’s in a Name?</title><link href="https://ayyjohn.com/posts/apis-are-hard" rel="alternate" type="text/html" title="APIs: What’s in a Name?" /><published>2024-10-20T15:59:51+00:00</published><updated>2024-10-20T15:59:51+00:00</updated><id>https://ayyjohn.com/posts/apis-are-hard</id><content type="html" xml:base="https://ayyjohn.com/posts/apis-are-hard"><![CDATA[<p>Having tutored at Ada Academy for 8+ cohorts now, I’m kind of surprised that it never occurred to me to write a post explaining APIs.
Not only has every student I’ve worked with at Ada asked me for help understanding them when they come up in the curriculum, but I’m also <em>really bad</em> at explaining them off the cuff because I usually ramble and go way too deep.
So this is going to serve as a distillation of the conversation I usually have with my students.</p>

<p>There’s a lot of things that make APIs confusing, and a lot of things about my verbal explanation that aren’t helping.
For starters: “Application Programming Interface” is a pretty useless combination of three words without any followup.
In addition, depending on the context, what you’re referring to when you talk about an API can mean wildly different things.
So I’d like to start there, let’s unpack that name.</p>

<p>An interface is a point where two systems interact.
When iPhones first came out, the “touch screen interface” was all new and sexy.
It’s just a fancy way of saying “you can touch your phone’s screen to do things rather than pushing physical buttons.” Unstated in all of this is that there’s a ton of things going on behind the scenes when you touch your phone in order to make the phone register that it’s being touched and then even more stuff going on between the phone saying “ok someone touched me here” and that touch being relayed to whichever app you’re using at the time and having it respond how it’s supposed to.
So, if interfaces are how two things interact, what does the “application programming” modifier change? It reduces “two things” down to “applications/programs,” meaning that an API is how two programs, or applications interact.
You’ve already used tons of APIs without knowing it.
If you’ve ever imported a third party package in Python, you’ve used the API of that package.
There’s tons of code in that package that you can’t interact with directly, instead the package <strong>exports</strong> certain functions and classes that allow you as a programmer to gain some functionality.
As an example: The python <code class="language-plaintext highlighter-rouge">requests</code> library allows programmers to make HTTP requests, and if you look at its codebase, there’s a lot of code that’s probably unfamiliar.
Realistically, if you’re using the requests package, you’re likely using something from <code class="language-plaintext highlighter-rouge">https://github.com/psf/requests/blob/main/requests/api.py</code></p>

<p>Frustratingly, when most people talk about APIs they’re not talking about anything as abstract as the above.
They’re probably talking about web APIs. Web APIs are just that, programs that are interacted with via the web.
Realistically there’s no point making a program that doesn’t do anything, so most web APIs take one of a few forms.</p>

<ol>
  <li>Data Access APIs: eg: pokeAPI, weather APIs, geo-&gt;IP APIs.
These are APIs that are usually read-only, meaning you can only request data from them, you can’t change anything.
This makes sense when you consider that it wouldn’t really make sense for you to be able to create new Pokemon, there’s a definitive data source, and pokeAPI is just making the data available to you in a nice way.
What pokemon is number 493? Well, it’s Arceus, but if you don’t know that, you can send a GET request to pokeAPI and it’ll tell you.
Want all Pokemon where Ghost is their secondary type? Sure thing.
Some other features of APIs like these is that they’re often un-authenticated, or at most rate-limited, since the worst thing you can really do to them would be make a lot of requests.</li>
  <li>Read/Write APIs.
These are really interesting and you’ll likely end up using one at some point.
Have you ever wondered how Twitter bots work? The answer is that they’re interacting with Twitter via Twitter’s public web API.
Twitter makes some functionality available programmatically over the web, so in the same way that you can log in to your account and make a tweet, you can also write a program on your computer that authenticates to Twitter and posts a tweet.
You can write a program that listens for new tweets from a certain person, or a list of people, or all people in a certain area, or just… all tweets.
Why would you do this? Maybe you want to archive tweets from politicians before they get deleted. Maybe you want to collect tweets about a sports team and analyze them to prove once and for all that Chiefs fans are the worst.
Who knows!? The point is, you can do this because most tech companies at this point have some form of API allowing developers to write programs that interact with their data. Spotify lets you use their API to search for music and get album artwork.
Reddit lets you both create and fetch posts by subreddit.
Without this you’d essentially need to write a web scraper to accomplish the same task, and web scrapers kinda suck to be honest.
They’re very brittle, and if the structure of the HTML of the website you’re scraping changes, your program might stop working.
So fully featured APIs let you read and write data.
This is why they’re almost exclusively authenticated, meaning you either need to log in as yourself, or create a developer application and use OAauth in order to interface with them.
This also functions to manage permissions, which is why if you try and tweet from your own account it works, but if you try and tweet as someone else it will fail.</li>
</ol>

<p>Under the hood what we really mean when we say web API is that there’s a server out there that, when sent specifically formatted requests, will respond by either sending back data, or changing some data on its own database.
These servers are normally accesssed via HTTP, though other options are available (websockets, semaphore, f*** it you name it).
Most HTTP APIs are “Restful” which just means that there’s a set of conventions about how they’ll respond to certain types of HTTP request.
Simple CRUD APIs are often implemented as REST APIs because it means you don’t have to read as much documentation to use them.
You have a reasonable chance of guessing that if you hit <code class="language-plaintext highlighter-rouge">https://pokeapi.com/v1/pokemon/1</code> with a GET request you’re going to get back some info about either Bulbasaur or Rhydon (or maybe Mew/Arceus).
This is useful, but maybe your API is more complex than just “create read update destroy” in which case you could implement a SOAP or RPC based API.
These conventions just serve to make it easier for developers to consume your API.</p>

<p>So, to summarize:</p>

<ul>
  <li>An API just defines the touch point between two programs. That can be as broad as the functions made available in a module or variables exposed on an object.</li>
  <li>Often when people refer to “APIs” they’re referring to web APIs which are services set up by someone to allow you as a programmer to easily access some resource, either as a consumer or a publisher.</li>
  <li>It’s APIs all the way down. You might use Python’s standard library to import a Twitter library which has public methods for authenticating and posting tweets, and under the hood it uses Python’s Requests library which has public methods for making GET and POST requests. That Twitter library you used will then use Requests to interact with Twitter via Twitter’s web API, and behind the scenes, once you send a request to Twitter, many services interact behind the scenes via each others’ internal private APIs (IE you as a non-Twitter engineer have no direct access to them) to publish that tweet, then other Twitter services interact with public APIs of other companies to serve ads on that tweet.</li>
</ul>]]></content><author><name></name></author><category term="apis" /><summary type="html"><![CDATA[Having tutored at Ada Academy for 8+ cohorts now, I’m kind of surprised that it never occurred to me to write a post explaining APIs. Not only has every student I’ve worked with at Ada asked me for help understanding them when they come up in the curriculum, but I’m also really bad at explaining them off the cuff because I usually ramble and go way too deep. So this is going to serve as a distillation of the conversation I usually have with my students.]]></summary></entry><entry><title type="html">G’on Now, Git!</title><link href="https://ayyjohn.com/posts/daily-git" rel="alternate" type="text/html" title="G’on Now, Git!" /><published>2022-03-13T15:59:51+00:00</published><updated>2022-03-13T15:59:51+00:00</updated><id>https://ayyjohn.com/posts/daily-git</id><content type="html" xml:base="https://ayyjohn.com/posts/daily-git"><![CDATA[<p>I’ve got this ridiculous screenshot saved somewhere on an old hard drive of my friend Rohan trying to explain Git to me when I first started programming. 
He was a CS major who’d been programming for years before college, and I was a chemical engineering major who’d almost failed my MATLAB course, so naturally everything went over my head, right? 
No. I mean, it <em>did</em> all go over my head, but not naturally. I fully believe Git is understandable at the level you’ll need for day to day work in a few minutes (hence this blog post).</p>

<p>Git is definitely one of those things where you’ll discover new functionality years, or even decades into your career. But it doesn’t need to be as complicated as a lot of people make it when you’re just starting out. You’re learning how to program, how to use the terminal for the first time, how to deal with ridiculous error messages that in a few months will be cake but as of right now are going to ruin your day. The last thing you need is someone explaining to you oh-so-elegantly how the intricacies of the staging area work and why git bisect is the coolest thing since sliced bread.</p>

<p>Here’s the Git you’ll need on a day to day when you’re starting out.</p>

<h2 id="basic-concepts">Basic Concepts</h2>

<p>Git is a “version control system.” 
You know how when you would write essays in high school you’d have a bunch of files on your computer like</p>

<pre><code class="language-txt">english/
  essays/
    i_read_the_odyssey.docx
    i_definitely_read_the_odyssey.docx
    i_definitely_read_the_odyssey_new.docx
    i_definitely_read_the_odyssey_revised.docx
    i_definitely_read_the_odyssey_final.docx
    i_totally_read_the_odyssey_final.docx
</code></pre>

<p>Well, Git is a program that makes it so you don’t have to do that anymore. 
It was terrible enough with one file, but in software it’s common for projects to have hundreds or even thousands of files, all of which need to be updated regularly, and any of which can completely break your project.
Oh and also in most cases other people will need to be working on them at the same time.</p>

<p>The way that Git does this is by tracking the history of each file over time. Your computer likely already auto-saves files, but this is different. 
Using Git, you can quickly jump back and forth between the version of the file that you changed today and the version you downloaded when you started the project. 
Let’s say your code was working a few minutes ago and now you’re getting some weird error message. Git will let you look at all the changes to every file you’ve changed recently.</p>

<p>Git won’t do this automatically. In order to begin this process, you have to “initialize a git repository” which just means telling Git to track what’s happening in a certain folder.
You do this with the command <code class="language-plaintext highlighter-rouge">git init</code> wherever you’re interested in tracking. 
So let’s say you had a hot new idea for an app you wanted to write, you’d go to wherever on your computer you make new projects, create a folder for it, and then go into that folder and run <code class="language-plaintext highlighter-rouge">git init</code>.</p>

<p>You’ll get a confirmation message of the form</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Initialized empty Git repository in /Users/ayyjohn/code/test/.git/
</code></pre></div></div>

<p>and now you’re ready to get to work!</p>

<h2 id="basic-commands">Basic Commands</h2>

<p>In this section we’ll be covering the following commands</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add
git commit
git status
</code></pre></div></div>

<p>All git commands come in the form of <code class="language-plaintext highlighter-rouge">git something</code> where the <code class="language-plaintext highlighter-rouge">something</code> is <code class="language-plaintext highlighter-rouge">add, status, etc</code>. You can add extra modifiers and arguments to change the behavior, but they’re not always necessary.</p>

<p>Now, let’s resume where we left off above: you’ve created your new app in the <code class="language-plaintext highlighter-rouge">new_app</code> folder and run <code class="language-plaintext highlighter-rouge">git init</code>, let’s say you also create a main python script, so you have the following structure</p>

<pre><code class="language-txt">new_app
  main.py (you made this)
  .git/ (Git made this)
</code></pre>

<h3 id="git-add">Git Add</h3>

<p><code class="language-plaintext highlighter-rouge">git add</code> is used to tell Git “Hey, I want to track changes to this file or folder”.
If you’ve never done this before, it will be “untracked”.</p>

<p>The first thing you’re going to do is create the base save point of your app in git, by running 
<code class="language-plaintext highlighter-rouge">git add .</code> where the <code class="language-plaintext highlighter-rouge">.</code> just refers to everything in the current folder.</p>

<p>Notably, <code class="language-plaintext highlighter-rouge">git add</code> <em>doesn’t</em>, I repeat, <em>does not</em> save the changes to the file in git.
Most code changes touch many files at once, and it’s rare that you’ll want to save changes in one file
at a time. 
If you’ve written a new function in one file and you want to import it somewhere else, you’ll want both the new function and the import saved at the same time, otherwise if you go back to the state where the import exists but the function doesn’t, your code will break.
So rather than create save points file by file, Git lets you group changes together in <code class="language-plaintext highlighter-rouge">commits</code> where a commit is just a group of changes that go together in your mind.</p>

<h3 id="git-commit">Git Commit</h3>

<p>To do this, you use <code class="language-plaintext highlighter-rouge">git commit</code> which says “take everything that I’ve <code class="language-plaintext highlighter-rouge">add</code>ed and create a save point where all of those changes have been made.”
<code class="language-plaintext highlighter-rouge">git commit</code> by default opens up a code editor where you can write a “commit message” which is essentially a note to anyone in the future about what happened in these changes. 
Committing your code often helps keep these small and easy to understand.
Rather than use that editor, though, you can run <code class="language-plaintext highlighter-rouge">git commit -m "some message"</code> which makes your commit message <code class="language-plaintext highlighter-rouge">"some message"</code></p>

<p>The canonical first commit in any new codebase is <code class="language-plaintext highlighter-rouge">initial commit</code>. It’s really useful to have a clean initial commit, especially when you’re working with big frameworks like Django that start you out with hundreds of files. 
Ideally the initial commit should be before you’ve made any real changes, that way by checking the difference between your code and the initial commit you can see everything you’ve done since you started the project, not including automatic set up.</p>

<h3 id="git-status">Git Status</h3>
<p>Before I close this section out, I want to talk about <code class="language-plaintext highlighter-rouge">git status</code>.
<code class="language-plaintext highlighter-rouge">git status</code> is a handy command for checking to make sure that you’ve <code class="language-plaintext highlighter-rouge">git add</code>ed what you want before committing. 
You can run it to get a summary of all changes that will be included in the next commit (staged) and won’t be included in the next commit (unstaged). <code class="language-plaintext highlighter-rouge">git add</code> is used to “stage” a file for commit.</p>

<p>If I run <code class="language-plaintext highlighter-rouge">git status</code> after running <code class="language-plaintext highlighter-rouge">git init</code> I’ll see the following</p>

<pre><code class="language-txt">On branch main

No commits yet

Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
	main.py

nothing added to commit but untracked files present (use "git add" to track)
</code></pre>

<p>and after I run <code class="language-plaintext highlighter-rouge">git add .</code> and run <code class="language-plaintext highlighter-rouge">git status</code> again I’ll see the following</p>

<pre><code class="language-txt">On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached &lt;file&gt;..." to unstage)
	new file:   main.py

</code></pre>

<p>Great! My file is staged, let’s commit it using <code class="language-plaintext highlighter-rouge">git commit -m "initial commit"</code>, then see what’s going on with <code class="language-plaintext highlighter-rouge">git status</code> once more</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>On branch main
nothing to commit, working tree clean
</code></pre></div></div>

<p>Which just means “there have been no changes since the last commit”</p>

<h2 id="basic-workflow">Basic Workflow</h2>

<p>Awesome, ok that’s a first commit. So now let’s walk through what a typical workflow looks like when you’re working on a project by yourself.
Typically the cycle looks something like</p>
<ul>
  <li>write or change some code</li>
  <li>test that change</li>
  <li>stage some or all of that code for committing using <code class="language-plaintext highlighter-rouge">git add</code></li>
  <li>(optionally) check to make sure everything is staged the way you want it using <code class="language-plaintext highlighter-rouge">git status</code></li>
  <li>commit your changes, sometimes in multiple commits using <code class="language-plaintext highlighter-rouge">git commit -m</code></li>
  <li>repeat</li>
</ul>

<p>A common thing to do when creating a new python project is to create a virtualenv. Let’s say this is going to be a Flask project. 
I’ll create a new virtualenv <code class="language-plaintext highlighter-rouge">venv/</code> using <code class="language-plaintext highlighter-rouge">python -m venv venv</code> (there are many other ways to do this) and after activating it, install Flask using <code class="language-plaintext highlighter-rouge">pip install flask</code>
Then I’ll create a <code class="language-plaintext highlighter-rouge">requirements.txt</code> file so that future people can install the dependencies they need using <code class="language-plaintext highlighter-rouge">pip freeze &gt;&gt; requirements.txt</code> 
and then toss the bones of a flask app into <code class="language-plaintext highlighter-rouge">main.py</code> and rename it to <code class="language-plaintext highlighter-rouge">app.py</code> as is common and WOW would you look at all these changes I really should commit some of these.</p>

<p><code class="language-plaintext highlighter-rouge">git status</code> tells me</p>
<pre><code class="language-txt">On branch main
Changes not staged for commit:
  (use "git add/rm &lt;file&gt;..." to update what will be committed)
  (use "git restore &lt;file&gt;..." to discard changes in working directory)
	deleted:    main.py

Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
	app.py
	requirements.txt
	venv/

no changes added to commit (use "git add" and/or "git commit -a")
</code></pre>

<p>so I’ll start by just doing <code class="language-plaintext highlighter-rouge">git add app.py</code> and <code class="language-plaintext highlighter-rouge">git add main.py</code>. Git is smart enough to detect that this file has just been renamed, so now it tells me</p>

<pre><code class="language-txt">On branch main
Changes to be committed:
  (use "git restore --staged &lt;file&gt;..." to unstage)
	renamed:    main.py -&gt; app.py

Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
	requirements.txt
	venv/
</code></pre>

<p>so now I’ll commit this with <code class="language-plaintext highlighter-rouge">git commit -m "rename main.py to app.py"</code></p>

<p>and go ahead and make another commit with <code class="language-plaintext highlighter-rouge">requirements.txt</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add requirements.txt
git commit -m "create requirements file, add flask as a dependency"
</code></pre></div></div>

<p>Now the only thing left uncommitted is <code class="language-plaintext highlighter-rouge">venv</code>, but you know what? I don’t actually want to save that. It’s a huge folder containing all of the dependencies that I installed, and if I share this project I’d rather just have people install the dependencies themselves using <code class="language-plaintext highlighter-rouge">requirements.txt</code>
But I also don’t want to have this in my git status all the time</p>

<pre><code class="language-txt">Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
	venv/
</code></pre>

<h3 id="introducing-the-gitignore">Introducing the gitignore</h3>

<p><code class="language-plaintext highlighter-rouge">.gitignore</code> is a file you can create that will tell git “don’t track this”. It doesn’t exist by default, so you can create it using <code class="language-plaintext highlighter-rouge">touch .gitignore</code>, and go ahead and add <code class="language-plaintext highlighter-rouge">venv/</code> to it by simply putting <code class="language-plaintext highlighter-rouge">venv/</code> at the top of it.</p>

<pre><code class="language-txt"># .gitignore
venv/
</code></pre>

<p>Now when we do <code class="language-plaintext highlighter-rouge">git status</code> again,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>On branch main
Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
	.gitignore

nothing added to commit but untracked files present (use "git add" to track)
</code></pre></div></div>

<p>which shows that <code class="language-plaintext highlighter-rouge">venv/</code> is being ignored by git. We’ve still got to track the gitignore, though, so we’ll do that using <code class="language-plaintext highlighter-rouge">git add .gitignore</code> and <code class="language-plaintext highlighter-rouge">git commit -m "add gitignore, ignore venv"</code></p>

<p>one last <code class="language-plaintext highlighter-rouge">git status</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>On branch main
nothing to commit, working tree clean
</code></pre></div></div>

<p>B E A Utiful.</p>

<h2 id="more-advanced-things-optional-but-encouraged">More Advanced Things (Optional but Encouraged)</h2>

<p>The above workflow will get you far. There’s a lot of small improvements that can be made, and a lot more commands to git. For example, <code class="language-plaintext highlighter-rouge">git log</code> will show you a list of commits (I use <code class="language-plaintext highlighter-rouge">--oneline</code>) to format it like so.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>204cddc (HEAD -&gt; main) add gitignore, ignore venv
d7bd579 create requirements file, add flask as a dependency
44013dd rename main.py to app.py
40b1dd1 initial commit
</code></pre></div></div>

<p>See those numbers at the start? Those are called “commit hashes” and they’re unique identifiers for each of the commits. Remember when I said we could use git to jump around in time? Let’s say the next thing I committed made it so I couldn’t use <code class="language-plaintext highlighter-rouge">flask run</code> because I was getting some error. I have a couple of options.</p>

<ol>
  <li>Look at what’s changed between now and then using <code class="language-plaintext highlighter-rouge">git show</code> and <code class="language-plaintext highlighter-rouge">git diff</code> to see if I can figure out why it broke</li>
  <li>Roll back to a commit where I know everything was working using <code class="language-plaintext highlighter-rouge">git checkout</code> or <code class="language-plaintext highlighter-rouge">git reset</code> and try again</li>
</ol>

<p><code class="language-plaintext highlighter-rouge">git show</code> by default will show you what changed in the last commit, or you can give it one of those commit hashes to show what happened in a certain commit <code class="language-plaintext highlighter-rouge">git show {commithash}</code></p>

<p>so if I do <code class="language-plaintext highlighter-rouge">git show d7bd579</code> it’ll tell me</p>

<pre><code class="language-txt">commit d7bd57982e11c943a2a5a96c85ffd075429a1030
Author: Alec Johnson &lt;redacted&gt;
Date:   Sun Mar 13 09:04:54 2022 -0700

    create requirements file, add flask as a dependency

diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..0c8d93e
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+click==8.0.4
+Flask==2.0.3
+itsdangerous==2.1.1
+Jinja2==3.0.3
+MarkupSafe==2.1.0
+Werkzeug==2.0.3
</code></pre>

<p>which tells me I created <code class="language-plaintext highlighter-rouge">requirements.txt</code> and added those items to it. 
If I add future dependencies, or uninstall some, they’ll be tracked in this file as well so I can tell what libraries I was using at any point in time!</p>

<p><code class="language-plaintext highlighter-rouge">git diff</code> by default will tell you the difference between what you’ve changed and the last commit, but if you give it one commit hash it will tell you the difference between now and that checkpoint. If you give it two commit hashes it will tell you everything that changed between those two checkpoints.</p>

<p>If using those I can’t figure out why things aren’t working, I can try things from option two above. <code class="language-plaintext highlighter-rouge">git checkout</code> is the analogy to having all of those save files around. By giving it a commit hash, the folder you’re in will be set to what it was like at that point in time and you can poke around as if you never made the changes since. <code class="language-plaintext highlighter-rouge">git checkout main</code> will bring you back where you came from.</p>

<p>Finally, if you really can’t figure out what happened, <code class="language-plaintext highlighter-rouge">git reset</code> is an option. Give it a commit hash, and it will reset your directory to that checkpoint. There are two variations, <code class="language-plaintext highlighter-rouge">git reset --soft</code> and <code class="language-plaintext highlighter-rouge">git reset --hard</code>. <em>be careful with <code class="language-plaintext highlighter-rouge">git reset --hard</code></em>, you cannot recover things that are hard reset.
Whereas using <code class="language-plaintext highlighter-rouge">git checkout</code> will let you jump between checkpoints, <code class="language-plaintext highlighter-rouge">git reset</code> will let you roll back changes made since then. A <code class="language-plaintext highlighter-rouge">--soft</code> reset will un-commit those things but leave the changes around for you to mess with and re-commit if you like. 
A <code class="language-plaintext highlighter-rouge">--hard</code> reset will remove the commits and change the files back to the way they were at the checkpoint you give it, so again, be very careful with this.</p>

<h2 id="branches-and-collaboration">Branches and Collaboration</h2>

<p>I think teaching too much about branches in an intro to git is a mistake. 
I may have already included too much jargon in this post and that’s without mentioning branches. 
Simply put, branches are a way for you to work on one version of a codebase while someone works on a different version. 
Git has tools that let you intelligently combine the changes once each of you has finished your work, but it’s not magical. 
If both of you change the same part of the same file, Git is going to ask you to manually specify which changes to keep.
For projects where you’re working by yourself, working on the <code class="language-plaintext highlighter-rouge">main</code> branch is usually fine.</p>

<p>And lastly, in the beginning there’s a lot of confusion between Git, the tool, and Github, the website. 
Git is the version control system that exists on your computer. Github is a social networking site for sharing code. 
It’s got a ton of amazing functionality that allows people working on the same project to review other people’s code and approve or reject changes to open source projects where people will be collaborating from around the world.
Instead of creating a brand new project on your computer, it’s common to work collaboratively on something someone else has already started. That’s where <code class="language-plaintext highlighter-rouge">git clone</code> comes in. 
<code class="language-plaintext highlighter-rouge">git clone</code> will let you copy the entire git history and all files of a project and do all of the awesomeness that you learned above. 
You can do this with pretty much any public repository on GitHub, and it’s a great way to learn about tools you currently use. 
You can even <code class="language-plaintext highlighter-rouge">git clone</code> <a href="https://github.com/python/cpython">the cPython codebase</a>.</p>

<p>Just remember, once it’s in git, it’s there forever (unless you hard reset) so if you accidentally commit, say, your password, just deleting it isn’t enough! Someone can go to the commit where it <em>was</em> there and steal it.</p>]]></content><author><name></name></author><category term="git" /><summary type="html"><![CDATA[I’ve got this ridiculous screenshot saved somewhere on an old hard drive of my friend Rohan trying to explain Git to me when I first started programming. He was a CS major who’d been programming for years before college, and I was a chemical engineering major who’d almost failed my MATLAB course, so naturally everything went over my head, right? No. I mean, it did all go over my head, but not naturally. I fully believe Git is understandable at the level you’ll need for day to day work in a few minutes (hence this blog post).]]></summary></entry><entry><title type="html">How to Read Python Stack Traces</title><link href="https://ayyjohn.com/posts/anatomy-of-a-python-stack-trace" rel="alternate" type="text/html" title="How to Read Python Stack Traces" /><published>2021-11-14T10:33:51+00:00</published><updated>2021-11-14T10:33:51+00:00</updated><id>https://ayyjohn.com/posts/anatomy-of-a-python-stack-trace</id><content type="html" xml:base="https://ayyjohn.com/posts/anatomy-of-a-python-stack-trace"><![CDATA[<p>No matter how good of a programmer you are, you will write code that breaks. Being able to read broken code is a valuable skill. Code fails more than it succeeds <code class="language-plaintext highlighter-rouge">[citation needed]</code></p>

<p>In fact, a real and valid way to write code, Test Driven Development (TDD) consists of writing code that breaks and then reading the error and fixing it.</p>

<p>When I’m working with people who are learning to code, one of the most important lessons I try and impart on them is the value of being able to read an error message with a stack trace and get to the root of the problem.</p>

<p>Sometimes I have a hard time in the moment explaining what I’m seeing, and so in this post I want to give some examples and do a kind of “here’s what I’m seeing.”</p>

<p>There are many ways to break down different types of stack traces, but I’m going to go with 4 classes today.</p>

<h2 id="class-1-errors-that-tell-you-exactly-what-was-wrong">Class 1: Errors that Tell You Exactly what was Wrong</h2>

<p>This is a prototypical example. I’ve got here a simple function that takes a user details dict and returns the username.</p>

<!-- cSpell:disable -->
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_username</span><span class="p">(</span><span class="n">user</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">user</span><span class="p">[</span><span class="s">'useranme'</span><span class="p">]</span>

<span class="n">user</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"username"</span><span class="p">:</span> <span class="s">"alec"</span><span class="p">,</span>
    <span class="s">"email"</span><span class="p">:</span> <span class="s">"sendspamhere@hotmail.com"</span><span class="p">,</span>
    <span class="s">"linkedin_url"</span><span class="p">:</span> <span class="s">"https://www.linkedin.com/in/ayyjohn/"</span>
<span class="p">}</span>

<span class="k">print</span><span class="p">(</span><span class="n">get_username</span><span class="p">(</span><span class="n">user</span><span class="p">))</span>
</code></pre></div></div>

<p>but when I run this, I get</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/stack_traces.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">10</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
    <span class="k">print</span><span class="p">(</span><span class="n">get_username</span><span class="p">(</span><span class="n">user</span><span class="p">))</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/stack_traces.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">get_username</span>
    <span class="k">return</span> <span class="n">user</span><span class="p">[</span><span class="s">'useranme'</span><span class="p">]</span>
<span class="nb">KeyError</span><span class="p">:</span> <span class="s">'useranme'</span>
</code></pre></div></div>

<p>Bummer, but simple enough.</p>

<p>First, the actual anatomy. A stack trace starts with the actual <code class="language-plaintext highlighter-rouge">Exception</code> thrown in Python. Here that’s the <code class="language-plaintext highlighter-rouge">KeyError</code>.
<code class="language-plaintext highlighter-rouge">Exception</code>s commonly come with a <code class="language-plaintext highlighter-rouge">message</code> argument that gives more context, that’s the <code class="language-plaintext highlighter-rouge">: 'useranme'</code> part. In user-defined errors these can be super helpful by linking to documentation or giving a good description of why it’s a problem.
Then, above that, the next two lines go together. <code class="language-plaintext highlighter-rouge">File "ayyjohn.github.io/code_examples/python/stack_traces.py",</code> tells you the name of the file where the error was, <code class="language-plaintext highlighter-rouge">line 2</code> is the line that it was caused on, and <code class="language-plaintext highlighter-rouge">in get_username</code> is the function that the execution was inside at the time. Finally, <code class="language-plaintext highlighter-rouge">return user['useranme']</code> is the actual line of code that broke.
Above that will be one or more calls that lead to the final, broken call. In this case, <code class="language-plaintext highlighter-rouge">&lt;module&gt;</code> just means that this was the top level because I ran this code using <code class="language-plaintext highlighter-rouge">python stack_traces.py</code> and the call to <code class="language-plaintext highlighter-rouge">print(get_username(user))</code> wasn’t inside any function.</p>

<p>Unless a user has done some custom exception handling, most stack traces should look something like this.</p>

<p>The way I read this is “There’s a <code class="language-plaintext highlighter-rouge">KeyError</code>, the key that wasn’t present in the dict was <code class="language-plaintext highlighter-rouge">"useranme"</code>, and that happened on line 2 of <code class="language-plaintext highlighter-rouge">stack_traces.py</code> when I called <code class="language-plaintext highlighter-rouge">get_username</code>, which happened because on line 10 I called <code class="language-plaintext highlighter-rouge">print(get_username(user))</code></p>

<p>As you can see, I start at the bottom and work my way up. In general, this should be the default.
It doesn’t always work, but it should be where you look first.</p>

<p>Stack traces will start with the error and the line it was caused on (that’s why it says <code class="language-plaintext highlighter-rouge">Traceback: most recent call last</code>), and then go “upward” over and over through each function that called a function that called a function to get to the error.
In the best case scenario, the typo or problem was caused by that last line and the error is self explanatory.</p>

<p>In this case, I meant <code class="language-plaintext highlighter-rouge">"username"</code> not <code class="language-plaintext highlighter-rouge">"useranme"</code>.</p>

<p>Other common versions of this are situations where you have a typo in a variable name and you get a <code class="language-plaintext highlighter-rouge">NameError</code> because that variable doesn’t exist, or a <code class="language-plaintext highlighter-rouge">SyntaxError</code> and Python points you directly to where you made an oopsy.
A fun plug for Python 3.10 is how much better its errors have gotten! <a href="https://www.python.org/downloads/release/python-3100/">Check out the improvements here</a>.</p>

<h2 id="class-2-errors-that-just-tell-you-where-to-go">Class 2: Errors that Just Tell You Where to Go</h2>

<p>Slightly worse, but still not too intimidating for newer programmers are errors like the following:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_first_users_username</span><span class="p">(</span><span class="n">users</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">users</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="s">'username'</span><span class="p">]()</span>

<span class="n">user1</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"username"</span><span class="p">:</span> <span class="s">"alec"</span><span class="p">,</span>
    <span class="s">"email"</span><span class="p">:</span> <span class="s">"sendspamhere@hotmail.com"</span><span class="p">,</span>
    <span class="s">"linkedin_url"</span><span class="p">:</span> <span class="s">"https://www.linkedin.com/in/ayyjohn/"</span>
<span class="p">}</span>

<span class="n">user2</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"username"</span><span class="p">:</span> <span class="s">"ayyjohn"</span><span class="p">,</span>
    <span class="s">"email"</span><span class="p">:</span> <span class="s">"sendspamhere@yahoo.com"</span><span class="p">,</span>
    <span class="s">"linkedin_url"</span><span class="p">:</span> <span class="s">"https://www.linkedin.com/in/alec/"</span>
<span class="p">}</span>

<span class="n">users</span> <span class="o">=</span> <span class="p">[</span><span class="n">user1</span><span class="p">,</span> <span class="n">user2</span><span class="p">]</span>

<span class="k">print</span><span class="p">(</span><span class="n">get_username</span><span class="p">(</span><span class="n">users</span><span class="p">))</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/stack_traces.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">18</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
    <span class="k">print</span><span class="p">(</span><span class="n">get_username</span><span class="p">(</span><span class="n">users</span><span class="p">))</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/stack_traces.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">get_first_users_username</span>
    <span class="k">return</span> <span class="n">users</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="s">'username'</span><span class="p">]()</span>
<span class="nb">TypeError</span><span class="p">:</span> <span class="s">'str'</span> <span class="nb">object</span> <span class="ow">is</span> <span class="ow">not</span> <span class="nb">callable</span>
</code></pre></div></div>
<!-- cSpell:enable -->

<p>What I’m seeing is that there’s a <code class="language-plaintext highlighter-rouge">TypeError</code> caused on line 2 when I called <code class="language-plaintext highlighter-rouge">get_first_users_username</code> but the first time I saw this I wasn’t positive what I did wrong.</p>

<p><code class="language-plaintext highlighter-rouge">'str' object is not callable</code>? What that means?</p>

<p>Luckily, StackOverflow has my back, and it’s got yours too. I simply Google that last line of the stack trace, and I notice <a href="https://stackoverflow.com/questions/36634449/python-str-object-is-not-callable">this post</a> which helpfully explains to me that I accidentally added an extra set of parentheses at the end of my function which means I’m trying to call a string like it’s a function.</p>

<p>Deleting those parentheses fixes the error.</p>

<!-- markdownlint-disable MD036 -->
<p><strong>Protip: if you’re not familiar with an error, try looking for it on stackoverflow</strong>
<!-- markdownlint-enable MD036 --></p>

<p>A sibling of this error is the <code class="language-plaintext highlighter-rouge">AttributeError: 'ClassName' object has no attribute 'something'</code> error, which comes from adding a <code class="language-plaintext highlighter-rouge">.something</code> to a variable that’s an instance of class <code class="language-plaintext highlighter-rouge">ClassName</code> which apparently doesn’t have a <code class="language-plaintext highlighter-rouge">self.something</code> or a <code class="language-plaintext highlighter-rouge">something()</code> method defined.
When you see <code class="language-plaintext highlighter-rouge">NoneType</code> instead of <code class="language-plaintext highlighter-rouge">ClassName</code> in that error it means you have a variable that’s equal to <code class="language-plaintext highlighter-rouge">None</code> (which is of the class <code class="language-plaintext highlighter-rouge">NoneType</code>) and you’ve tried to call a method on it. This is Python’s version of the infamous Java <a href="https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it">NullPointerException</a>.</p>

<h2 id="class-3-errors-that-are-hidden-deeper-in-the-stack-trace">Class 3: Errors that are Hidden Deeper in the Stack Trace</h2>

<p>Once you’re done writing your first programs, you’re going to start using libraries.
Libraries are great because millions of other people have written code to do things so that you don’t have to. But one major downside of them is they make debugging more complicated.</p>

<p>You can use their code wrong and cause it to throw an exception that you don’t recognize or even worse, the errors can be buried under layers of indirection because your code called their code which probably called some other library’s code and finally something broke. Let’s take a look at an example like that.</p>

<p>What could be simpler? This code should get me the HTML code from Google’s homepage.</p>

<!-- cSpell:disable -->
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>

<span class="k">def</span> <span class="nf">ping_google</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"htts://google.com"</span><span class="p">))</span>

<span class="n">ping_google</span><span class="p">()</span>
</code></pre></div></div>

<!-- cSpell:disable -->
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/stack_traces.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">6</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
    <span class="n">ping_google</span><span class="p">()</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/stack_traces.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">4</span><span class="p">,</span> <span class="ow">in</span> <span class="n">ping_google</span>
    <span class="k">print</span><span class="p">(</span><span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"htts://google.com"</span><span class="p">))</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/venv/lib/python3.7/site-packages/requests/api.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">75</span><span class="p">,</span> <span class="ow">in</span> <span class="n">get</span>
    <span class="k">return</span> <span class="n">request</span><span class="p">(</span><span class="s">'get'</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">params</span><span class="o">=</span><span class="n">params</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/venv/lib/python3.7/site-packages/requests/api.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">61</span><span class="p">,</span> <span class="ow">in</span> <span class="n">request</span>
    <span class="k">return</span> <span class="n">session</span><span class="p">.</span><span class="n">request</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="n">method</span><span class="p">,</span> <span class="n">url</span><span class="o">=</span><span class="n">url</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/venv/lib/python3.7/site-packages/requests/sessions.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">542</span><span class="p">,</span> <span class="ow">in</span> <span class="n">request</span>
    <span class="n">resp</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">prep</span><span class="p">,</span> <span class="o">**</span><span class="n">send_kwargs</span><span class="p">)</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/venv/lib/python3.7/site-packages/requests/sessions.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">649</span><span class="p">,</span> <span class="ow">in</span> <span class="n">send</span>
    <span class="n">adapter</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">get_adapter</span><span class="p">(</span><span class="n">url</span><span class="o">=</span><span class="n">request</span><span class="p">.</span><span class="n">url</span><span class="p">)</span>
  <span class="n">File</span> <span class="s">"ayyjohn.github.io/code_examples/python/venv/lib/python3.7/site-packages/requests/sessions.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">742</span><span class="p">,</span> <span class="ow">in</span> <span class="n">get_adapter</span>
    <span class="k">raise</span> <span class="n">InvalidSchema</span><span class="p">(</span><span class="s">"No connection adapters were found for {!r}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">))</span>
<span class="n">requests</span><span class="p">.</span><span class="n">exceptions</span><span class="p">.</span><span class="n">InvalidSchema</span><span class="p">:</span> <span class="n">No</span> <span class="n">connection</span> <span class="n">adapters</span> <span class="n">were</span> <span class="n">found</span> <span class="k">for</span> <span class="s">'htts://google.com'</span>
</code></pre></div></div>

<!-- cSpell:enable -->
<p>Yo, what the fuck, Python? I thought we were friends.</p>

<p>This is the shortest program I’ve written so far, so why is the error so gnarly?
No connection adapters were found? <code class="language-plaintext highlighter-rouge">InvalidSchema</code>? I didn’t write sessions.py. What is <code class="language-plaintext highlighter-rouge">site-packages</code>? How did I get here?
Guess I’ll just give up on being a software engineer.</p>

<p>Ok no, that’s dramatic, but this is a big step away from the two prior examples.
The reason is that Python starts the stack trace where the error was first caused, and it’s common for an argument passed to a library function to go unvalidated or not break anything for a while before it causes problems.
It’s really easy to get intimidated by all of this mess dumping into your terminal, but you can fall back to those same steps from before.</p>

<!-- cSpell:disable -->
<p>When you find yourself in this type of situation, the first thing I recommend is finding the first place in the stack trace that’s actually in code that you wrote. In that case, it’s <code class="language-plaintext highlighter-rouge">line 4, in ping_google</code> where i wrote <code class="language-plaintext highlighter-rouge">print(requests.get("htts://google.com))</code>.
<!-- cSpell:enable --></p>

<p>At least that will tell you, hopefully, what you wrote that caused the error (though not always as we’ll see below).</p>

<!-- cSpell:disable -->
<p>In this case, with a keen eye you’ll notice that <code class="language-plaintext highlighter-rouge">htts</code> should be <code class="language-plaintext highlighter-rouge">https</code>.
<!-- cSpell:enable --></p>

<p>Let’s move to an example where the error isn’t easy to spot, and the stack trace is largely unhelpful.</p>

<h2 id="class-4-errors-that-are-disconnected-from-the-actual-problem-entirely">Class 4: Errors that are Disconnected from the Actual Problem Entirely</h2>

<p>That last one was bad, but not that bad.</p>

<p>But now, let’s take a look at an example that was actually the inspiration for this post.</p>

<p>This week at <a href="https://adadevelopersacademy.org">Ada</a> students were working on a CRUD flask app. Here’s the necessary context:</p>

<p>There’s three models, <code class="language-plaintext highlighter-rouge">Customer</code>, <code class="language-plaintext highlighter-rouge">Video</code>, and a <code class="language-plaintext highlighter-rouge">Rental</code> model representing a user checking out a video from a video store (‘member video stores?).</p>

<!-- cSpell:disable -->
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">app</span> <span class="kn">import</span> <span class="n">db</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>

<span class="k">class</span> <span class="nc">Customer</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">String</span><span class="p">(</span><span class="mi">80</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">postal_code</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">phone_number</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">String</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">registered_at</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">datetime</span><span class="p">.</span><span class="n">utcnow</span><span class="p">)</span>


<span class="k">class</span> <span class="nc">Video</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">title</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">String</span><span class="p">(</span><span class="mi">80</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">release_date</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">total_inventory</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>


<span class="k">class</span> <span class="nc">Rental</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">__tablename__</span> <span class="o">=</span> <span class="s">"video_rentals"</span>
    <span class="nb">id</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">autoincrement</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">due_date</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">DateTime</span><span class="p">)</span>
    <span class="n">customer_id</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="p">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s">"customer.id"</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">video_id</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Column</span><span class="p">(</span><span class="n">db</span><span class="p">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">db</span><span class="p">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s">"video.id"</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">customer</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">relationship</span><span class="p">(</span><span class="s">"Customer"</span><span class="p">,</span> <span class="n">backref</span><span class="o">=</span><span class="s">"rentals"</span><span class="p">)</span>
    <span class="n">video</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">relationship</span><span class="p">(</span><span class="s">"Video"</span><span class="p">,</span> <span class="n">backref</span><span class="o">=</span><span class="s">"rentals"</span><span class="p">)</span>
</code></pre></div></div>

<p>And here’s the test in question</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">test_can_delete_customer_with_rental</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">one_checked_out_video</span><span class="p">):</span>
    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="s">"/customers/1"</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">response</span><span class="p">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
</code></pre></div></div>

<p>Finally, here’s the route being hit</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">current_app</span><span class="p">,</span> <span class="n">Blueprint</span><span class="p">,</span> <span class="n">jsonify</span>
<span class="kn">from</span> <span class="nn">app.models.customer</span> <span class="kn">import</span> <span class="n">Customer</span>
<span class="kn">from</span> <span class="nn">app.models.rental</span> <span class="kn">import</span> <span class="n">Rental</span>
<span class="kn">from</span> <span class="nn">app</span> <span class="kn">import</span> <span class="n">db</span>

<span class="n">CustomerBlueprint</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">"customers"</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s">"/customers"</span><span class="p">)</span>


<span class="o">@</span><span class="n">CustomerBlueprint</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/&lt;id&gt;"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">"DELETE"</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">delete_customer</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
    <span class="n">customer</span> <span class="o">=</span> <span class="n">Customer</span><span class="p">.</span><span class="n">query</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">customer</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">{</span><span class="s">"message"</span><span class="p">:</span> <span class="sa">f</span><span class="s">"Customer </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="s"> was not found"</span><span class="p">}</span>

    <span class="n">rentals</span> <span class="o">=</span> <span class="n">Rental</span><span class="p">.</span><span class="n">query</span><span class="p">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">customer_id</span><span class="o">=</span><span class="nb">id</span><span class="p">)</span>
    <span class="n">db</span><span class="p">.</span><span class="n">session</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="n">rentals</span><span class="p">)</span>
    <span class="n">db</span><span class="p">.</span><span class="n">session</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="n">customer</span><span class="p">)</span>
</code></pre></div></div>

<p>Running the test causes the following.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">.</span><span class="o">/</span><span class="n">tests</span><span class="o">/</span><span class="n">test_wave_02</span><span class="p">.</span><span class="n">py</span><span class="p">::</span><span class="n">test_can_delete_customer_with_rental</span> <span class="n">Failed</span><span class="p">:</span> <span class="p">[</span><span class="n">undefined</span><span class="p">]</span><span class="n">sqlalchemy</span><span class="p">.</span><span class="n">orm</span><span class="p">.</span><span class="n">exc</span><span class="p">.</span><span class="n">UnmappedInstanceError</span><span class="p">:</span> <span class="n">Class</span> <span class="s">'flask_sqlalchemy.BaseQuery'</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">mapped</span>
<span class="bp">self</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">sqlalchemy</span><span class="p">.</span><span class="n">orm</span><span class="p">.</span><span class="n">session</span><span class="p">.</span><span class="n">SignallingSession</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x1033a0610</span><span class="o">&gt;</span>
<span class="n">instance</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">flask_sqlalchemy</span><span class="p">.</span><span class="n">BaseQuery</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0x1033c7070</span><span class="o">&gt;</span>

    <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">):</span>
        <span class="s">"""Mark an instance as deleted.

        The database delete operation occurs upon ``flush()``.

        """</span>
        <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">_warn_on_events</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">_flush_warning</span><span class="p">(</span><span class="s">"Session.delete()"</span><span class="p">)</span>

        <span class="k">try</span><span class="p">:</span>
<span class="o">&gt;</span>           <span class="n">state</span> <span class="o">=</span> <span class="n">attributes</span><span class="p">.</span><span class="n">instance_state</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span>
<span class="n">E</span>           <span class="nb">AttributeError</span><span class="p">:</span> <span class="s">'BaseQuery'</span> <span class="nb">object</span> <span class="n">has</span> <span class="n">no</span> <span class="n">attribute</span> <span class="s">'_sa_instance_state'</span>

<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">sqlalchemy</span><span class="o">/</span><span class="n">orm</span><span class="o">/</span><span class="n">session</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">2054</span><span class="p">:</span> <span class="nb">AttributeError</span>

<span class="n">The</span> <span class="n">above</span> <span class="n">exception</span> <span class="n">was</span> <span class="n">the</span> <span class="n">direct</span> <span class="n">cause</span> <span class="n">of</span> <span class="n">the</span> <span class="n">following</span> <span class="n">exception</span><span class="p">:</span>

<span class="n">client</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">FlaskClient</span> <span class="o">&lt;</span><span class="n">Flask</span> <span class="s">'app'</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="n">one_checked_out_video</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="k">def</span> <span class="nf">test_can_delete_customer_with_rental</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">one_checked_out_video</span><span class="p">):</span>
        <span class="c1"># Act
</span><span class="o">&gt;</span>       <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="s">"/customers/1"</span><span class="p">)</span>

<span class="n">tests</span><span class="o">/</span><span class="n">test_wave_02</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">184</span><span class="p">:</span>
<span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">werkzeug</span><span class="o">/</span><span class="n">test</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">1031</span><span class="p">:</span> <span class="ow">in</span> <span class="n">delete</span>
    <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="nb">open</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">kw</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">testing</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">222</span><span class="p">:</span> <span class="ow">in</span> <span class="nb">open</span>
    <span class="k">return</span> <span class="n">Client</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">werkzeug</span><span class="o">/</span><span class="n">test</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">970</span><span class="p">:</span> <span class="ow">in</span> <span class="nb">open</span>
    <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">run_wsgi_app</span><span class="p">(</span><span class="n">environ</span><span class="p">.</span><span class="n">copy</span><span class="p">(),</span> <span class="n">buffered</span><span class="o">=</span><span class="n">buffered</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">werkzeug</span><span class="o">/</span><span class="n">test</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">861</span><span class="p">:</span> <span class="ow">in</span> <span class="n">run_wsgi_app</span>
    <span class="n">rv</span> <span class="o">=</span> <span class="n">run_wsgi_app</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">application</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">buffered</span><span class="o">=</span><span class="n">buffered</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">werkzeug</span><span class="o">/</span><span class="n">test</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">1096</span><span class="p">:</span> <span class="ow">in</span> <span class="n">run_wsgi_app</span>
    <span class="n">app_rv</span> <span class="o">=</span> <span class="n">app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">2464</span><span class="p">:</span> <span class="ow">in</span> <span class="n">__call__</span>
    <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">wsgi_app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">2450</span><span class="p">:</span> <span class="ow">in</span> <span class="n">wsgi_app</span>
    <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">handle_exception</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">1867</span><span class="p">:</span> <span class="ow">in</span> <span class="n">handle_exception</span>
    <span class="n">reraise</span><span class="p">(</span><span class="n">exc_type</span><span class="p">,</span> <span class="n">exc_value</span><span class="p">,</span> <span class="n">tb</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">_compat</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">39</span><span class="p">:</span> <span class="ow">in</span> <span class="n">reraise</span>
    <span class="k">raise</span> <span class="n">value</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">2447</span><span class="p">:</span> <span class="ow">in</span> <span class="n">wsgi_app</span>
    <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">full_dispatch_request</span><span class="p">()</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">1952</span><span class="p">:</span> <span class="ow">in</span> <span class="n">full_dispatch_request</span>
    <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">handle_user_exception</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">1821</span><span class="p">:</span> <span class="ow">in</span> <span class="n">handle_user_exception</span>
    <span class="n">reraise</span><span class="p">(</span><span class="n">exc_type</span><span class="p">,</span> <span class="n">exc_value</span><span class="p">,</span> <span class="n">tb</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">_compat</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">39</span><span class="p">:</span> <span class="ow">in</span> <span class="n">reraise</span>
    <span class="k">raise</span> <span class="n">value</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">1950</span><span class="p">:</span> <span class="ow">in</span> <span class="n">full_dispatch_request</span>
    <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">dispatch_request</span><span class="p">()</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">flask</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">1936</span><span class="p">:</span> <span class="ow">in</span> <span class="n">dispatch_request</span>
    <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">view_functions</span><span class="p">[</span><span class="n">rule</span><span class="p">.</span><span class="n">endpoint</span><span class="p">](</span><span class="o">**</span><span class="n">req</span><span class="p">.</span><span class="n">view_args</span><span class="p">)</span>
<span class="n">app</span><span class="o">/</span><span class="n">customer_routes</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">16</span><span class="p">:</span> <span class="ow">in</span> <span class="n">delete_customer</span>
    <span class="n">db</span><span class="p">.</span><span class="n">session</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="n">rentals</span><span class="p">)</span>
<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">sqlalchemy</span><span class="o">/</span><span class="n">orm</span><span class="o">/</span><span class="n">scoping</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">163</span><span class="p">:</span> <span class="ow">in</span> <span class="n">do</span>
    <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">registry</span><span class="p">(),</span> <span class="n">name</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">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">sqlalchemy</span><span class="o">/</span><span class="n">orm</span><span class="o">/</span><span class="n">session</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">2056</span><span class="p">:</span> <span class="ow">in</span> <span class="n">delete</span>
    <span class="n">util</span><span class="p">.</span><span class="n">raise_</span><span class="p">(</span>
<span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span> <span class="n">_</span>

    <span class="k">def</span> <span class="nf">raise_</span><span class="p">(</span>
        <span class="n">exception</span><span class="p">,</span> <span class="n">with_traceback</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">replace_context</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">from_</span><span class="o">=</span><span class="bp">False</span>
    <span class="p">):</span>
        <span class="sa">r</span><span class="s">"""implement "raise" with cause support.

        :param exception: exception to raise
        :param with_traceback: will call exception.with_traceback()
        :param replace_context: an as-yet-unsupported feature.  This is
         an exception object which we are "replacing", e.g., it's our
         "cause" but we don't want it printed.    Basically just what
         ``__suppress_context__`` does but we don't want to suppress
         the enclosing context, if any.  So for now we make it the
         cause.
        :param from\_: the cause.  this actually sets the cause and doesn't
         hope to hide it someday.

        """</span>
        <span class="k">if</span> <span class="n">with_traceback</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">exception</span> <span class="o">=</span> <span class="n">exception</span><span class="p">.</span><span class="n">with_traceback</span><span class="p">(</span><span class="n">with_traceback</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">from_</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">False</span><span class="p">:</span>
            <span class="n">exception</span><span class="p">.</span><span class="n">__cause__</span> <span class="o">=</span> <span class="n">from_</span>
        <span class="k">elif</span> <span class="n">replace_context</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="c1"># no good solution here, we would like to have the exception
</span>            <span class="c1"># have only the context of replace_context.__context__ so that the
</span>            <span class="c1"># intermediary exception does not change, but we can't figure
</span>            <span class="c1"># that out.
</span>            <span class="n">exception</span><span class="p">.</span><span class="n">__cause__</span> <span class="o">=</span> <span class="n">replace_context</span>

        <span class="k">try</span><span class="p">:</span>
<span class="o">&gt;</span>           <span class="k">raise</span> <span class="n">exception</span>
<span class="n">E</span>           <span class="n">sqlalchemy</span><span class="p">.</span><span class="n">orm</span><span class="p">.</span><span class="n">exc</span><span class="p">.</span><span class="n">UnmappedInstanceError</span><span class="p">:</span> <span class="n">Class</span> <span class="s">'flask_sqlalchemy.BaseQuery'</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">mapped</span>

<span class="n">venv</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="p">.</span><span class="mi">8</span><span class="o">/</span><span class="n">site</span><span class="o">-</span><span class="n">packages</span><span class="o">/</span><span class="n">sqlalchemy</span><span class="o">/</span><span class="n">util</span><span class="o">/</span><span class="n">compat</span><span class="p">.</span><span class="n">py</span><span class="p">:</span><span class="mi">182</span><span class="p">:</span> <span class="n">UnmappedInstanceError</span>
</code></pre></div></div>
<!-- cSpell:enable -->

<p>Be honest, can you spot the problem?</p>

<p>I couldn’t!</p>

<p>Going through the same steps as before</p>

<ul>
  <li>looking at the error itself isn’t helpful.</li>
  <li>looking at the rest of the bottom of the stack trace isn’t helpful</li>
  <li>finding the first place where the stack trace mentions our code with <code class="language-plaintext highlighter-rouge">db.session.delete(rentals)</code> tells me there’s an issue with that call but it’s not immediately apparent why.</li>
  <li>scrolling up to the top of the stacktrace, nothing immediately jumps out.</li>
</ul>

<p>My first thought was that since <code class="language-plaintext highlighter-rouge">BaseQuery</code> wasn’t mapped that there was some definition issue in the models. I figured it was coming from <code class="language-plaintext highlighter-rouge">Rental.query</code> throwing an error because the <code class="language-plaintext highlighter-rouge">Rental</code> model didn’t have a <code class="language-plaintext highlighter-rouge">.query</code> method.</p>

<p>So I checked the imports to make sure the models were what I thought they were, and I had some other students post their models to make sure nothing was noticeably different.</p>

<p><strong>Protip: debugging in teams is way better than debugging by yourself!</strong></p>

<p>Then I Googled the error and came up with exactly nothing helpful.</p>

<p>I mentioned before that as a last resort, it never helps to read the source code of the library you’re using.</p>

<p>Looking at a stack trace like this can definitely be intimidating, but one of the most awesome things about code is that there’s nothing technically magic about it.
It’s really just the same thing we’re doing in the first few examples, except the difference now is that we didn’t write the code at the bottom of the stack trace.
<!-- cSpell:disable -->
Code is code is code, and we can go read the source code for the libraries we use! In this case, what I’m seeing first is that the error came from <code class="language-plaintext highlighter-rouge">venv/lib/python3.8/site-packages/sqlalchemy/util/compat.py:182</code>.</p>

<p>A quick Google turns up the <a href="https://github.com/sqlalchemy/sqlalchemy/">source code</a> for <code class="language-plaintext highlighter-rouge">sqlalchemy</code> which isn’t even one of our direct dependencies in the project!
It’s a dependency of <a href="https://sqlalchemy.palletsprojects.com/">flask-sqlalchemy</a>. We’re going deep.
The <code class="language-plaintext highlighter-rouge">compat.py</code> file is a red herring. It’s a wrapper for handling exceptions, which means if there’s an exception thrown in sqlalchemy it’s likely going to go through here first.
But above that is our first real clue: <code class="language-plaintext highlighter-rouge">sqlalchemy/orm/session.py:2056 in delete</code>. Now we can start to think about why there might be some issue in our code when we did <code class="language-plaintext highlighter-rouge">db.session.delete(rentals)</code> or <code class="language-plaintext highlighter-rouge">db.session.delete(customer)</code></p>

<p>Luckily, the student had written code that had the same syntax as the delete customer call, so we felt confident that it was the rentals call that was problematic.
But unluckily, I’m not a Flask expert. I use <a href="https://www.djangoproject.com">Django</a>, where unfortunately <code class="language-plaintext highlighter-rouge">db.session.delete(rentals)</code> looks totally legal and valid.</p>

<p>What ended up working was comparing the types of the two calls. One succeeded, the other didn’t. <code class="language-plaintext highlighter-rouge">type(customer)</code> revealed that it was a <code class="language-plaintext highlighter-rouge">db.Model</code> instance, but <code class="language-plaintext highlighter-rouge">type(rentals)</code> said that it was a <code class="language-plaintext highlighter-rouge">flask_sqlalchemy.BaseQuery</code> instance! Hey look, it’s that thing that apparently doesn’t have a <code class="language-plaintext highlighter-rouge">delete</code> method!</p>

<p>I knew that the results of queries are iterable, and so a fix was changing <code class="language-plaintext highlighter-rouge">db.session.delete(rentals)</code> to the following</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">rental</span> <span class="ow">in</span> <span class="n">rentals</span><span class="p">:</span>
    <span class="n">db</span><span class="p">.</span><span class="n">session</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="n">rental</span><span class="p">)</span>
</code></pre></div></div>
<!-- cSpell:enable -->

<p>But more generally the problem was you can’t call <code class="language-plaintext highlighter-rouge">delete</code> on a <code class="language-plaintext highlighter-rouge">Query</code> object, you have to have an instance of the model itself.</p>

<p>What a journey!</p>

<p>On a final note, here are some other things I often try when I see a new error message</p>

<ul>
  <li>Search the error in Slack! Often times the error is specific to the company or project you’re working on, and somebody has run into it before.</li>
  <li>Read some documentation! Often when you Google something the answer won’t be spelled out exactly. The error might be a product of some logical inconsistency in your app, and it’s only after understanding that you’ve violated the API of the library in some way that you’ll figure it out.</li>
  <li>Change the code slightly to see if I can get a new error message that’s more familiar.</li>
  <li>Talk through it with somebody else, or type out what I’m thinking as if I was going to send it to someone. Usually I’ll have a hard time explaining something that I did, and more often than not the error is related to that thing I don’t understand as well as I thought</li>
</ul>

<p>Until next time!</p>]]></content><author><name></name></author><category term="python" /><category term="debugging" /><summary type="html"><![CDATA[No matter how good of a programmer you are, you will write code that breaks. Being able to read broken code is a valuable skill. Code fails more than it succeeds [citation needed]]]></summary></entry><entry><title type="html">Evaluation vs Execution in Python</title><link href="https://ayyjohn.com/posts/evaluation-vs-execution" rel="alternate" type="text/html" title="Evaluation vs Execution in Python" /><published>2021-04-25T20:30:51+00:00</published><updated>2021-04-25T20:30:51+00:00</updated><id>https://ayyjohn.com/posts/evaluation-vs-execution</id><content type="html" xml:base="https://ayyjohn.com/posts/evaluation-vs-execution"><![CDATA[<p>Most people, when they’re introduced to Python, are told it’s run top to bottom.</p>

<p>That is to say, if you write</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">f</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"hello"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"world"</span><span class="p">)</span>

<span class="n">f</span><span class="p">()</span>
</code></pre></div></div>

<p>it’ll execute the top statement first, and the second statement second.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hello</span>
<span class="n">world</span>
</code></pre></div></div>

<p>This helps us as teachers to explain to new programmers how to read code, and it’s a contrast to languages
like Javascript.</p>

<h2 id="how-javascript-does-it">How Javascript Does It</h2>

<p>Javascript is asynchronous by default and so you can’t depend on something that is invoked above happening before something that is invoked below.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">first</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">first!</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">second</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">second!</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">third</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">third!</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">first</span><span class="p">();</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="nx">second</span><span class="p">);</span>
<span class="nx">third</span><span class="p">();</span>
</code></pre></div></div>

<p>Because we technically schedule the execution of <code class="language-plaintext highlighter-rouge">second</code> with <code class="language-plaintext highlighter-rouge">setTimeout</code>, even though we tell it to happen as soon as it can, we get the following</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">first</span><span class="o">!</span>
<span class="nx">third</span><span class="o">!</span>
<span class="nx">second</span><span class="o">!</span>
</code></pre></div></div>

<p>node has moved on.</p>

<p>For more on this behavior, I recommend <a href="https://www.youtube.com/watch?v=8aGhZQkoFbQ">this video on Javascript’s execution model and the event loop</a>.</p>

<p>The “equivalent” python code would look something like this</p>

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

<span class="k">def</span> <span class="nf">first</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"first!"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">second</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"second!"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">first</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"third!"</span><span class="p">)</span>

<span class="n">first</span><span class="p">()</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">second</span><span class="p">()</span>
<span class="n">third</span><span class="p">()</span>
</code></pre></div></div>

<p>but this isn’t really the same as saying “enqueue an invocation of <code class="language-plaintext highlighter-rouge">second</code>.” What it’s really saying is “pause momentarily before executing <code class="language-plaintext highlighter-rouge">second</code> and then proceed, which it does</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>first!
second!
third!
</code></pre></div></div>

<p>without you even knowing the <code class="language-plaintext highlighter-rouge">sleep</code> call was there.</p>

<p>Without involving an async framework like <a href="https://docs.python.org/3/library/asyncio.html">asyncio</a> we can’t really replicate the same behavior in Python, which only reinforces what we were told in the beginning: Python executes top to bottom.</p>

<!-- markdownlint-disable CMD004 -->
<h2 id="you-were-sort-of-lied-to">You Were (sort of) Lied To</h2>
<!-- markdownlint-enable CMD004 -->

<p>Recently, though, I was introduced to a youtube channel <a href="https://www.youtube.com/channel/UCaiL2GDNpLYH6Wokkk1VNcg">mCoding</a> and he put up a video <a href="https://www.youtube.com/watch?v=9v8eu4MOet8">Variable Lookup Weirdness in Python</a> that broke my mental model of Python’s execution.</p>

<p>This gets back to my <a href="/posts/import-vs-runtime">import vs runtime</a> post, and only reinforces my point that there <em>is</em> a difference, even if it’s not as clearly cut as in a compiled language.</p>

<p>In his video, mCoding distinguishes between the following two functions</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">f</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">g</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
    <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span>
</code></pre></div></div>

<p>My inclination when watching this was that both functions would have the same result, but they don’t.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="n">f</span><span class="p">()</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
  <span class="n">File</span> <span class="s">"&lt;stdin&gt;"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
  <span class="n">File</span> <span class="s">"&lt;stdin&gt;"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span>
<span class="nb">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s">'x'</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="n">g</span><span class="p">()</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
  <span class="n">File</span> <span class="s">"&lt;stdin&gt;"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
  <span class="n">File</span> <span class="s">"&lt;stdin&gt;"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">g</span>
<span class="nb">UnboundLocalError</span><span class="p">:</span> <span class="n">local</span> <span class="n">variable</span> <span class="s">'x'</span> <span class="n">referenced</span> <span class="n">before</span> <span class="n">assignment</span>
</code></pre></div></div>

<p>This is only possible because Python doesn’t just evaluate code top to bottom on the fly. Code <em>is</em> executed top to bottom (as long as it’s spaghetti code), but it’s interpreted first and that interpretation allows for what looks, to us at runtime, like look-ahead.</p>

<p>This is to say nothing of a language like Rust which can tell that this type of thing will happen <em>at compile time</em>, instead of letting us define the function and only complaining when it’s executed, but that’s a story for another time.</p>]]></content><author><name></name></author><category term="python" /><summary type="html"><![CDATA[Most people, when they’re introduced to Python, are told it’s run top to bottom.]]></summary></entry><entry><title type="html">Writing Custom Lint Rules</title><link href="https://ayyjohn.com/posts/custom-lint-rules" rel="alternate" type="text/html" title="Writing Custom Lint Rules" /><published>2021-04-09T19:14:07+00:00</published><updated>2021-04-09T19:14:07+00:00</updated><id>https://ayyjohn.com/posts/custom-lint-rules</id><content type="html" xml:base="https://ayyjohn.com/posts/custom-lint-rules"><![CDATA[<p>Back in March I wrote <a href="/posts/linting-a-jekyll-blog-with-mega-linter">a post about introducing linting to this blog</a> and at the end I said I’d follow up when I started adding custom lint rules. That time is now!</p>

<h2 id="why-write-your-own-lint-rules">Why Write Your Own Lint Rules?</h2>

<p>The <a href="https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md">default rules that come with markdownlint</a> are awesome, don’t get me wrong. But as I mentioned in the previous post, linters are great for enforcing quality control, and what quality looks like differs depending on the author.</p>

<p>For example, not everyone who’s using markdown is using <a href="https://jekyllrb.com/">Jekyll</a>, and not everyone who’s using Jekyll is leveraging the <a href="https://jekyllrb.com/docs/posts/#categories">categories</a> to make a page like <a href="https://ayyjohn.com/categories">the one I have</a>. So it wouldn’t make sense for either <a href="https://github.com/DavidAnson/markdownlint/">markdownlint</a> or Jekyll to have their own lint rules for it.</p>

<p>But in this blog I’ve already encountered bugs in how I use categories, and so it makes sense for me to lint my <code class="language-plaintext highlighter-rouge">categories</code> section on each post.</p>

<h2 id="your-own-regression-testing-suite">Your Own Regression Testing Suite</h2>

<p>As I mentioned in <a href="/posts/adding-categories">my post about categories</a> I made <a href="https://shopify.github.io/liquid/">Liquid</a> iterate over all unique categories to generate my posts grouped by categories page.
And since, to Jekyll, <code class="language-plaintext highlighter-rouge">Linting</code> is a different tag than <code class="language-plaintext highlighter-rouge">linting</code>, it’ll make a new section on the categories page above all the others because capitals sort higher than lowercase letters.</p>

<p><img src="/assets/custom_lint_rules/extra_category.png" alt="extra_category" /></p>

<p>Needless to say, I didn’t want this to happen again, so there’s sort of two options.</p>

<ol>
  <li>
    <p>Create a checklist of “things to review before publishing a new post” and add <code class="language-plaintext highlighter-rouge">all categories are lowercase</code> to it.</p>
  </li>
  <li>
    <p>Write a custom lint rule to check for this automatically.</p>
  </li>
</ol>

<p>The first option might make <a href="http://atulgawande.com/book/the-checklist-manifesto/">Atul Gawande</a> proud, but it doesn’t scale. Eventually I’d have more things on that list than I want to bother to check for, and once I stop checking it, it’s worthless.</p>

<p>I’d much rather have a concise checklist where linting for all of these things is one step. The list of things I’m checking for can then grow organically as I discover bugs in new posts. I can also proactively write rules even if I’m not sure they’ve happened before and then use the linter to discover if I’ve made that error in the past and fix it.</p>

<h2 id="my-first-custom-rules">My First Custom Rules</h2>

<p>In addition to the check mentioned above about categories, there was one other bug that I wanted to cover immediately.
When you add markdownlint-disable comments to your code to say that you explicitly want to ignore a broken lint rule, if these blocks are inside a highlight block, they’ll actually get rendered in the text of the post.</p>

<p>Here’s an example from my <a href="/posts/tuple-unpacking">tuple unpacking post</a> that my friend Kyle caught (thanks Kyle!)</p>

<!-- markdownlint-disable CMD001 MD031 -->

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="o">%</span> <span class="n">highlight</span> <span class="n">python</span> <span class="o">%</span><span class="p">}</span>
<span class="o">&lt;</span><span class="err">!</span><span class="o">--</span> <span class="n">markdownlint</span><span class="o">-</span><span class="n">disable</span> <span class="n">MD037</span> <span class="o">--&gt;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">l</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="mi">10</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">_</span>
<span class="mi">11</span>
<span class="p">{</span><span class="o">%</span> <span class="n">endhighlight</span> <span class="o">%</span><span class="p">}</span>
<span class="o">&lt;</span><span class="err">!</span><span class="o">--</span> <span class="n">markdownlint</span><span class="o">-</span><span class="n">enable</span> <span class="n">MD037</span> <span class="o">--&gt;</span>
</code></pre></div></div>

<!-- markdownlint-enable CMD001 MD031 -->

<p>This code produces the following rendered text</p>

<p><img src="/assets/custom_lint_rules/rendered_comment.png" alt="rendered_comment" /></p>

<p>This can be fixed simply by enforcing that there are none of those comments inside those highlight blocks.</p>

<h2 id="coding-it-out">Coding It Out</h2>

<p>Now that I’ve got my two rules formalized in words, it’s time to translate them into code.
<a href="https://github.com/DavidAnson/markdownlint/blob/main/doc/CustomRules.md">markdownlint has a section on authoring custom rules</a> that’s pretty comprehensive, and by combining that with <a href="https://github.com/DavidAnson/markdownlint/tree/main/lib">the code for all of the built in rules</a> and the <a href="https://github.com/DavidAnson/markdownlint/blob/main/helpers/helpers.js">helpers code</a> I was able to write my first rule</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// no_rendered_comments.js</span>
<span class="kd">const</span> <span class="p">{</span>
  <span class="nx">forEachLine</span><span class="p">,</span>
  <span class="nx">getLineMetadata</span><span class="p">,</span>
  <span class="nx">inlineCommentRe</span><span class="p">,</span>
<span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">markdownlint-rule-helpers</span><span class="dl">"</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">start_highlight</span> <span class="o">=</span> <span class="sr">/{% highlight </span><span class="se">\w</span><span class="sr">+ %}/</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">end_highlight</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">{% endhighlight %}</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">noRenderedComments</span> <span class="o">=</span> <span class="p">(</span><span class="nx">params</span><span class="p">,</span> <span class="nx">onError</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">insideHighlightBlock</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  <span class="nx">forEachLine</span><span class="p">(</span><span class="nx">getLineMetadata</span><span class="p">(</span><span class="nx">params</span><span class="p">),</span> <span class="p">(</span><span class="nx">line</span><span class="p">,</span> <span class="nx">lineIndex</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">line</span> <span class="o">&amp;&amp;</span> <span class="nx">start_highlight</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">line</span><span class="p">))</span> <span class="p">{</span>
      <span class="nx">insideHighlightBlock</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">line</span> <span class="o">&amp;&amp;</span> <span class="nx">line</span> <span class="o">==</span> <span class="nx">end_highlight</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">insideHighlightBlock</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="kd">const</span> <span class="nx">isComment</span> <span class="o">=</span> <span class="nx">inlineCommentRe</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">line</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">insideHighlightBlock</span> <span class="o">&amp;&amp;</span> <span class="nx">isComment</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">onError</span><span class="p">({</span>
        <span class="na">lineNumber</span><span class="p">:</span> <span class="nx">lineIndex</span><span class="p">,</span>
      <span class="p">});</span>
    <span class="p">}</span>
  <span class="p">});</span>
<span class="p">};</span>

<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">names</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">CMD001</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">no-rendered-comments</span><span class="dl">"</span><span class="p">],</span>
  <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">No markdown comments should be inside code blocks</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">tags</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">test</span><span class="dl">"</span><span class="p">],</span>
  <span class="na">function</span><span class="p">:</span> <span class="nx">noRenderedComments</span><span class="p">,</span>
<span class="p">};</span></code></pre></figure>

<p>All this really does is toggle “am I inside a code block?” on and off when it sees <code class="language-plaintext highlighter-rouge">{% highlight * %}</code> and <code class="language-plaintext highlighter-rouge">{% endhighlight %}</code> , and then raise an error if it sees an inline comment block while <code class="language-plaintext highlighter-rouge">insideHighlightBlock</code> is <code class="language-plaintext highlighter-rouge">true</code>.</p>

<p>I haven’t figured out how to get <a href="https://github.com/nvuillam/mega-linter/">mega-linter</a>’s version of markdownlint to find custom rules yet, but I’ve got <a href="https://github.com/nvuillam/mega-linter/issues/396">a question open on the repo</a> for it.</p>

<p>For now, I installed markdownlint and markdownlint-rule-helpers via npm, and was able to test this new rule by creating a test file</p>

<!-- markdown-link-check-disable -->
<!-- markdownlint-disable CMD001 MD031 -->

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="o">%</span> <span class="n">highlight</span> <span class="n">python</span> <span class="o">%</span><span class="p">}</span>
<span class="o">&lt;</span><span class="err">!</span><span class="o">--</span> <span class="n">markdownlint</span><span class="o">-</span><span class="n">disable</span> <span class="n">MD022</span> <span class="n">MD025</span> <span class="o">--&gt;</span>
<span class="kn">import</span> <span class="nn">webbrowser</span>
<span class="kn">import</span> <span class="nn">hashlib</span>

<span class="n">webbrowser</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="s">"https://xkcd.com/353/"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">geohash</span><span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">,</span> <span class="n">datedow</span><span class="p">):</span>
    <span class="p">...</span>
<span class="p">{</span><span class="o">%</span> <span class="n">endhighlight</span> <span class="o">%</span><span class="p">}</span>
<span class="o">&lt;</span><span class="err">!</span><span class="o">--</span> <span class="n">markdownlint</span><span class="o">-</span><span class="n">enable</span> <span class="n">MD022</span> <span class="n">MD025</span> <span class="o">--&gt;</span>
</code></pre></div></div>

<!-- markdown-link-check-enable -->
<!-- markdownlint-enable CMD001 MD031 -->

<p>and running the following</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>markdownlint <span class="nt">-r</span> custom_lint_rules/no_rendered_comments.js ./test_posts/test_post.md <span class="nt">-c</span> .markdown-lint.json
</code></pre></div></div>

<p>which produced the following</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>test_posts/test-post.md:8 CMD001/no-rendered-comments No markdown comments should be inside code blocks
</code></pre></div></div>

<p>Success! Although funnily enough I had to disable my own linter in a bunch of places in this post to show these.</p>

<p>For the second rule I had to do a bit of digging because one thing I noticed while writing the first rule is that the <a href="https://jekyllrb.com/docs/front-matter/">frontmatter</a> from the test post wasn’t counted in <code class="language-plaintext highlighter-rouge">forEachLine</code>.
By searching the markdownlint source code for <code class="language-plaintext highlighter-rouge">frontmatter</code> I found that there’s a function in <a href="https://github.com/DavidAnson/markdownlint/blob/main/lib/markdownlint.js"><code class="language-plaintext highlighter-rouge">markdownlint.js</code></a> called <code class="language-plaintext highlighter-rouge">removeFrontMatter</code> which is used to store the <code class="language-plaintext highlighter-rouge">frontMatterLines</code> in <code class="language-plaintext highlighter-rouge">params</code>.
Once I had that, though, the rest was pretty simple</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// lowercase_categories.js</span>
<span class="kd">const</span> <span class="p">{</span>
  <span class="nx">forEachLine</span><span class="p">,</span>
  <span class="nx">getLineMetadata</span><span class="p">,</span>
  <span class="nx">inlineCommentRe</span><span class="p">,</span>
<span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">markdownlint-rule-helpers</span><span class="dl">"</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">isLowerCase</span> <span class="o">=</span> <span class="p">(</span><span class="nx">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">string</span> <span class="o">!=</span> <span class="nx">string</span><span class="p">.</span><span class="nx">toUpperCase</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="nx">string</span> <span class="o">==</span> <span class="nx">string</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">lowercaseCategories</span> <span class="o">=</span> <span class="p">(</span><span class="nx">params</span><span class="p">,</span> <span class="nx">onError</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">categories_lines</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">frontMatterLines</span><span class="p">.</span><span class="nx">filter</span><span class="p">((</span><span class="nx">line</span><span class="p">)</span> <span class="o">=&gt;</span>
    <span class="nx">line</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="dl">"</span><span class="s2">categories: </span><span class="dl">"</span><span class="p">)</span>
  <span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">categories_lines</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">onError</span><span class="p">({</span>
      <span class="na">lineNumber</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
      <span class="na">detail</span><span class="p">:</span> <span class="dl">"</span><span class="s2">all posts should have categories specified</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">});</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">categories_lines</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">onError</span><span class="p">({</span>
      <span class="na">lineNumber</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
      <span class="na">detail</span><span class="p">:</span> <span class="dl">"</span><span class="s2">posts are not allowed to specify multiple lists of categories</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">});</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">array_start</span> <span class="o">=</span> <span class="nx">categories_lines</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">"</span><span class="s2">[</span><span class="dl">"</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">array_end</span> <span class="o">=</span> <span class="nx">categories_lines</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">categories_string</span> <span class="o">=</span> <span class="nx">categories_lines</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">substring</span><span class="p">(</span>
      <span class="nx">array_start</span><span class="p">,</span>
      <span class="nx">array_end</span>
    <span class="p">);</span>
    <span class="kd">let</span> <span class="nx">categories</span> <span class="o">=</span> <span class="nx">categories_string</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2">, </span><span class="dl">"</span><span class="p">);</span>
    <span class="nx">categories</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">category</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isLowerCase</span><span class="p">(</span><span class="nx">category</span><span class="p">))</span> <span class="p">{</span>
        <span class="nx">onError</span><span class="p">({</span>
          <span class="na">lineNumber</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
        <span class="p">});</span>
      <span class="p">}</span>
    <span class="p">});</span>
  <span class="p">}</span>
<span class="p">};</span>

<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">names</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">CMD002</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">all-categories-lowercase</span><span class="dl">"</span><span class="p">],</span>
  <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">all categories in frontmatter should be lowercase</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">tags</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">test</span><span class="dl">"</span><span class="p">],</span>
  <span class="na">function</span><span class="p">:</span> <span class="nx">lowercaseCategories</span><span class="p">,</span>
<span class="p">};</span></code></pre></figure>

<p>This one is pretty easy to follow. All posts should have exactly one <code class="language-plaintext highlighter-rouge">categories</code> section, and all the strings inside that section should be lowercase. I’m not particularly adept at JavaScript so there’s probably a cleaner way to do it, relying less on indexes, for example.</p>

<p>But one of the takeaways here is that neither of these custom lint rules are what I’d call “production ready.” They’re tested rather anecdotally by running them manually against one test case I know they should fail and one that I know they should pass, but there’s not exactly complete coverage, and it’s not automated. I might do that as a follow-up.</p>

<p>But for now the point is that they’re only as good as I need them to be! I’m the only one using them, and they prevent my site from regressions so they’re doing their job.</p>

<h2 id="future-developments">Future Developments</h2>

<p>This second rule is a good candidate for automated fixing. Right now, it’ll just tell me if it finds a post where the categories aren’t all lowercase, but by providing it some more information I could have it automatically downcase all of them too. But that’s a story for another time.</p>

<p>If you want to check out any of the custom rules I’ve written, you can see the source code <a href="https://github.com/ayyjohn/ayyjohn.github.io/tree/master/custom_lint_rules">here</a></p>]]></content><author><name></name></author><category term="javascript" /><category term="linting" /><category term="meta" /><summary type="html"><![CDATA[Back in March I wrote a post about introducing linting to this blog and at the end I said I’d follow up when I started adding custom lint rules. That time is now!]]></summary></entry><entry><title type="html">Import Time vs Run Time in Python</title><link href="https://ayyjohn.com/posts/import-vs-runtime" rel="alternate" type="text/html" title="Import Time vs Run Time in Python" /><published>2021-04-04T05:21:07+00:00</published><updated>2021-04-04T05:21:07+00:00</updated><id>https://ayyjohn.com/posts/import-vs-runtime</id><content type="html" xml:base="https://ayyjohn.com/posts/import-vs-runtime"><![CDATA[<p>Earlier today I was working with my student at <a href="https://adadevelopersacademy.org/">Ada Academy</a> about <code class="language-plaintext highlighter-rouge">import</code>s in Python.
The root of their question was essentially “what happens when I import something” which lead to me doing a poor job of trying to explain the difference between “import time” and “run time.”</p>

<p>In compiled languages, compile time and run time are distinct. Compilation is its own step, and imports are checked during compile time to make sure they exist and that there are no cycles. Compiled code is then run, hence “run time.”</p>

<p>But in Python, an interpreted language, code is just run.
And so the reason that I was failing was that I was trying to describe a distinction between “importing code” and “running code” which, in Python, <a href="https://www.benkuhn.net/importtime/">doesn’t actually exist</a>.</p>

<!-- markdownlint-disable CMD004 -->
<h2 id="so-what-happens">So What Happens?</h2>
<!-- markdownlint-enable CMD004 -->

<p>When Python modules are imported, they’re executed (aka run), and it just so happens that <strong>most Python code, when executed, doesn’t do anything but declare something</strong>, like a class. When you write a function in the Python REPL, all that happens is that function is available for later use.</p>

<p>But if you write something like <code class="language-plaintext highlighter-rouge">print("hello")</code>, it runs, and if you include bare, executable code in a Python file and import that file, it’ll get run too. That’s actually what’s happening when you type</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">antigravity</span></code></pre></figure>

<p>because the body of that file looks like</p>

<!-- markdown-link-check-disable -->
<!-- markdownlint-disable MD022 MD025 CMD003 CMD004 -->

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1"># antigravity.py
</span><span class="kn">import</span> <span class="nn">webbrowser</span>
<span class="kn">import</span> <span class="nn">hashlib</span>

<span class="n">webbrowser</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="s">"https://xkcd.com/353/"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">geohash</span><span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">,</span> <span class="n">datedow</span><span class="p">):</span>
    <span class="p">...</span></code></pre></figure>

<!-- markdownlint-enable MD022 MD025 CMD003 CMD004 -->
<!-- markdown-link-check-enable -->

<p>and so that <code class="language-plaintext highlighter-rouge">webbrowser.open("https://xkcd.com/353/")</code> call is immediately invoked. Don’t do this.</p>

<h2 id="theres-a-better-way">There’s a Better Way</h2>

<p>This behavior is why you tend to see a script that could be written as</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
<span class="k">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span></code></pre></figure>

<p>written as</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
    <span class="k">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span></code></pre></figure>

<p>If you don’t do this, your code <em>will</em> be executed when it’s imported, which is usually bad, or at least unexpected by the user.
But no matter which way you do it, you can execute the code via <code class="language-plaintext highlighter-rouge">python filename.py</code>, except now you can also do <code class="language-plaintext highlighter-rouge">from filename import main</code>.</p>

<p>And it’s pretty cheap, as the code’s author, to add this extra boilerplate just in case you want to reuse your function by importing it later.</p>

<p>This is why, unless you’re explicitly trying to make something happen when your library is imported, you should pretty much never have any code that actually executes at import time. You don’t want users of your code to unknowingly run code that they didn’t ask for.</p>

<h2 id="why-does-this-matter">Why Does This Matter?</h2>

<p>So, back to the original point of “what happens when I import something?” the answer is: it’s executed.</p>

<p>Still, I think it’s useful to draw a distinction between import time and run time in Python, even if a functional one doesn’t exist.</p>

<p>To me, it makes sense to define “import time” as basically what happens between the first and last import statement in the file you’re executing and “run time” as everything that happens after the interpreter starts up once you try and execute a script. So import time is a subset of run time.</p>

<!-- markdownlint-disable MD022 MD025 CMD003 CMD004 -->

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1"># run time starts
# import time starts
</span><span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">space</span>
<span class="kn">import</span> <span class="nn">the_human_race</span>
<span class="c1"># import time ends
</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">():</span>
    <span class="k">pass</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">f</span><span class="p">()</span>

<span class="c1"># run time ends</span></code></pre></figure>

<!-- markdownlint-enable MD022 MD025 CMD003 CMD004 -->

<p>During import time Python is effectively <a href="https://chrisyeh96.github.io/2017/08/08/definitive-guide-python-imports.html#basics-of-the-python-import-and-syspath">searching everywhere it knows it should</a> for the module you told it to look for, and then recursively importing (aka running) everything that file needs until eventually it hits a file that requires no new dependencies. (This creates what’s called a “Dependency Tree” which you can actually inspect using a tool called <a href="https://pypi.org/project/pipdeptree/">pipdeptree</a>.</p>

<p>And the goal should be to have as few things as possible that you don’t expect happen during that process.</p>]]></content><author><name></name></author><category term="python" /><summary type="html"><![CDATA[Earlier today I was working with my student at Ada Academy about imports in Python. The root of their question was essentially “what happens when I import something” which lead to me doing a poor job of trying to explain the difference between “import time” and “run time.”]]></summary></entry><entry><title type="html">The Best Hello World Program</title><link href="https://ayyjohn.com/posts/best-hello-world" rel="alternate" type="text/html" title="The Best Hello World Program" /><published>2021-03-31T14:21:07+00:00</published><updated>2021-03-31T14:21:07+00:00</updated><id>https://ayyjohn.com/posts/best-hello-world</id><content type="html" xml:base="https://ayyjohn.com/posts/best-hello-world"><![CDATA[<p>What makes a good “Hello, World!” program?</p>

<p>I’ve been volunteering as a 1:1 mentor at Ada Academy for over a year now, but this month, for the first time, I volunteered for <a href="https://adadevelopersacademy.org/prepare/">Ada Build</a>, their prep course to help incoming students get ready to apply to the program.</p>

<p>As opposed to the students I’ve mentored 1:1, these students are often at the very beginning of their programming journey and need help with things from basic terminology and syntax to their first loop.</p>

<p>It’s not so often, once you’ve been programming for a few years, that you get to go back and put yourself in the shoes of someone who looks at something like</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">print</span><span class="p">(</span><span class="s">"Hello, World"</span><span class="p">)</span></code></pre></figure>

<p>and goes “I don’t get it,” but it really is a pivotal moment in any coder’s journey. The resistance they feel in that moment can impact whether or not they decide to continue on with programming at all.
That got me thinking about what the best possible introduction to programming is.</p>

<h2 id="the-original">The Original</h2>

<p>Ever since <a href="https://en.wikipedia.org/wiki/%22Hello,_World!%22_program">the original C programming language demo</a> programmers have been taking their first steps by writing this program, in some form, in whatever language they’re first introduced to.</p>

<!-- markdownlint-disable MD025 CMD003 CMD004 -->

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp"># include &lt;stdio.h&gt;
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
   <span class="n">printf</span><span class="p">(</span><span class="s">"Hello, World!"</span><span class="p">);</span>
   <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<!-- markdownlint-enable MD025 CMD003 CMD004 -->

<p>Not ideal. Even if you get the gist of this, you’re likely to wonder what the heck a bunch of it means.</p>

<h2 id="the-best">The Best</h2>

<p>I still think Python 2.7’s Hello World is the cleanest I’ve ever seen.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">print</span> <span class="s">"Hello, World!"</span></code></pre></figure>

<p>Part of it is that <code class="language-plaintext highlighter-rouge">print</code> wasn’t a normal function in Python2 and so it’s missing parentheses, which often confuse people who don’t have a math background. Part of it is just the sheer simplicity of it.</p>

<p>No <code class="language-plaintext highlighter-rouge">\n</code>, no <code class="language-plaintext highlighter-rouge">;</code>. The choice of <code class="language-plaintext highlighter-rouge">print</code> as the function name doesn’t require understanding what “the console” is, or what objects are, like JavaScript’s does, and is more clear than Ruby’s <code class="language-plaintext highlighter-rouge">puts</code> (though, points to Ruby for cuteness)</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">puts</span> <span class="s2">"Hello, World!"</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello, World!</span><span class="dl">"</span><span class="p">);</span></code></pre></figure>

<p>or Java for that matter, which not only requires a call to <code class="language-plaintext highlighter-rouge">System.out</code>, but needs to be wrapped in all this boilerplate and compiled before a user can run their first program.</p>

<h2 id="the-worst">The Worst</h2>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.io.*</span><span class="o">;</span>

<span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span>
    <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Hello, World!"</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>Languages like Go and Rust have intimidating syntax, and it’s often not possible to explain to new programmers why they’re great, which is why they’re rarely recommended to users as a first language.</p>

<p>Given that most people’s first programming language nowadays is likely to be one of <code class="language-plaintext highlighter-rouge">[Python, Java, Ruby, JavaScript]</code> it’s probably good to over-index a bit on what the first programming experience will be like to newcomers.</p>

<p>We want to teach new programmers that anything is possible and that programming is delightful, and that starts with their first program.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">__hello__</span></code></pre></figure>

<p>wait, no</p>]]></content><author><name></name></author><category term="python" /><summary type="html"><![CDATA[What makes a good “Hello, World!” program?]]></summary></entry><entry><title type="html">Linting a Jekyll Blog with Mega-Linter</title><link href="https://ayyjohn.com/posts/linting-a-jekyll-blog-with-mega-linter" rel="alternate" type="text/html" title="Linting a Jekyll Blog with Mega-Linter" /><published>2021-03-27T23:21:07+00:00</published><updated>2021-03-27T23:21:07+00:00</updated><id>https://ayyjohn.com/posts/linting-a-jekyll-blog-with-mega-linter</id><content type="html" xml:base="https://ayyjohn.com/posts/linting-a-jekyll-blog-with-mega-linter"><![CDATA[<p>A friend of mine introduced me to <a href="https://www.gwern.net/About#">Gwern’s blog</a> a while back. Some of the posts are interesting, and they’re definitely well written.
And even though I didn’t become a regular reader, one thing that caught my eye was the meta section of the About page where they talk about various technical aspects of the blog, including custom linters.</p>

<h2 id="why-lint-your-blog">Why Lint Your Blog?</h2>

<p>There’s a lot of good reasons to lint your blog. Basics start at spellchecking and syntax, where you might catch a typo or two per post. But there’s a lot more available.
Once a blog gets large enough, you start having degradation of quality because it’s not easy to manually check for consistency between posts.
You may not use the same formatting, and so posts might look different. You might forget if you normally use <code class="language-plaintext highlighter-rouge">##</code> or <code class="language-plaintext highlighter-rouge">###</code> for sub headers, or if you always use punctuation at the end of lines.
Posts may go through multiple drafts, and you might leave yourself <code class="language-plaintext highlighter-rouge">todos</code> that you want to take care of before publishing.
Links may expire, and so older posts will have dead links that don’t point anywhere anymore.
Long story short, just like having an auto linter on a production codebase is a great idea, so is having an auto linter on your blog! So that’s what I added this morning to this site.</p>

<h2 id="what-i-chose-to-use">What I Chose to Use</h2>

<p>Gwern wrote a custom linter, and working at Dropbox we had a combination of <a href="https://github.com/psf/black">Black</a> and custom lint rules. The custom rules are really useful for projects like deprecations where you can say, via code, “hey, if you see someone writing new code invoking {deprecated function} put a note on their pull request directing them to a doc with info about the deprecation.”</p>

<p>I definitely want some custom lint rules for myself, though they’re not quite as useful when you’re the only person working on a project. But to get started, I wanted to leverage a pretty strong base layer, because I know almost nothing about ASTs and the kind of stuff necessary to write my own linter from scratch. I’d much rather add custom rules to an existing framework.
By default, I mostly care about linting the actual posts, so that’s just markdown. <a href="https://github.com/DavidAnson/markdownlint">markdownlint</a> exists, and would probably do fine, but it also comes as part of <a href="https://github.com/nvuillam/mega-linter">mega-linter</a> which extends to a bunch of other languages.
This means I can start by limiting linting to my posts, and over time expand to other folders and file types, just how you would if you were introducing a linter or typing system into a production codebase that had been around a while.</p>

<h2 id="starting-out">Starting Out</h2>

<p>After installing mega-linter-runner using</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx mega-linter-runner <span class="nt">--install</span>
</code></pre></div></div>

<p>I tried running it in default mode on my whole repo using</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">lint</span><span class="o">=</span><span class="s2">"npx mega-linter-runner"</span>
</code></pre></div></div>

<p>and there were thousands of errors. So many that I didn’t even know where to start.
So the first thing I did is majorly decrease scope.</p>

<p>mega-linter uses a <code class="language-plaintext highlighter-rouge">.mega-linter.yml</code> as a config file, and will actually help you create one using a setup wizard when you install. I did this, and then edited it to contain the following</p>

<!-- markdownlint-disable MD003 MD022 MD025 MD032 CMD003 CMD004 -->

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="nn">---</span>
<span class="c1"># only check for markdown errors and spelling</span>
<span class="na">ENABLE</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">MARKDOWN</span>
<span class="pi">-</span> <span class="s">SPELL</span>
<span class="c1"># only look in the _posts/ directory</span>
<span class="na">FILTER_REGEX_INCLUDE</span><span class="pi">:</span> <span class="s">(_posts/)</span>
<span class="c1"># suppress fun output cause I'm boring</span>
<span class="na">PRINT_ALPACA</span><span class="pi">:</span> <span class="no">false</span>
<span class="c1"># tell me how long linting is taking</span>
<span class="na">SHOW_ELAPSED_TIME</span><span class="pi">:</span> <span class="no">true</span>
<span class="nn">---</span></code></pre></figure>

<!-- markdownlint-enable MD003 MD022 MD025 MD032 CMD003 CMD004 -->

<p>I also added a handy alias to my <code class="language-plaintext highlighter-rouge">.zshrc</code> and set mega-linter to use the <code class="language-plaintext highlighter-rouge">ruby</code> flavor since Jekyll is a ruby based project.</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">lint</span><span class="o">=</span><span class="s2">"npx mega-linter-runner --flavor ruby"</span>
</code></pre></div></div>

<p>This produced a much more manageable report where I can actually parse the logs and understand what to change. The first output had a lot of things I don’t care about, and this one still does, but not nearly as many.</p>

<h2 id="spelling">Spelling</h2>

<p>The first thing the report tells me is that I have a <em>lot</em> of misspelled words. My <a href="/posts/python-easter-eggs">Python Easter Eggs post</a> by itself gave me hundreds because of the ROT13 cyphertext inside it. Luckily, each of the individual linters that mega-linter uses can be more tightly configured, including straight up ignoring files or disabling certain rules in a block.</p>

<p>The spelling checker it uses by default is called <a href="https://www.npmjs.com/package/cspell">cSpell</a>, and you can create a <code class="language-plaintext highlighter-rouge">.cspell.json</code> with keys like <code class="language-plaintext highlighter-rouge">words</code> and <code class="language-plaintext highlighter-rouge">flagWords</code> where <code class="language-plaintext highlighter-rouge">words</code> is a list of overrides where you can basically tell it “yes, this is a word.” and <code class="language-plaintext highlighter-rouge">flagWords</code> is the opposite, so you can include words you tend to mis-type, or words you always want to type a certain way. So for example, if you can’t remember whether you want to always use “nonbinary” or “non-binary” or “non binary”, you can add the two versions you don’t want to <code class="language-plaintext highlighter-rouge">flagWords</code>.
Also, in order to generate the report of invalid words, cSpell will create a sorted array of words it didn’t recognize so that you can just copy all of them into your <code class="language-plaintext highlighter-rouge">words</code> list and remove any you didn’t mean to add, rather than adding them one by one.</p>

<p>Here’s mine (heavily truncated) as an example</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.1"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"words"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"bazel"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"xkcd"</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"flagWords"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"yeet"</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<h2 id="links">Links</h2>

<p>Next, my linter told me my blog had a lot of broken links. This is terrible! It sucks to be reading a post you find really interesting and clicking a link to take your research deeper only to find that it’s a dead end.
The only thing was that a lot of my links <em>weren’t</em> dead, they’re just local. Luckily, just like cSpell had its own local config, <a href="https://github.com/tcort/markdown-link-check">markdown-link-checker</a> has its own <code class="language-plaintext highlighter-rouge">.markdown-link-check.json</code></p>

<!-- markdown-link-check-disable -->

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"replacementPatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"pattern"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^/posts/"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"replacement"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://ayyjohn.com/posts/"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"pattern"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^/assets/"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"replacement"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://ayyjohn.com/assets/"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<!-- markdown-link-check-enable -->

<p>What this does is ensure that any time I include a local link it substitutes my domain before checking validity. This worked for all my internal links, however the report was still telling me I had some further dead links. I went in to fix them, but when I noticed that when I tried them in Chrome, they were still active.
It turns out that some websites actively prevent DDoS attacks and/or crawling by rejecting requests from non-browsers.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[markdown-link-check] _posts/2021-02-15-broken-reddit.md - ERROR - 1 error(s)
--Error detail:

FILE: /tmp/lint/_posts/2021-02-15-broken-reddit.md
[✓] <span class="nv">&lt;https://ayyjohn.com/posts/tampering-with-reddit&gt;</span>
[✖] <span class="nv">&lt;https://www.cloudflare.com/learning/performance/why-minify-javascript-code/&gt;</span>

2 links checked.

ERROR: 1 dead links found!
[✖] <span class="nv">&lt;https://www.cloudflare.com/learning/performance/why-minify-javascript-code/&gt;</span> → Status: 403
</code></pre></div></div>

<p>For now, this can be silenced by adding</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"aliveStatusCodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="mi">200</span><span class="p">,</span><span class="w">
        </span><span class="mi">403</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>to my <code class="language-plaintext highlighter-rouge">.markdown-link-check.json</code> which tells the linter that if it gets a <code class="language-plaintext highlighter-rouge">403</code> from the website to consider it okay. And with that, my lint output looks like the following</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----SUMMARY--+--------------------------+-------+-------+--------+--------------+
| Descriptor  | Linter                   | Files | Fixed | Errors | Elapsed time |
+-------------+--------------------------+-------+-------+--------+--------------+
| ❌ MARKDOWN | markdownlint             |    13 |     0 |    801 |        2.16s |
| ✅ MARKDOWN | markdown-link-check      |    13 |       |      0 |       31.15s |
| ✅ MARKDOWN | markdown-table-formatter |    13 |     0 |      0 |        1.09s |
| ✅ SPELL    | cspell                   |    13 |       |      0 |        5.66s |
| ✅ SPELL    | misspell                 |    13 |     0 |      0 |        1.63s |
+-------------+--------------------------+-------+-------+--------+--------------+
</code></pre></div></div>

<p>Great! Now only <code class="language-plaintext highlighter-rouge">markdownlint</code> errors remaining.</p>

<!-- markdownlint-disable CMD003 CMD004 -->
<h2 id="markdownlint">markdownlint</h2>
<!-- markdownlint-enable CMD003 CMD004 -->

<p>While the other two linters were looking for particular things (dead links and spelling errors), <a href="https://github.com/DavidAnson/markdownlint">markdownlint</a> is a multi-purpose, highly configurable tool for finding pretty much <em>anything</em> wrong with your markdown.
There’s an exhaustive list of them with an explanation of each in the <code class="language-plaintext highlighter-rouge">README</code>, and the report it generates will tell you the file and line where you violated each rule. Also, by adding</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">APPLY_FIXES</span><span class="pi">:</span> <span class="s">all</span>
</code></pre></div></div>

<p>to your <code class="language-plaintext highlighter-rouge">.mega-linter.yml</code> you can have it automatically fix any that it knows how to. This is great for bulk fixing things like newlines and spacing.</p>

<p>With so many failures, the first step was to figure out which rules I actually care about. By creating a <code class="language-plaintext highlighter-rouge">.markdown-lint.json</code> and adding the following to it</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"MD003"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"style"</span><span class="p">:</span><span class="w"> </span><span class="s2">"atx"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"MD013"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"MD033"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>I can silence a <em>lot</em> of the errors from <code class="language-plaintext highlighter-rouge">MD103</code> (long lines) and <code class="language-plaintext highlighter-rouge">MD033</code> (no-inline-html) because frankly I don’t care about either of those things.</p>

<p>With a lot less noise, the output was much nicer. My goal at this point became to add inline ignores for the exceptions that I wanted to remain, and then turn on the <code class="language-plaintext highlighter-rouge">APPLY_FIXES</code> flag for the remaining ones.</p>

<p>For example, in my last post about tuple unpacking I had the following snippet</p>

<figure class="highlight"><pre><code class="language-markdown" data-lang="markdown">{% highlight python %}
<span class="gt">&gt;&gt;&gt; l = (10, 11)</span>
<span class="gt">&gt;&gt;&gt; x, _= l</span>
<span class="gt">&gt;&gt;&gt; x</span>
10
<span class="gt">&gt;&gt;&gt;_</span>
11
{% endhighlight %}</code></pre></figure>

<p>which rule <code class="language-plaintext highlighter-rouge">MD037</code> flagged as a “space in emphasis” because it recognized the <code class="language-plaintext highlighter-rouge">_</code> as me trying to italicize text.
If you wrap that code in the following tags, it silences the error, similar to something like <code class="language-plaintext highlighter-rouge">type: ignore</code> in MyPy.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- markdownlint-disable MD037 --&gt;</span>
<span class="c">&lt;!-- markdownlint-enable MD037 --&gt;</span>
</code></pre></div></div>

<p>After a series of those, the remaining lint warnings were all for fixable errors like <code class="language-plaintext highlighter-rouge">MD047</code> (files should end with a single newline character), so I turned on <code class="language-plaintext highlighter-rouge">APPLY_FIXES: all</code> and let the linter fix the remaining issues.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----SUMMARY--+--------------------------+-------+-------+--------+--------------+
| Descriptor  | Linter                   | Files | Fixed | Errors | Elapsed time |
+-------------+--------------------------+-------+-------+--------+--------------+
| ✅ MARKDOWN | markdownlint             |    13 |   104 |    104 |        2.73s |
| ✅ MARKDOWN | markdown-link-check      |    13 |       |      0 |       22.69s |
| ✅ MARKDOWN | markdown-table-formatter |    13 |     0 |      0 |        0.49s |
| ✅ SPELL    | cspell                   |    13 |       |      0 |        5.87s |
| ✅ SPELL    | misspell                 |    13 |     0 |      0 |         1.4s |
+-------------+--------------------------+-------+-------+--------+--------------+
</code></pre></div></div>

<p>Awesome!</p>

<p>With this solid base, I can follow up by slowly expanding the scope to new directories and new linters.
In addition, mega-linter has some really awesome features that let you do things like only lint new changes, or disable certain linters by default. For example, I added</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">DISABLE_LINTERS</span><span class="pi">:</span>

<span class="pi">-</span> <span class="s">MARKDOWN_MARKDOWN_LINK_CHECK</span></code></pre></figure>

<p>to my <code class="language-plaintext highlighter-rouge">.mega-linter.yml</code> so that it won’t automatically check for broken links. I can always run that manually every so often, but as you can see from the <code class="language-plaintext highlighter-rouge">Elapsed time</code> part of the table above, link checking takes 22 seconds, while the next slowest part of linting is spelling at about six seconds.</p>

<p>In a follow-up post I’ll go into more detail about adding custom lint rules. Look out for it!</p>]]></content><author><name></name></author><category term="meta" /><category term="jekyll" /><category term="linting" /><summary type="html"><![CDATA[A friend of mine introduced me to Gwern’s blog a while back. Some of the posts are interesting, and they’re definitely well written. And even though I didn’t become a regular reader, one thing that caught my eye was the meta section of the About page where they talk about various technical aspects of the blog, including custom linters.]]></summary></entry><entry><title type="html">Comically Valid Syntax in Python</title><link href="https://ayyjohn.com/posts/tuple-unpacking" rel="alternate" type="text/html" title="Comically Valid Syntax in Python" /><published>2021-03-26T21:21:07+00:00</published><updated>2021-03-26T21:21:07+00:00</updated><id>https://ayyjohn.com/posts/tuple-unpacking</id><content type="html" xml:base="https://ayyjohn.com/posts/tuple-unpacking"><![CDATA[<p>Sometimes you run across something in Python that looks like it should generate a syntax error but doesn’t.
Here’s a monstrosity I ran into this morning.</p>

<h2 id="tuples-in-python">Tuples in Python</h2>

<p>I’ve always found tuples and tuple unpacking in Python to behave more than a little strangely.</p>

<p>For example, it’s a pretty common accident to include an extra comma and find out that rather than</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="mi">10</span></code></pre></figure>

<p>you’ve got</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="p">(</span><span class="mi">10</span><span class="p">,)</span></code></pre></figure>

<p>and as it turns out, this is a perfectly reasonable way to create tuples! parentheses are optional.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">l</span>
<span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">)</span></code></pre></figure>

<p>but then of course when you explicitly try to create a tuple with one element</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="mi">10</span></code></pre></figure>

<p>Urgh.</p>

<p>I guess that’s what you should expect, because the point of parentheses in Python is just order of operations and grouping multiline statements. So, technically, the <em>correct</em> way to make a tuple is the first one, and the fact that the parentheses are there in the <code class="language-plaintext highlighter-rouge">__str__</code> representation is just for our fragile eyes.</p>

<h2 id="unpacking-tuples">Unpacking Tuples</h2>

<p>Unpacking tuples is another syntax lots of people might have seen but not have explored.</p>

<p>For those who aren’t familiar, tuple unpacking basically allows you to do multiple assignments in one expression as long as the number of variables matches the number of items, and it actually works for all iterables.</p>

<p>The most basic example:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span> <span class="o">=</span> <span class="n">l</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="mi">10</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">y</span>
<span class="mi">11</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">z</span>
<span class="mi">12</span></code></pre></figure>

<p>but there’s also the convention of using <code class="language-plaintext highlighter-rouge">_</code> to drop items if you only want one</p>

<!-- markdownlint-disable MD037 -->

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">l</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="mi">10</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">_</span>
<span class="mi">11</span></code></pre></figure>

<!-- markdownlint-enable MD037 -->

<p>and that underscore is actually a variable, it’s just convention for “not needed.” You do need <em>something</em> there though, cause if you do</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="p">,</span> <span class="o">=</span> <span class="n">l</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
  <span class="n">File</span> <span class="s">"&lt;stdin&gt;"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
<span class="nb">ValueError</span><span class="p">:</span> <span class="n">too</span> <span class="n">many</span> <span class="n">values</span> <span class="n">to</span> <span class="n">unpack</span> <span class="p">(</span><span class="n">expected</span> <span class="mi">1</span><span class="p">)</span></code></pre></figure>

<p>wait. it doesn’t complain that it’s a syntax error?</p>

<p>oh lord.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="p">,</span> <span class="o">=</span> <span class="n">l</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="mi">10</span></code></pre></figure>

<p>and as you may know, whitespace in some cases in Python can be ignored:</p>

<p><code class="language-plaintext highlighter-rouge">[10, 11, 12] == [10,11,12]</code></p>

<p>which leads us to this monstrosity
<!-- markdownlint-disable MD037 --></p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="p">,</span><span class="o">=</span> <span class="n">l</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">l</span>
<span class="p">(</span><span class="mi">10</span><span class="p">,)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span>
<span class="mi">10</span></code></pre></figure>

<!-- markdownlint-enable MD037 -->

<!-- markdownlint-disable MD036 -->
<p><em>sigh</em>
<!-- markdownlint-enable MD036 --></p>]]></content><author><name></name></author><category term="python" /><summary type="html"><![CDATA[Sometimes you run across something in Python that looks like it should generate a syntax error but doesn’t. Here’s a monstrosity I ran into this morning.]]></summary></entry><entry><title type="html">Being Able to Answer Stupid Questions</title><link href="https://ayyjohn.com/posts/justice-for-max-dell" rel="alternate" type="text/html" title="Being Able to Answer Stupid Questions" /><published>2021-03-08T18:21:07+00:00</published><updated>2021-03-08T18:21:07+00:00</updated><id>https://ayyjohn.com/posts/justice-for-max-dell</id><content type="html" xml:base="https://ayyjohn.com/posts/justice-for-max-dell"><![CDATA[<p>One of my favorite things about knowing how to program is the ability it gives me to do really stupid things. Not like, hacking the government or stealing money or anything, just satisfy my brain’s silly curiosity.</p>

<p>Randal Munroe, creator of <a href="https://xkcd.com/">XKCD</a>, wrote a book called <a href="https://www.amazon.com/What-Scientific-Hypothetical-Questions-International/dp/0544456866">What If</a> that captures this idea pretty perfectly.
His knowledge of physics, math, and chemistry allow him to do reasonable calculations to answer questions like “<a href="https://www.youtube.com/watch?v=I64CQp6z0Pk">If all digital data were stored on punch cards how big would Google’s data warehouse be?</a>” by looking at their power consumption, data center size, and expenditures on drives.</p>

<p>It’s like a whimsical version of dimensional analysis, that process in high school chem class where you convert units by multiplying them out.
(<em>It’s also how you should approach obnoxious interview questions like “how many elevator repairmen work in New York City?</em>”)</p>

<h2 id="brains-are-weird">Brains are Weird</h2>

<p>Earlier this week I was rewatching Breaking Bad with my girlfriend. One of the fun gimmicks of the show is that during the opening credits they take all the names and insert a chemical element’s symbol.
So for example, Aaron Paul becomes A<strong>Ar</strong>on Paul because they inserted <code class="language-plaintext highlighter-rouge">Ar</code> for Argon, and Bryan Cranston becomes <strong>Br</strong>yan Cranston because they inserted the <code class="language-plaintext highlighter-rouge">Br</code> for Bromium.</p>

<p>And that got me wondering whether there are reasonable names that you can’t put elemental symbols in.</p>

<h2 id="the-question">The Question</h2>

<p>What would happen if somebody had auditioned for a part in Breaking Bad but they were denied the role because their name couldn’t be “elementified”?</p>

<p><img src="/assets/max_dell/periodic_table.png" alt="periodic table" /></p>

<p>Just the first three rows cause a lot of problems by themselves. They knock off any names containing <code class="language-plaintext highlighter-rouge">H, B, C, N, O F, P, or S</code>, and then Iodine, Uranium, and Yttrium take care of  <code class="language-plaintext highlighter-rouge">I</code>, <code class="language-plaintext highlighter-rouge">U</code>, and <code class="language-plaintext highlighter-rouge">Y</code> too.</p>

<p>This means the only vowels available are <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">E</code>, and even then a lot of common uses for them are eliminated by elements like Argon, Astatine, Neon, and Beryllium.</p>

<p>Then there are rude elements like Thorium and Arsenic that take away a whole bunch of names by themselves by removing <code class="language-plaintext highlighter-rouge">Th</code> and <code class="language-plaintext highlighter-rouge">As</code> which are super common combinations.</p>

<p>All this is to say that it didn’t feel easy to come up with any off the top of my head, so I decided to use programming to answer the question.</p>

<h2 id="switching-to-code">Switching to Code</h2>

<p>This question can be reduced to the combination of three other questions</p>

<ol>
  <li>What are all possible first and last names,</li>
  <li>Which of them do not contain an elemental symbol</li>
  <li>Are there any actors with those names?</li>
</ol>

<p>One thing I love about the internet is that if you have a question, probably somebody has already had it before.
However, a few quick Google searches reveal that this hasn’t been asked on Quora or Reddit before. Someone <em>has</em> created <a href="https://pypi.org/project/PeriodicElements/">a Python module for elements</a>, though, and someone <em>has</em> created <a href="https://github.com/smashew/NameDatabases">a database of common first and last names</a>, and IMDb will let us look for actors given a name, too. So now all I needed to do was aggregate those three together.</p>

<p>Here’s what I came up with</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">elements</span> <span class="kn">import</span> <span class="n">elements</span>

<span class="n">all_element_symbols</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="n">elements</span><span class="p">.</span><span class="n">AllSymbols</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="n">all_element_symbols</span><span class="p">)</span>

<span class="n">first_name_count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"first_names.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">first_names</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">first_name</span> <span class="ow">in</span> <span class="n">first_names</span><span class="p">.</span><span class="n">readlines</span><span class="p">():</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span><span class="n">symbol</span> <span class="ow">in</span> <span class="n">first_name</span> <span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="n">all_element_symbols</span><span class="p">):</span>
            <span class="n">first_name_count</span> <span class="o">+=</span> <span class="mi">1</span>
            <span class="k">print</span><span class="p">(</span><span class="n">first_name</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s">""</span><span class="p">)</span>

<span class="n">last_name_count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"last_names.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">last_names</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">last_name</span> <span class="ow">in</span> <span class="n">last_names</span><span class="p">.</span><span class="n">readlines</span><span class="p">():</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span><span class="n">symbol</span> <span class="ow">in</span> <span class="n">last_name</span> <span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="n">all_element_symbols</span><span class="p">):</span>
            <span class="n">last_name_count</span> <span class="o">+=</span> <span class="mi">1</span>
            <span class="k">print</span><span class="p">(</span><span class="n">last_name</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s">""</span><span class="p">)</span></code></pre></figure>

<p>I’m always amazed how little code it takes for things like this. So many things reduce to simple things like <code class="language-plaintext highlighter-rouge">is x in any of y</code> or <code class="language-plaintext highlighter-rouge">does there exist one item in x for which some_condition</code></p>

<p>This ended up finding just over 30 reasonable first names and over 100 last names (though some of them were kinda ehhh)</p>

<p>A lot of these aren’t common enough to search for, so rather than try randomly with all possible combinations of first and last names from this list, there are a few of each we can filter down to and search for. Names like <code class="language-plaintext highlighter-rouge">Max</code>, <code class="language-plaintext highlighter-rouge">Adele</code>, <code class="language-plaintext highlighter-rouge">Emma</code>, and <code class="language-plaintext highlighter-rouge">Jade</code>.</p>

<h2 id="the-outcome">The Outcome</h2>

<p>From this escapade I was able to find <a href="https://www.imdb.com/name/nm2938935/">Max Dell</a>, a stunt actor who I guarantee would have been hired if it were not for this shameful discrimination by the show’s creators. I don’t have proof or anything, just this research.</p>

<p>Justice for Max Dell.</p>]]></content><author><name></name></author><category term="python" /><summary type="html"><![CDATA[One of my favorite things about knowing how to program is the ability it gives me to do really stupid things. Not like, hacking the government or stealing money or anything, just satisfy my brain’s silly curiosity.]]></summary></entry></feed>