<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Cameron MacLeod</title><link href="https://www.cameronmacleod.com/" rel="alternate"></link><link href="https://www.cameronmacleod.com/feeds/all.atom.xml" rel="self"></link><id>https://www.cameronmacleod.com/</id><updated>2023-08-06T00:00:00+01:00</updated><entry><title>How to sort parent nodes before child nodes? - Topological sort</title><link href="https://www.cameronmacleod.com/blog/topological-sort" rel="alternate"></link><published>2023-08-06T00:00:00+01:00</published><updated>2023-08-06T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2023-08-06:/blog/topological-sort</id><summary type="html">&lt;p&gt;&lt;img alt="" src="/images/toposort/header.png"&gt;&lt;/p&gt;
&lt;p&gt;Dependencies between things are common, but they can be tricky to manage in code.
You might think of using a graph to model your system, but how can you ensure that
thing A happens before thing B? This is where the idea of a topological sort comes in.&lt;/p&gt;
&lt;p&gt;This article …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="" src="/images/toposort/header.png"&gt;&lt;/p&gt;
&lt;p&gt;Dependencies between things are common, but they can be tricky to manage in code.
You might think of using a graph to model your system, but how can you ensure that
thing A happens before thing B? This is where the idea of a topological sort comes in.&lt;/p&gt;
&lt;p&gt;This article is aimed at programmers who want to use topological sorting. I won't
explain the maths but I will give code examples. There are also &lt;a href="http://localhost:8001/blog/topological-sort#how-do-you-use-a-topological-sort"&gt;links&lt;/a&gt;
to help you find an implementation for your language. The examples will be in Python, but the
concepts should apply generally.&lt;/p&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#why-would-you-need-a-topological-sort"&gt;Why would you need a topological sort?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#what-is-a-topological-sort"&gt;What is a topological sort?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#cycles-and-topological-sorts"&gt;Cycles and topological sorts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#how-do-you-use-a-topological-sort"&gt;How do you use a topological sort?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#multi-threading-and-topologicalsorter"&gt;Multi-threading and TopologicalSorter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id="why-would-you-need-a-topological-sort"&gt;Why would you need a topological sort?&lt;a class="headerlink" href="#why-would-you-need-a-topological-sort" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A package manager is a piece of software that installs other software. For example,
a user could ask a package manager to install a text editor. It would then be the
package manager's job to know where to find the text editor and how to install it.&lt;/p&gt;
&lt;p&gt;Software packages almost always have dependencies. To install a text editor, you
might first need to install a UI library and that UI library might require a C++
compiler. This dependency graph would look like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A text editor package depending on gcc, a C++ compiler" src="/images/toposort/simpledependency.svg"&gt;&lt;/p&gt;
&lt;p&gt;This example is simple, but in the general case each package might have many dependencies
and packages may share dependencies. This quickly becomes a tangled mess, and
making sure that dependencies are installed before the software that needs them
becomes difficult.&lt;/p&gt;
&lt;p&gt;You can phrase this problem more generally. Given a graph, with dependencies between
parent nodes and child nodes, how do you produce a list of nodes such that parent
nodes come before their child nodes?&lt;/p&gt;
&lt;p&gt;You can sort nodes of a graph in this way using a topological sort.&lt;/p&gt;
&lt;h2 id="what-is-a-topological-sort"&gt;What is a topological sort?&lt;a class="headerlink" href="#what-is-a-topological-sort" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A topological sort is a fancy name for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Taking a graph that has parent and child nodes (a &lt;strong&gt;directed&lt;/strong&gt; graph)&lt;/li&gt;
&lt;li&gt;Making a list of nodes in that graph such that:&lt;ul&gt;
&lt;li&gt;Each parent node will always appear before its children&lt;/li&gt;
&lt;li&gt;Each child node will come after its parents in the list&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So taking a graph like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A more complex dependency graph" src="/images/toposort/complexdependencies.svg"&gt;&lt;/p&gt;
&lt;p&gt;A topological sort might look like:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Depends on&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. gcc&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Network library&lt;/td&gt;
&lt;td&gt;gcc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. UI library&lt;/td&gt;
&lt;td&gt;gcc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Python&lt;/td&gt;
&lt;td&gt;gcc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Language server&lt;/td&gt;
&lt;td&gt;gcc, Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6. Text editor&lt;/td&gt;
&lt;td&gt;Network library, UI library, Language server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In the above ordering, a package always comes after its dependencies.&lt;/p&gt;
&lt;p&gt;In the case of a package manager, this would mean that topological sorting produces
an installation order that ensures dependencies are always installed before the
software that depends on them. This is great, because there will never be missing
dependency errors!&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Why is it called a topological sort?&lt;/summary&gt;
&lt;br&gt;
&lt;p&gt;
&lt;b&gt;&lt;i&gt;hand-waving begins&lt;/i&gt;&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
There is a branch of maths called topology which deals with shape and structure.
Graph theory originally came out of topology.
&lt;/p&gt;
&lt;p&gt;
Dependencies give structure to a graph, and topology is structure. Sorting a graph
by its dependencies, therefore, can be considered a &lt;i&gt;topological&lt;/i&gt; sort.
&lt;/p&gt;
&lt;p&gt;
There are a lot of Wikipedia pages with topology in their name in this area, so it
feels like the name intuitively fits, but I can't give you a more concrete
answer unfortunately. Further reading below!
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Topological_graph_theory"&gt;Topological graph theory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Network_topology"&gt;Network topology&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Topological_sorting"&gt;Topological sorting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;b&gt;&lt;i&gt;hand-waving ends, back to the coding&lt;/i&gt;&lt;/b&gt;
&lt;/details&gt;
&lt;p&gt;&lt;a name="how-to-cycles"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="cycles-and-topological-sorts"&gt;Cycles and topological sorts&lt;a class="headerlink" href="#cycles-and-topological-sorts" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can't apply a topological sort to a graph with a cycle in it.
A cycle is where two or more nodes depend on each other. For example, &lt;code&gt;A&lt;/code&gt; depends
on &lt;code&gt;B&lt;/code&gt;, but &lt;code&gt;B&lt;/code&gt; also depends on &lt;code&gt;A&lt;/code&gt;. In this case you can't produce a topological
sort, because both &lt;code&gt;A, B&lt;/code&gt; and &lt;code&gt;B, A&lt;/code&gt; have a child node before a parent node.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A cycle with two nodes" src="/images/toposort/cycle.svg"&gt;&lt;/p&gt;
&lt;p&gt;What can you do about cycles? Fundamentally, cycles cannot be topologically sorted.
That said, there are a few things you could do depending on what problem you are solving:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Manually remove the cycle&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are building a graph by hand, then the easiest way to fix a problem like this
is to manually remove the cycle. This can be done by removing conflicting edges.&lt;/p&gt;
&lt;p&gt;For example, in the &lt;code&gt;A-&amp;gt;B-&amp;gt;A&lt;/code&gt; graph above, you could remove &lt;code&gt;A&lt;/code&gt;'s dependency on &lt;code&gt;B&lt;/code&gt;,
or &lt;code&gt;B&lt;/code&gt;'s dependency on &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Throw an error to the user/caller of your code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If the graph is being passed from outside your code, then it might be acceptable to
throw an error in case of a cycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Algorithmically remove cycles&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The problem of removing cycles from a graph while keeping the maximum number of
edges is called the &lt;a href="https://en.wikipedia.org/wiki/Feedback_arc_set"&gt;feedback arc set&lt;/a&gt;.
There are algorithms for the feedback arc set, but they are not for the faint of
heart! Proceed with caution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/6284469/how-to-remove-cycles-in-an-unweighted-directed-graph-such-that-the-number-of-ed"&gt;How to remove cycles in an unweighted directed graph, such that the number of edges is maximised? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cs.stackexchange.com/questions/90481/how-to-remove-cycles-from-a-directed-graph"&gt;How to remove cycles from a directed graph? - Computer Science Stack Exchange&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-do-you-use-a-topological-sort"&gt;How do you use a topological sort?&lt;a class="headerlink" href="#how-do-you-use-a-topological-sort" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;For those who just want Python code, I've compiled it in &lt;a href="https://gist.github.com/notexactlyawe/606734bcffdaa7d0c091dfbe55f09baa"&gt;this gist&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The below examples are all in Python, however, here are some links for libraries in
other languages. I have not used any of the below, but hopefully they can provide a
starting point for your research.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript: &lt;a href="https://www.npmjs.com/package/toposort"&gt;toposort - npm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Java: &lt;a href="https://jgrapht.org/javadoc/org.jgrapht.core/org/jgrapht/traverse/TopologicalOrderIterator.html"&gt;TopologicalOrderIterator - JGraphT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Rust: &lt;a href="https://docs.rs/petgraph/latest/petgraph/algo/fn.toposort.html"&gt;petagraph::algo::toposort&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thankfully Python (&lt;a href="https://docs.python.org/3/whatsnew/3.9.html"&gt;from version 3.9&lt;/a&gt;) comes
with topological sort in the standard library! The wonderful &lt;a href="https://docs.python.org/3/library/graphlib.html"&gt;graphlib&lt;/a&gt;
provides everything you need.&lt;/p&gt;
&lt;p&gt;Going back to the package manager example from earlier, you could model a package and
its dependencies with the below class:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;    
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;    


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;          
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;                   
    &lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above code defines a class &lt;code&gt;Package&lt;/code&gt; that has two fields, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;depends_on&lt;/code&gt;.
&lt;code&gt;name&lt;/code&gt; is the name of the package and &lt;code&gt;depends_on&lt;/code&gt; is a list of other package names
that this package depends on. The fields have type hints on them for readability.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/dataclasses.html"&gt;&lt;code&gt;@dataclass&lt;/code&gt;&lt;/a&gt; is a really powerful
decorator that creates a lot of boilerplate methods, such as &lt;code&gt;__init__&lt;/code&gt; for free. Thanks
to &lt;code&gt;@dataclass&lt;/code&gt;, you can use &lt;code&gt;Package&lt;/code&gt; like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;editor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ui_lib&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;ui_lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ui_lib&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gcc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;gcc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gcc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create three packages. A text editor which depends on a UI library which in
turn depends on GCC:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A text editor package depending on gcc, a C++ compiler" src="/images/toposort/simpledependency.svg"&gt;&lt;/p&gt;
&lt;p&gt;After creating the packages, the next job is to create a &lt;a href="https://docs.python.org/3.9/library/graphlib.html#graphlib.TopologicalSorter"&gt;&lt;code&gt;TopologicalSorter&lt;/code&gt;&lt;/a&gt;.
A &lt;code&gt;TopologicalSorter&lt;/code&gt; has methods for topologically sorting a graph. The below code
creates one and tells it about the packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TopologicalSorter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui_lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ui_lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the above snippet, the &lt;code&gt;add&lt;/code&gt; method takes a node as the first argument, and its dependencies
as the following arguments. The code uses an asterisk &lt;code&gt;*&lt;/code&gt; to unpack the &lt;code&gt;depends_on&lt;/code&gt; fields
into separate arguments. If you've not seen this syntax before, check out
&lt;a href="https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists"&gt;the Python docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are a couple of things to note in the above code snippet. Firstly, all arguments
provided to &lt;code&gt;add&lt;/code&gt; represent nodes and must be &lt;a href="https://stackoverflow.com/questions/42203673/in-python-why-is-a-tuple-hashable-but-not-a-list"&gt;hashable&lt;/a&gt;.
This means that strings and tuples are valid nodes, but lists are not.&lt;/p&gt;
&lt;p&gt;Secondly, you may notice that the &lt;code&gt;text_editor&lt;/code&gt; node was added before the nodes it
depends on. This is because &lt;code&gt;TopologicalSorter&lt;/code&gt; is very forgiving and allows you to
add nodes in any order. From the &lt;a href="https://docs.python.org/3.9/library/graphlib.html#graphlib.TopologicalSorter.add"&gt;docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a node that has not been provided before is included among &lt;em&gt;predecessors&lt;/em&gt; it will be automatically added to the graph with no predecessors of its own.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The next step is to call &lt;code&gt;static_order&lt;/code&gt; on the &lt;code&gt;TopologicalSorter&lt;/code&gt; to produce a
topological sort of the package graph:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_order&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will print:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gcc
ui_lib
editor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Great! This prints packages in the correct installation order. GCC can be installed
first because it has no dependencies, and installing it unblocks installing the UI library
which, in turn, unblocks installing the text editor.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;static_order()&lt;/code&gt; returns an &lt;a href="https://realpython.com/python-iterators-iterables/"&gt;iterator&lt;/a&gt;,
which is why the code uses a &lt;code&gt;for&lt;/code&gt; loop to get the resulting nodes. Iterators are usually
more efficient than lists, but if you wanted the result in a list, you could do
the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_order&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# nodes = [&amp;quot;gcc&amp;quot;, &amp;quot;ui_lib&amp;quot;, &amp;quot;editor&amp;quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For a more complex example, imagine the text editor had more dependencies:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A more complex dependency graph" src="/images/toposort/complexdependencies.svg"&gt;&lt;/p&gt;
&lt;p&gt;In the dependency graph above, there are a lot more packages, and some of them
even share dependencies! Luckily, &lt;code&gt;TopologicalSorter&lt;/code&gt; will handle them without a problem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;editor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ui_lib&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;lang_server&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;network_lib&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;lang_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lang_server&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gcc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;python&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;network_lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;network_lib&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gcc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;ui_lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ui_lib&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gcc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;python&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gcc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;gcc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gcc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TopologicalSorter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# more add()s here&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_order&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will print:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gcc
network_lib
ui_lib
python
lang_server
editor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;TopologicalSorter&lt;/code&gt; is a powerful tool indeed! However, what happens when you accidentally
pass it a cycle? The below code example creates a cycle to demonstrate what happens:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;editor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lang_server&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;lang_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lang_server&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;editor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TopologicalSorter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lang_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;lang_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_order&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="c1"&gt;# raises CycleError&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the code calls &lt;code&gt;static_order&lt;/code&gt;, it raises a &lt;code&gt;CycleError&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Traceback (most recent call last):
 File &amp;quot;/home/cameron/src/scratch/cycle-toposort.py&amp;quot;, line 21, in &amp;lt;module&amp;gt;
   for node in ts.static_order():
 File &amp;quot;/usr/lib/python3.9/graphlib.py&amp;quot;, line 242, in static_order
   self.prepare()
 File &amp;quot;/usr/lib/python3.9/graphlib.py&amp;quot;, line 104, in prepare
   raise CycleError(f&amp;quot;nodes are in a cycle&amp;quot;, cycle)
graphlib.CycleError: (&amp;#39;nodes are in a cycle&amp;#39;, [&amp;#39;editor&amp;#39;, &amp;#39;lang_server&amp;#39;, &amp;#39;editor&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Take a look at the section above on &lt;a href="#how-to-cycles"&gt;dealing with cycles&lt;/a&gt; for pointers
on handling this error.&lt;/p&gt;
&lt;h2 id="multi-threading-and-topologicalsorter"&gt;Multi-threading and TopologicalSorter&lt;a class="headerlink" href="#multi-threading-and-topologicalsorter" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Calling &lt;code&gt;static_order&lt;/code&gt;, as in the examples above, produces a list that works well
with single-threaded code. However, it doesn't tell you which nodes can be processed
at the same time.&lt;/p&gt;
&lt;p&gt;To take the package manager example, a naive implementation using &lt;code&gt;static_order&lt;/code&gt;
may end up installing a package at the same time as its dependency. This could cause
problems.&lt;/p&gt;
&lt;p&gt;A naive implementation using two threads and &lt;code&gt;static_order&lt;/code&gt; would break on the
simple dependencies example from earlier. It would try to install gcc at the same
time as the UI library, which would fail as the UI library depends on gcc:&lt;/p&gt;
&lt;p&gt;&lt;img alt="An example of a naive multi-threaded implementation causing problems" src="/images/toposort/multithreadconflict.svg"&gt;&lt;/p&gt;
&lt;p&gt;Processing multiple nodes at once is useful as it can speed up your code, but
is incompatible with &lt;code&gt;static_order()&lt;/code&gt;. You can still use &lt;code&gt;graphlib&lt;/code&gt; in multi-threaded
code, though. Below are steps from the docs to use &lt;code&gt;TopologicalSorter&lt;/code&gt; in a
parallel way:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Create an instance of the &lt;code&gt;TopologicalSorter&lt;/code&gt; with an optional initial graph.&lt;/li&gt;
&lt;li&gt;Add additional nodes to the graph.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;prepare()&lt;/code&gt; on the graph.&lt;/li&gt;
&lt;li&gt;While &lt;code&gt;is_active()&lt;/code&gt; is True, iterate over the nodes returned by &lt;code&gt;get_ready()&lt;/code&gt; and process them. Call &lt;code&gt;done()&lt;/code&gt; on each node as it finishes processing.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;You've already seen how to do steps 1 and 2, that's the same as in the earlier code.
To recap, here's how to add nodes to the &lt;code&gt;TopologicalSorter&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TopologicalSorter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;text_editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After creating a &lt;code&gt;TopologicalSorter&lt;/code&gt; and adding nodes to it, step 3 is to call
&lt;code&gt;prepare()&lt;/code&gt; on it. &lt;code&gt;prepare()&lt;/code&gt; marks the graph as ready for processing and
checks for cycles:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;graphlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CycleError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# do something with the error&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is where a &lt;code&gt;graphlib.CycleError&lt;/code&gt; may be raised, so the code uses a
&lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt; to deal with the error. After calling &lt;code&gt;prepare()&lt;/code&gt;, you can't add
any more nodes to the graph.&lt;/p&gt;
&lt;p&gt;Before writing the code to get packages from &lt;code&gt;TopologicalSorter&lt;/code&gt;, there needs to
be a way of simulating package installation. Since installing packages takes time,
the code can simulate it with &lt;code&gt;time.sleep&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;queue&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;

&lt;span class="n"&gt;installed_packages_queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;install_package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;* Installing package &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;installed_packages_queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above code defines a function &lt;code&gt;install_package&lt;/code&gt; that &lt;a href="https://docs.python.org/3/library/time.html#time.sleep"&gt;sleeps&lt;/a&gt; for a second and
then puts the package that it just 'installed' onto a &lt;a href="https://docs.python.org/3/library/queue.html"&gt;queue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The queue is used to communicate to the &lt;code&gt;TopologicalSorter&lt;/code&gt; that packages depending
on the now installed package can be processed. A queue allows the code to wait
for any package to be installed even if multiple threads are installing packages.&lt;/p&gt;
&lt;p&gt;Step 4 is where things get a little more complicated as it introduces three new
functions, &lt;code&gt;is_active()&lt;/code&gt;, &lt;code&gt;get_ready()&lt;/code&gt; and &lt;code&gt;done()&lt;/code&gt;. Let's take a look at the
code and then step through it to see what it does:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;ready_packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Ready to install &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ready_packages&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;install_package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ready_packages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;installed_package&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;installed_packages_queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;installed_package&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Installed package &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;installed_package&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor"&gt;&lt;code&gt;ThreadPoolExecutor&lt;/code&gt;&lt;/a&gt;
is a convenient way to work with threads. You can submit jobs to a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;
and have them automatically run in different threads. It simplifies a lot of the
boilerplate and clean-up that typically comes with threaded code.&lt;/p&gt;
&lt;p&gt;The code creates a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; which will get cleaned up automatically
once execution leaves the &lt;code&gt;with&lt;/code&gt; block. &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; is a &lt;a href="https://docs.python.org/3/reference/datamodel.html#context-managers"&gt;&lt;strong&gt;context manager&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code then enters into the loop that the &lt;code&gt;graphlib&lt;/code&gt; docs described:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While &lt;code&gt;is_active()&lt;/code&gt; is True, iterate over the nodes returned by &lt;code&gt;get_ready()&lt;/code&gt; and process them. Call &lt;code&gt;done()&lt;/code&gt; on each node as it finishes processing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;is_active()&lt;/code&gt; reports whether there are nodes left to process. Until all
the nodes have been returned by &lt;code&gt;get_ready()&lt;/code&gt;, &lt;code&gt;is_active()&lt;/code&gt; will return &lt;code&gt;True&lt;/code&gt;.
The code uses &lt;code&gt;is_active()&lt;/code&gt; to stop once all packages are installed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_ready()&lt;/code&gt; gets a tuple of nodes that can be processed right now. A node can be
processed if all of its parents have been processed or if it has no parents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;ready_packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Ready to install &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ready_packages&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;install_package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ready_packages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above code snippet gets a list of currently ready packages and then submits them
as jobs to the &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; using &lt;code&gt;map(func, iterable)&lt;/code&gt;. This creates a new
&lt;code&gt;install_package&lt;/code&gt; job for each package that &lt;code&gt;get_ready&lt;/code&gt; returns. Note that &lt;code&gt;map&lt;/code&gt; is
non-blocking.&lt;/p&gt;
&lt;p&gt;The code then blocks on a package being put onto &lt;code&gt;installed_packages_queue&lt;/code&gt;. The
&lt;code&gt;.get()&lt;/code&gt; call will return after the first &lt;code&gt;install_package&lt;/code&gt; returns:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;installed_package&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;installed_packages_queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;installed_package&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Installed package &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;installed_package&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;done(package)&lt;/code&gt; marks a node as processed. This enables &lt;code&gt;get_ready()&lt;/code&gt; to return
any child nodes of the processed node. In the above code, &lt;code&gt;done&lt;/code&gt; marks a package as
installed, which allows &lt;code&gt;get_ready()&lt;/code&gt; to return packages that depend on the
installed package.&lt;/p&gt;
&lt;p&gt;With all these pieces in place, here's the English version of the above code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;While there are packages left to install  &lt;ol&gt;
&lt;li&gt;Get the packages that can currently be installed&lt;/li&gt;
&lt;li&gt;Start an installation thread for each package&lt;/li&gt;
&lt;li&gt;Wait for a package to be installed by listening on &lt;code&gt;installed_packages_queue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mark that package as done in the &lt;code&gt;TopologicalSorter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Go round again to see whether any more package can now be installed&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It took me a little while to wrap my head around this example, so don't be afraid
to re-read the above a couple of times! It's worth noting that if your
use case works with single-threaded processing, then &lt;code&gt;static_order&lt;/code&gt; is an easier
option.&lt;/p&gt;
&lt;p&gt;Running the code in the attached &lt;a href="https://gist.github.com/notexactlyawe/606734bcffdaa7d0c091dfbe55f09baa"&gt;gist&lt;/a&gt;
produces the following output:&lt;/p&gt;
&lt;script async id="asciicast-600838" src="https://asciinema.org/a/600838.js"&gt;&lt;/script&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="headerlink" href="#conclusion" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Topological sorting is a really powerful tool. It allows you to order nodes in a graph
by their dependencies, making sure that parent nodes come before their children.&lt;/p&gt;
&lt;p&gt;In this article, you learned:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What a topological sort is and what it's useful for&lt;/li&gt;
&lt;li&gt;How to use &lt;code&gt;graphlib.TopologicalSorter&lt;/code&gt; in the Python standard library&lt;/li&gt;
&lt;li&gt;How to deal with cycles in graphs when topologically sorting them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All the code in this article is available in a &lt;a href="https://gist.github.com/notexactlyawe/606734bcffdaa7d0c091dfbe55f09baa"&gt;GitHub Gist&lt;/a&gt;.&lt;/p&gt;</content><category term="Tutorials"></category><category term="Python"></category><category term="Graph theory"></category></entry><entry><title>abracadabra: How does Shazam work?</title><link href="https://www.cameronmacleod.com/blog/how-does-shazam-work" rel="alternate"></link><published>2022-02-19T00:00:00+00:00</published><updated>2022-02-19T00:00:00+00:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2022-02-19:/blog/how-does-shazam-work</id><summary type="html">&lt;p&gt;&lt;img alt="" src="/images/abracadabra/header.png"&gt;&lt;/p&gt;
&lt;p&gt;Your phone's ability to identify any song it listens to is pure technological magic. In this article, I'll show you how one of the most popular apps, &lt;a href="https://www.shazam.com"&gt;Shazam&lt;/a&gt;, does it. The founders of Shazam released &lt;a href="https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf"&gt;a paper&lt;/a&gt; in 2003 documenting how it works, and I have been working on an …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="" src="/images/abracadabra/header.png"&gt;&lt;/p&gt;
&lt;p&gt;Your phone's ability to identify any song it listens to is pure technological magic. In this article, I'll show you how one of the most popular apps, &lt;a href="https://www.shazam.com"&gt;Shazam&lt;/a&gt;, does it. The founders of Shazam released &lt;a href="https://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf"&gt;a paper&lt;/a&gt; in 2003 documenting how it works, and I have been working on an implementation of that paper, &lt;a href="https://github.com/notexactlyawe/abracadabra"&gt;abracadabra&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Where the paper doesn't explain something, I will fill in the gaps with how abracadabra approaches it. I've also included links to the corresponding part of the abracadabra codebase in relevant sections so you can follow along in Python if you prefer.&lt;/p&gt;
&lt;p&gt;The state of the art has moved on since this paper, and Shazam has probably evolved its algorithm. However, the core principles of audio identification systems haven't changed, and the accuracy you can obtain using the original Shazam method is impressive.&lt;/p&gt;
&lt;p&gt;To get the most out of this article, you should understand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dobrian.github.io/cmp/topics/physics-of-sound/1.frequency-and-pitch.html"&gt;Frequency and pitch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pudding.cool/2018/02/waveforms/"&gt;Waves&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twinkl.co.uk/teaching-wiki/axes"&gt;Graphs and axes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Quick links&lt;/strong&gt;&lt;/p&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#what-is-shazam"&gt;What is Shazam?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#why-is-song-recognition-hard-anyway"&gt;Why is song recognition hard anyway?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#system-overview"&gt;System overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#calculating-a-spectrogram"&gt;Calculating a spectrogram&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-fourier-transform"&gt;The Fourier transform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#spectrograms"&gt;Spectrograms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#fingerprinting"&gt;Fingerprinting&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#why-is-the-fingerprint-based-on-spectrogram-peaks"&gt;Why is the fingerprint based on spectrogram peaks?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#finding-peaks"&gt;Finding peaks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#hashing"&gt;Hashing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#matching"&gt;Matching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#enter-abracadabra"&gt;Enter abracadabra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#further-reading"&gt;Further reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id="what-is-shazam"&gt;What is Shazam?&lt;a class="headerlink" href="#what-is-shazam" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Shazam is an app that identifies songs that are playing around you. You open the app while music is playing, and Shazam will record a few seconds of audio which it uses to search its database. Once it identifies the song that's playing, it will display the result on screen.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/abracadabra/shazam.mp4" class="portrait-video"&gt;Shazam recognising a song&lt;/video&gt;

&lt;p&gt;Before Shazam was an app, it was a phone number. To identify a song, you would ring up the number and hold your phone's microphone to the music. After 30 seconds, Shazam would hang up and then text you details on the song you were listening to. If you were using a mobile phone back in 2002, you'll understand that the quality of phone calls back then made this a challenging task!&lt;/p&gt;
&lt;h2 id="why-is-song-recognition-hard-anyway"&gt;Why is song recognition hard anyway?&lt;a class="headerlink" href="#why-is-song-recognition-hard-anyway" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you haven't done much signal processing before, it may not be obvious why this is a difficult problem to solve. To help give you an idea, take a look at the following audio:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Chris Cornell's &amp;quot;Like a Stone&amp;quot; waveform" src="/images/abracadabra/likeastone.png"&gt;&lt;/p&gt;
&lt;p&gt;The above graph shows what &lt;a href="https://www.youtube.com/watch?v=pom_tO2-5s8"&gt;Chris Cornell's "Like a Stone"&lt;/a&gt; looks like when stored in a computer. Now take a look at the following section of the track:&lt;/p&gt;
&lt;p&gt;&lt;img class="small-image" alt="Section from Like a Stone" src="/images/abracadabra/likeastonesection.png" /&gt;&lt;/p&gt;
&lt;p&gt;If you wanted to tell whether this section of audio came from the track above, you could use a brute-force method. For example, you could slide the section of audio along the track and see if it matches at any point:&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/abracadabra/slidingtrack.mp4"&gt;Matching a section of track by sliding it&lt;/video&gt;

&lt;p&gt;This would be a bit slow, but it would work. Now imagine that you didn't know which track this audio came from, and you had a database of 10 million songs to search. This would take a lot longer!&lt;/p&gt;
&lt;p&gt;What's worse, when you move from this toy example to samples that are recorded through a microphone you introduce background noise, frequency effects, amplitude changes and more. All of these can change the shape of the audio significantly. The sliding method just doesn't work that well for this problem.&lt;/p&gt;
&lt;p&gt;Thankfully, Shazam's approach is a lot smarter than that. In the next section, you'll see the high-level overview of how this works.&lt;/p&gt;
&lt;h2 id="system-overview"&gt;System overview&lt;a class="headerlink" href="#system-overview" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If Shazam doesn't take the sliding approach we described above, what does it do? Take a look at the following high-level diagram:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram showing high-level overview of Shazam design" src="/images/abracadabra/shazam_overview.png"&gt;&lt;/p&gt;
&lt;p&gt;The first thing you will notice is that the diagram is split up into &lt;em&gt;register&lt;/em&gt; and &lt;em&gt;recognise&lt;/em&gt; flows. The &lt;em&gt;register&lt;/em&gt; flow remembers a song to enable it to be recognised in the future. The &lt;em&gt;recognise&lt;/em&gt; flow identifies a short section of audio.&lt;/p&gt;
&lt;p&gt;Registering a song and identifying some audio share a lot of commonality. The following sections will go into more detail, but both flows have the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Calculate the &lt;strong&gt;spectrogram&lt;/strong&gt; of the song/audio. This is a graph of frequency against time. We'll talk more about spectrograms later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find peaks&lt;/strong&gt; in that spectrogram. These represent the loudest frequencies in the audio and will help us build a fingerprint.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hash&lt;/strong&gt; these peaks. In short, this means pairing peaks up to make a better fingerprint.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After calculating these hashes, the &lt;em&gt;register&lt;/em&gt; flow will store them in the database. The &lt;em&gt;recognise&lt;/em&gt; flow will compare them to hashes already in the database to identify which song is playing through the &lt;strong&gt;matching&lt;/strong&gt; step.&lt;/p&gt;
&lt;p&gt;In the next few sections, you'll learn more about each of these steps.&lt;/p&gt;
&lt;h2 id="calculating-a-spectrogram"&gt;Calculating a spectrogram&lt;a class="headerlink" href="#calculating-a-spectrogram" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step for both flows is to obtain a spectrogram of the audio being registered or recognised. To understand spectrograms, you first have to understand Fourier transforms.&lt;/p&gt;
&lt;h3 id="the-fourier-transform"&gt;The Fourier transform&lt;a class="headerlink" href="#the-fourier-transform" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A &lt;a href="https://realpython.com/python-scipy-fft/"&gt;Fourier transform&lt;/a&gt; takes some audio and tells you which frequencies are present in that audio. For example, if you took a 20 Hertz sine wave and used the Fourier transform on it, you would see a big spike around 20 Hertz (Hz):&lt;/p&gt;
&lt;p&gt;&lt;img alt="20Hz sine wave and its Fourier transform" src="/images/abracadabra/fouriertransform20hz.png"&gt;&lt;/p&gt;
&lt;p&gt;In the above image, you can see a large spike around 20Hz and nothing at other frequencies. Sine waves are often called &lt;strong&gt;pure tones&lt;/strong&gt; because of this property, since they only contain a single frequency.&lt;/p&gt;
&lt;p&gt;The result of a Fourier transform is called a &lt;strong&gt;frequency spectrum&lt;/strong&gt;. We say that when you take the Fourier transform of a signal, you move it from the &lt;strong&gt;time domain&lt;/strong&gt; into the &lt;strong&gt;frequency domain&lt;/strong&gt;. These are fancy terms for describing whether time or frequency is along the bottom of a graph. In mathematical terms, the domain is more or less the X-axis of a graph.&lt;/p&gt;
&lt;p&gt;The Y-axis of the frequency spectrum represents the strength of each frequency component. If a frequency component is stronger, then it will be more audible in the time-domain signal.&lt;/p&gt;
&lt;p&gt;If you were to add a 50Hz sine wave at half the strength to that 20Hz sine wave, the resulting frequency spectrum would show a spike at 20Hz and a smaller spike at 50Hz:&lt;/p&gt;
&lt;p&gt;&lt;img alt="20Hz sine wave plus 50Hz sine wave and its Fourier transform" src="/images/abracadabra/fouriertransform20plus50.png"&gt;&lt;/p&gt;
&lt;p&gt;As you can see, adding multiple audio waves together combines the frequencies present in them. In fact, all audio signals can be reconstructed from waves like this. For more, take a look at 3Blue1Brown's &lt;a href="https://www.youtube.com/watch?v=spUNpyF58BY"&gt;video on the Fourier transform&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One great property of the frequency domain is that it often helps us to see things that aren't clear in the time domain. For example, if you take the signal with two frequencies from before and add noise to it, in the time domain it looks visually very different. However, in the frequency domain, the two spikes are still very clear:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Fourier transform of a noisy signal" src="/images/abracadabra/noisyfouriertransform.png"&gt;&lt;/p&gt;
&lt;p&gt;In the frequency domain graph on the right, you can still clearly see the spikes for the main component frequencies. It would be harder in the time domain to see what frequency sine waves went into the signal.&lt;/p&gt;
&lt;p&gt;Up until now, our examples have only contained one or two frequencies, but what happens if you put a more complex signal through the Fourier transform? Let's take a look at our section of audio from Like a Stone:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Fourier transform of a Like a Stone sample" src="/images/abracadabra/fouriertransformsample.png"&gt;&lt;/p&gt;
&lt;p&gt;Real audio files like the one above contain lots of different frequencies. This is a good thing, as it means that the frequencies present can uniquely identify songs.&lt;/p&gt;
&lt;h3 id="spectrograms"&gt;Spectrograms&lt;a class="headerlink" href="#spectrograms" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/notexactlyawe/abracadabra/blob/e0eb59a944d7c9999ff8a4bc53f5cfdeb07b39aa/abracadabra/fingerprint.py#L9"&gt;abracadabra implementation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you run a Fourier transform over an entire song, then you will see the strength of the frequencies present over the whole song. However, the frequencies that are present change over time. To better represent the frequencies changing over time, we need to split the song into small sections before taking the Fourier transform. This is called taking a spectrogram.&lt;/p&gt;
&lt;p&gt;Here's a simplified animation of how spectrograms work:&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/abracadabra/spectrogram.mp4"&gt;Explanation of the spectrogram process&lt;/video&gt;

&lt;p&gt;In the above animation, you can see that the song is first split into small sections. Next, we use the Fourier transform to calculate the frequency spectrum of each of these sections. When you put all these frequency spectrums together, you get a spectrogram.&lt;/p&gt;
&lt;p&gt;To make this concrete, let's take a look at the spectrogram of Like a Stone:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Spectrogram of Like a Stone" src="/images/abracadabra/spectrogram.png"&gt;&lt;/p&gt;
&lt;p&gt;Even though the spectrogram looks 2-dimensional, it's actually a 3D graph with the following axes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Time (X-axis)&lt;/li&gt;
&lt;li&gt;Frequency (Y-axis)&lt;/li&gt;
&lt;li&gt;Strength (Z-axis/colour)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Z-axis is represented by colour in the spectrogram above. Bright green shows a high magnitude for a particular frequency component and dark blue shows a low magnitude.&lt;/p&gt;
&lt;p&gt;Looking at the spectrogram above, you can see that the brightest spots (strongest frequencies) almost exclusively occur below 5000Hz. This is quite common with music, for example most pianos have a &lt;a href="https://en.wikipedia.org/wiki/Piano_key_frequencies"&gt;frequency range&lt;/a&gt; of 27Hz-4186Hz.&lt;/p&gt;
&lt;p&gt;The frequencies present in a track contain a lot of identifying information, and calculating the spectrogram allows us access to that information. In the next section, you'll learn how we turn all that information into a unique fingerprint for the track.&lt;/p&gt;
&lt;h2 id="fingerprinting"&gt;Fingerprinting&lt;a class="headerlink" href="#fingerprinting" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Just as a fingerprint uniquely identifies a person, we can extract a unique fingerprint for some audio from its spectrogram.&lt;/p&gt;
&lt;p&gt;These audio fingerprints rely on finding peaks in the spectrogram. These peaks are the loudest frequencies at some time in the song. Because they are loud, it's likely they'll survive when subjected to noise or other distortions.&lt;/p&gt;
&lt;p&gt;In the next section, you'll read some more about the motivation behind using spectrogram peaks to build fingerprints.&lt;/p&gt;
&lt;h3 id="why-is-the-fingerprint-based-on-spectrogram-peaks"&gt;Why is the fingerprint based on spectrogram peaks?&lt;a class="headerlink" href="#why-is-the-fingerprint-based-on-spectrogram-peaks" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A spectrogram peak is a frequency that is loud at some point in an audio signal. You can recognise these on a spectrogram since they will be the brightest points.&lt;/p&gt;
&lt;p&gt;In music, these would represent the loudest notes. For example, during a guitar solo, the notes that the guitar is playing might become spectrogram peaks since they would likely be the loudest notes at that time.&lt;/p&gt;
&lt;p&gt;A spectrogram peak is the point least likely to be affected by noise. Noise has to be louder than the spectrogram peak to make it unrecognisable and the spectrogram peaks are the loudest frequency components in the track.&lt;/p&gt;
&lt;p&gt;To make this visual, take a look at our earlier example of a Fourier transformed signal that had noise added to it. When noise is added, the frequency peaks still retain their rough shape.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Fourier transform of a noisy signal" src="/images/abracadabra/noisyfouriertransform.png"&gt;&lt;/p&gt;
&lt;p&gt;Another advantage of using spectrogram peaks to fingerprint audio is that they cut down the amount of data we have to store. Storing only the loudest frequency components means we don't have to store everything else. This speeds up searching fingerprints since there is less data to look through.&lt;/p&gt;
&lt;p&gt;Before we can use frequency peaks in our fingerprint though, we have to find them. In the next section, you'll learn how.&lt;/p&gt;
&lt;h3 id="finding-peaks"&gt;Finding peaks&lt;a class="headerlink" href="#finding-peaks" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/notexactlyawe/abracadabra/blob/e0eb59a944d7c9999ff8a4bc53f5cfdeb07b39aa/abracadabra/fingerprint.py#L31"&gt;abracadabra implementation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As discussed in the previous section, the peaks of a spectrogram represent the strongest frequencies in a signal. For frequency peaks to be usable in an audio fingerprint, it's important that they are evenly spaced through the spectrogram.&lt;/p&gt;
&lt;p&gt;It's important the peaks are evenly spaced in &lt;strong&gt;time&lt;/strong&gt;, so the system can recognise any section of the song. For example, if all the peaks were at the start of the song, then the fingerprint wouldn't cover later sections:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Peaks clustered in time" src="/images/abracadabra/peak_cluster_time.png"&gt;&lt;/p&gt;
&lt;p&gt;In the image above, all the peaks (white crosses) are clustered at the start of the song. This means that the system can't recognise any sample from the rest of the song.&lt;/p&gt;
&lt;p&gt;It's also important that the peaks are evenly spaced in &lt;strong&gt;frequency&lt;/strong&gt;, so the system can deal with noise and frequency distortion. Sometimes noise will be very loud and concentrated at a specific frequency range, for example a car horn in the background:&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/abracadabra/peak_cluster_freq.mp4"&gt;Peaks clustered in a frequency band affected by noise&lt;/video&gt;

&lt;p&gt;In the above animation, the peaks are well-spaced in time, but are clustered into a small frequency band. When a loud noise is introduced, for example a car horn, it can make an entire section of song unrecognisable by changing which peaks are selected.&lt;/p&gt;
&lt;p&gt;To find spectrogram peaks while keeping them well-spaced, we can borrow a technique from image processing known as a maximum filter. The process looks something like the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the maximum filter to highlight peaks in the spectrogram.&lt;/li&gt;
&lt;li&gt;Locate the highlighted peaks by comparing to our original spectrogram.&lt;/li&gt;
&lt;li&gt;(Optional) Discard some peaks.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's run through these steps one-by-one. First, let's take a look at how the maximum filter works:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Maximum filter&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A maximum filter emphasises the peaks in an image. It does this by looking in a neighbourhood around each pixel for the maximum value and then setting the pixel to that local maximum. The following animation shows a maximum filter that looks at a 3x3 neighbourhood around each pixel:&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/abracadabra/maximumfilter.mp4"&gt;Animation of a maximum filter on a simple image&lt;/video&gt;

&lt;p&gt;As you can see in the above animation, the maximum filter takes each pixel of an image in turn and finds the maximum in a region surrounding it. The filtered pixel is then set to that local maximum. This has the effect of expanding each local peak to its surrounding area.&lt;/p&gt;
&lt;p&gt;Running a maximum filter on Like a Stone's spectrogram gives the following result:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Spectrogram and maximum-filtered spectrogram of Like a Stone" src="/images/abracadabra/maxfilteredspectrogram.png"&gt;&lt;/p&gt;
&lt;p&gt;The maximum-filtered spectrogram looks like a lower-resolution version of the original spectrogram. This is because the peaks in the signal have expanded and taken over the other pixels. Each box with the same colour in the filtered image corresponds to a local peak in the original image.&lt;/p&gt;
&lt;p&gt;The maximum filter has a parameter that controls the size of the box to use when finding the local maxima. When you set this parameter to make a smaller box, you end up getting more peaks. Similarly, by setting this parameter larger you get fewer peaks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Recover original peaks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The maximum filter doesn't do all the work for us. The filter has emphasised the local peaks, but it hasn't found their locations. To find the peak locations, we need to find the points that have equal values in the original spectrogram and the filtered spectrogram.&lt;/p&gt;
&lt;p&gt;The idea behind this trick is that all the non-peak points in the spectrogram have been replaced by their local peaks, so their values have changed. The only points whose values haven't changed are the peaks.&lt;/p&gt;
&lt;p&gt;Below is a zoomed in section of the spectrogram above. The points where the values are equal in the filtered and original spectrograms are highlighted:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Zoomed section of spectrogram showing equal values in the maximum-filtered and original spectrograms" src="/images/abracadabra/zoomed_spectrogram.png"&gt;&lt;/p&gt;
&lt;p&gt;As you can see in the images above, the highlighted points where the two spectrograms are equal correspond to the local peaks of that part of the image.&lt;/p&gt;
&lt;p&gt;Plotting all of the peaks together produces something called a &lt;strong&gt;constellation map&lt;/strong&gt;. Here's the constellation map for Like a Stone:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Constellation map of Like a Stone" src="/images/abracadabra/constellationmap.png"&gt;&lt;/p&gt;
&lt;p&gt;These graphs are called constellation maps since they look a bit like an image of the night sky. Who said computer science couldn't be romantic?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: (Optional) Discard peaks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once we have a constellation map of peaks, the next step is to potentially discard some. The size of our fingerprint is dependent on the number of peaks that we use in it. Keeping fingerprints small matters when you are storing millions of songs in your database.&lt;/p&gt;
&lt;p&gt;However, by reducing the number of peaks we use, we reduce the accuracy of our system. Fewer peaks in a fingerprint mean fewer chances to match a sample to the correct song.&lt;/p&gt;
&lt;p&gt;There are a couple of options for reducing the number of peaks in our fingerprint:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take the top N peaks. N should be proportional to the length of audio that you are fingerprinting to avoid over-representing shorter songs.&lt;/li&gt;
&lt;li&gt;Take all peaks above a certain threshold. This doesn't guarantee you a certain fingerprint size per time like the other method, but may give more accurate results.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;We have almost finished constructing our fingerprint, the next step is to produce a set of hashes from our peaks.&lt;/p&gt;
&lt;h3 id="hashing"&gt;Hashing&lt;a class="headerlink" href="#hashing" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/notexactlyawe/abracadabra/blob/e0eb59a944d7c9999ff8a4bc53f5cfdeb07b39aa/abracadabra/fingerprint.py#L96"&gt;abracadabra implementation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To motivate hashing, imagine that our fingerprint was just a collection of spectrogram peaks. Each peak's frequency would be represented by a certain number of bits, for example 10. With 10 bits of information, we can represent 2^10=1024 individual frequencies. With thousands of these points per track, we quickly get a lot of repeats.&lt;/p&gt;
&lt;p&gt;Uniqueness is important for a fingerprint, since it makes searching a lot faster and helps to recognise more songs. Shazam's solution to the problem of uniqueness is to create hashes from pairs of peaks:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of two spectrogram peaks forming a hash" src="/images/abracadabra/hash_diagram.png"&gt;&lt;/p&gt;
&lt;p&gt;The diagram above shows a zoomed in portion of a spectrogram. Each circle represents a peak and the dashed line box represents a hash. You can see that a hash is formed of two peaks. The information that is recorded for each hash is the frequency of each peak, f&lt;sub&gt;A&lt;/sub&gt; and f&lt;sub&gt;B&lt;/sub&gt;, and the time delta between them, 𝚫T.&lt;/p&gt;
&lt;p&gt;The advantage of pairing points up is that two paired points are much more unique than a single point. Looking at it mathematically, if each point has 10 bits of frequency information, and the time delta between the two points could be represented by 10 bits, then you have 30 bits of information. 2^30=1073741824 which is &lt;strong&gt;significantly&lt;/strong&gt; larger than 1024 possibilities for a single point.&lt;/p&gt;
&lt;p&gt;Shazam creates pairs using the following algorithm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick a point. This will be called the anchor point.&lt;/li&gt;
&lt;li&gt;Calculate a target zone of the spectrogram for the anchor point.&lt;/li&gt;
&lt;li&gt;For every point in the target zone, create a pair with the anchor point.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can see this algorithm illustrated in the following animation:&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/abracadabra/pairing.mp4"&gt;Animation of pairing points&lt;/video&gt;

&lt;p&gt;Choosing a target zone isn't described in the Shazam paper, but the images the paper contains show it as starting slightly ahead of time of the anchor point and being centred on the anchor point's frequency.&lt;/p&gt;
&lt;p&gt;Once a pair has been created, it is stored as a hash in the database with the following information:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colspan="3"&gt;&lt;/th&gt;
      &lt;th colspan="2"&gt;Other information&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tr&gt;
    &lt;td&gt;Point A freq (f&lt;sub&gt;A&lt;/sub&gt;)&lt;/th&gt;
    &lt;td&gt;Point B freq (f&lt;sub&gt;B&lt;/sub&gt;)&lt;/th&gt;
    &lt;td&gt;Time delta (𝚫T)&lt;/th&gt;
    &lt;td&gt;Point A time&lt;/th&gt;
    &lt;td&gt;Track ID&lt;/th&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;The first three columns (f&lt;sub&gt;A&lt;/sub&gt;, f&lt;sub&gt;B&lt;/sub&gt; and 𝚫T) make up the hash. The "Other information" is used to locate the hash at a specific time in a song. This will be used in matching later.&lt;/p&gt;
&lt;p&gt;All of the hashes for a particular track make up the fingerprint. In the next section, you'll read about how Shazam matches these fingerprints.&lt;/p&gt;
&lt;h2 id="matching"&gt;Matching&lt;a class="headerlink" href="#matching" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Given a collection of fingerprints in a database, how does Shazam figure out which one a given audio sample matches? This is where the matching part of the system comes in. Recall the system diagram from earlier:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram showing high-level overview of Shazam design" src="/images/abracadabra/shazam_overview.png"&gt;&lt;/p&gt;
&lt;p&gt;The recognise and register flows both generate fingerprints. The difference lies in what they do with them. While the register flow stores fingerprints away for future matching, the recognise flow has to match its fingerprint with what is already in the database.&lt;/p&gt;
&lt;p&gt;The matching algorithm contains the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Retrieve all hashes from the database that match the sample's fingerprint.&lt;/li&gt;
&lt;li&gt;Group these hashes by song.&lt;/li&gt;
&lt;li&gt;For each song, figure out if the hashes line up.&lt;/li&gt;
&lt;li&gt;Choose the track with the most lined up hashes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We'll look at each of these steps in turn.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Retrieve matching hashes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/notexactlyawe/abracadabra/blob/e0eb59a944d7c9999ff8a4bc53f5cfdeb07b39aa/abracadabra/storage.py#L75"&gt;abracadabra implementation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first step is to find every hash in the database that matches a hash in the fingerprint we just created. Even though a hash is a 3-tuple of &lt;em&gt;(f&lt;sub&gt;A&lt;/sub&gt;, f&lt;sub&gt;B&lt;/sub&gt;, 𝚫T)&lt;/em&gt;, abracadabra stores this as &lt;em&gt;hash(f&lt;sub&gt;A&lt;/sub&gt;, f&lt;sub&gt;B&lt;/sub&gt;, 𝚫T)&lt;/em&gt; where &lt;code&gt;hash()&lt;/code&gt; is a &lt;a href="https://en.wikipedia.org/wiki/Hash_function"&gt;hash function&lt;/a&gt; that returns a single value. This way you only have to search for a single value per hash instead of three.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Group hashes by song&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Recall the format of an individual hash in the database:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colspan="3"&gt;&lt;/th&gt;
      &lt;th colspan="2"&gt;Other information&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tr&gt;
    &lt;td&gt;Point A freq (f&lt;sub&gt;A&lt;/sub&gt;)&lt;/th&gt;
    &lt;td&gt;Point B freq (f&lt;sub&gt;B&lt;/sub&gt;)&lt;/th&gt;
    &lt;td&gt;Time delta (𝚫T)&lt;/th&gt;
    &lt;td&gt;Point A time&lt;/th&gt;
    &lt;td&gt;Track ID&lt;/th&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Thanks to the track ID that we associated with each hash, we can group the hashes by track. This allows us to score each potentially matching track.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Figure out if hashes line up&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/notexactlyawe/abracadabra/blob/e0eb59a944d7c9999ff8a4bc53f5cfdeb07b39aa/abracadabra/recognise.py#L80"&gt;abracadabra implementation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If a sample matches a song, then the hashes present in that sample should line up nicely with the hashes in some section of that song. The diagram below illustrates what this would look like:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of sample hashes lining up to song hashes" src="/images/abracadabra/lineduphashes.png"&gt;&lt;/p&gt;
&lt;p&gt;In the above diagram, a sample has been lined up with the section of the original song that it came from. The blue points represent the anchor points of the hashes.&lt;/p&gt;
&lt;p&gt;While the above diagram shows the perfect scenario, there is a chance that the matching hashes from the database don't line up perfectly. For example, noise could have introduced peaks in the sample that resemble peaks at a different point in the song. This can lead to the following scenario:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of sample hashes imperfectly lining up with song hashes" src="/images/abracadabra/badlylineduphashes.png"&gt;&lt;/p&gt;
&lt;p&gt;In the above diagram, the red circles represent hashes that match to points in the song outside the section the sample came from. In this situation, it's harder to see that the sample is a perfect match for the song.&lt;/p&gt;
&lt;p&gt;What's worse, sometimes hashes can match to the wrong song! This is where checking that the hashes line up comes in.&lt;/p&gt;
&lt;p&gt;To explain how we can check whether the hashes line up in code, let's look at an example. Let's imagine that we've got a list of matching hashes from the database and grouped them by track. For a given track, we can then check the time that the hash occurs in the original track against the time that the hash occurs in the sample.&lt;/p&gt;
&lt;style&gt;
.highlight-row&gt;td {
  background-color: #B3FCB3 !important;
}
&lt;/style&gt;
&lt;table&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Sample time&lt;/th&gt;
    &lt;th&gt;Track time&lt;/th&gt;
    &lt;th&gt;Track time - Sample time&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
  &lt;tr class="highlight-row"&gt;
    &lt;td&gt;3&lt;/td&gt;
    &lt;td&gt;13&lt;/td&gt;
    &lt;td&gt;&lt;b&gt;10&lt;/b&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="highlight-row"&gt;
    &lt;td&gt;4&lt;/td&gt;
    &lt;td&gt;14&lt;/td&gt;
    &lt;td&gt;&lt;b&gt;10&lt;/b&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;7&lt;/td&gt;
    &lt;td&gt;20&lt;/td&gt;
    &lt;td&gt;13&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="highlight-row"&gt;
    &lt;td&gt;5&lt;/td&gt;
    &lt;td&gt;15&lt;/td&gt;
    &lt;td&gt;&lt;b&gt;10&lt;/b&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;6&lt;/td&gt;
    &lt;td&gt;12&lt;/td&gt;
    &lt;td&gt;6&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="highlight-row"&gt;
    &lt;td&gt;1&lt;/td&gt;
    &lt;td&gt;11&lt;/td&gt;
    &lt;td&gt;&lt;b&gt;10&lt;/b&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;In the above table, you can see that all the matches with a &lt;em&gt;Track time - Sample time&lt;/em&gt; of 10 have been highlighted. These are the true matches, while the other two rows are false matches. To see this is the case, let's look at a similar diagram to the ones we saw before:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of sample hashes lining up to song hashes annotated with times" src="/images/abracadabra/lineduphashes2.png"&gt;&lt;/p&gt;
&lt;p&gt;The above diagram contains the same hashes from the previous table. As you can see, the true matches have a &lt;em&gt;Track time - Sample time&lt;/em&gt; that is equal to how far into the track time that the sample starts.&lt;/p&gt;
&lt;p&gt;To see how we turn this into a score for the track, let's make this data into a histogram. A histogram is a fancy name for a bar chart. We're going to plot each &lt;em&gt;Track time - Sample time&lt;/em&gt; against the number of times it occurs:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Histogram showing frequency of 'Track time - Sample time'" src="/images/abracadabra/sample_histogram.png"&gt;&lt;/p&gt;
&lt;p&gt;Each bar in the histogram above is referred to as a &lt;strong&gt;bin&lt;/strong&gt;. To score a song on how good a match it is for an audio sample, we just need to take the largest bin. Songs that aren't good matches will have low values in all bins, whereas a song that's a good match will have a large spike in one of the bins.&lt;/p&gt;
&lt;p&gt;This way we can compare a sample to all the songs with matching hashes in our database and score each of them. The song with the highest score is likely to be the correct result.&lt;/p&gt;
&lt;p&gt;You might wonder why we don't just go for the song that matches the largest number of hashes as it would be much simpler to implement. The problem with this approach is that not all songs are the same length. Longer songs are likely to get more matches than shorter songs and when some Spotify tracks are &lt;a href="https://www.reddit.com/r/spotify/comments/9i2ps6/longest_song_on_spotify/"&gt;over 4 hours long&lt;/a&gt; this can really bias your results!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="headerlink" href="#conclusion" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Well done for making it this far, that was a long journey! Over the course of this article, you've learned how Shazam extracts fingerprints from audio, and how it matches these fingerprints to those that it has already registered in its database.&lt;/p&gt;
&lt;p&gt;To summarise, Shazam does the following to &lt;strong&gt;register&lt;/strong&gt; a song:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Calculates a &lt;strong&gt;spectrogram&lt;/strong&gt; of a song&lt;/li&gt;
&lt;li&gt;Extracts &lt;strong&gt;peaks&lt;/strong&gt; from that spectrogram&lt;/li&gt;
&lt;li&gt;Pairs those peaks up into &lt;strong&gt;hashes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Stores the collection of hashes for a song as a &lt;strong&gt;fingerprint&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Shazam does the following to &lt;strong&gt;recognise&lt;/strong&gt; an audio sample:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Calculates a &lt;strong&gt;fingerprint&lt;/strong&gt; of the audio sample&lt;/li&gt;
&lt;li&gt;Finds the &lt;strong&gt;hashes&lt;/strong&gt; that match that fingerprint in the database&lt;/li&gt;
&lt;li&gt;For each potential song match:&lt;ul&gt;
&lt;li&gt;Calculate &lt;strong&gt;Track time - Sample time&lt;/strong&gt; for each matching hash&lt;/li&gt;
&lt;li&gt;Group those values into a &lt;strong&gt;histogram&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Take the largest bin in this histogram as the &lt;strong&gt;score&lt;/strong&gt; for the song&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Return the song with the highest score&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="enter-abracadabra"&gt;Enter abracadabra&lt;a class="headerlink" href="#enter-abracadabra" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I learned everything written here over the process of writing &lt;a href="https://github.com/notexactlyawe/abracadabra"&gt;abracadabra&lt;/a&gt;, my implementation of this paper.&lt;/p&gt;
&lt;p&gt;If you are interested in seeing what this might look like in code, please take a look! Everything is open source and I've done my best to document the project. abracadabra can also be used as a library in other projects, so please feel free to re-mix and build something cool. If you do use it, I'd love to &lt;a href="/about"&gt;hear about it&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further reading&lt;a class="headerlink" href="#further-reading" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to find out more about anything mentioned in this article, take a look below. I've also scattered some helpful links throughout the page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://abracadabra.readthedocs.io/en/latest/"&gt;abracadabra docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/worldveil/dejavu"&gt;dejavu&lt;/a&gt; is another implementation of a song recogniser in Python. The author wrote a &lt;a href="https://willdrevo.com/fingerprinting-and-audio-recognition-with-python/"&gt;wonderful explanation&lt;/a&gt; on how it works.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dhoiem.cs.illinois.edu/publications/cvpr2005-mr.pdf"&gt;Computer Vision for Music Identification&lt;/a&gt; is another approach to song recognition that is similar to how dejavu works.&lt;/li&gt;
&lt;li&gt;An algorithm that takes a slightly different approach is &lt;a href="https://acoustid.org/chromaprint"&gt;Chromaprint&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.musicbrainz.org/Fingerprinting"&gt;Musicbrainz&lt;/a&gt; is an open-source encyclopedia of music information. This page explains how they fingerprint audio.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://aubio.org/news/20091111-2339_shazam"&gt;Playing with Shazam fingerprints&lt;/a&gt; is an article from 2009 about the author's experience implementing the Shazam algorithm.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://static1.squarespace.com/static/53f7940ae4b05c506d396373/t/5669c81ba2bab86d89ef3dec/1449773083824/Koh_30x40.pdf"&gt;Alignment of videos of same event using audio fingerprinting&lt;/a&gt; is an example of a use case for this algorithm that goes beyond music.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Tutorials"></category><category term="abracadabra"></category><category term="Audio"></category><category term="Python"></category></entry><entry><title>What a computer science degree looks like in 2020</title><link href="https://www.cameronmacleod.com/blog/cs-degree-structure" rel="alternate"></link><published>2020-05-25T00:00:00+01:00</published><updated>2020-05-25T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2020-05-25:/blog/cs-degree-structure</id><summary type="html">&lt;p&gt;Lots of people who are learning to code ask the question "Should I get a CS degree or do a bootcamp?", or "Is a degree necessary?". I have just finished my degree (not graduated yet), and while I can't give all the answers to these questions, I can share my …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Lots of people who are learning to code ask the question "Should I get a CS degree or do a bootcamp?", or "Is a degree necessary?". I have just finished my degree (not graduated yet), and while I can't give all the answers to these questions, I can share my experience.&lt;/p&gt;
&lt;p&gt;In this post I will go into detail about what my degree contained. You can use this information to decide whether university is for you, make your own curriculum, or even just see how the syllabus has changed since you went.&lt;/p&gt;
&lt;p&gt;I don't want to tell you to get a degree, and I equally don't want to tell you to not get one. It is a personal choice. However, on the whole I'm very pleased that I went to university. I learned a lot of things that I wouldn't have otherwise, but more importantly, I met lots of great people and had experiences that I couldn't have had in any other setting. The piece of paper that I will get once I graduate is important for the &lt;a href="/blog/why-pm-over-software"&gt;job I chose&lt;/a&gt;, but it won't be important for everyone on my course.&lt;/p&gt;
&lt;p&gt;Below you will find a list of every module I took in the course of my degree and their content. Not every degree will look like this, I had a lot of elective modules and some universities are more practical than others, but this should give you an idea of what university can offer.&lt;/p&gt;
&lt;p&gt;The courses are broken down by year and semester. Each course has the following structure.&lt;/p&gt;
&lt;div class="csdegree-course "&gt;
  &lt;summary&gt;Name and link to full course description&lt;/summary&gt;

  Some information about the course, including searchable terms where appropriate and the main textbook used.
  &lt;br /&gt;&lt;br /&gt;
  Number of credits to give you an idea of how much depth each course has.
&lt;/div&gt;

&lt;p&gt;This is a Bachelor of Science (Hons) degree from the University of Edinburgh. It is under the Scottish university system, so it is four years long. Each year is composed of two semesters and you need to take 120 credits of courses across these semesters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This is not a comprehensive guide to the degree program at Edinburgh, if you are looking for that, go to &lt;a href="http://www.drps.ed.ac.uk/"&gt;DRPS (Degree Regulations and Programmes of Study)&lt;/a&gt;. I have tried not to include my opinions of the courses, since it's the syllabus that's important.&lt;/p&gt;
&lt;p&gt;With that out the way, here's the full list.&lt;/p&gt;
&lt;div class="csdegree-year"&gt;
  &lt;h2&gt;Year 1&lt;/h2&gt;
  &lt;p&gt;Year 1 had 40 credits free for optional modules. The number of free credits in first year may be higher in Scotland than elsewhere due to Scottish universities doing a four year bachelors degree.&lt;/p&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 1&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/inf1/cl"&gt;Computation and Logic&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Propositional and first-order logic&lt;/li&gt;
          &lt;li&gt;Finite state machines&lt;/li&gt;
          &lt;li&gt;Set theory&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: &lt;a href="https://myelms.umd.edu/courses/1154654/files/39380916/download?download_frd=1"&gt;Mathematical Methods in Linuguistics&lt;/a&gt;&lt;/p&gt;

        10 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/inf1/fp/"&gt;Functional Programming&lt;/a&gt;&lt;/summary&gt;
        &lt;p&gt;Introductory course on Haskell. Gets as far as monads, but doesn't really address them that thoroughly.&lt;/p&gt;

        &lt;p&gt;Textbook: &lt;a href="http://learnyouahaskell.com/"&gt;Learn you a Haskell for Great Good!&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;10 credits&lt;/p&gt;
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/16-17/dpt/cxmath08057.htm"&gt;Introduction to Linear Algebra&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Complex numbers&lt;/li&gt;
          &lt;li&gt;Vectors&lt;/li&gt;
          &lt;li&gt;Systems of linear equations&lt;/li&gt;
          &lt;li&gt;Matrices&lt;/li&gt;
          &lt;li&gt;Eigenvalues/eigenvectors&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: 'Linear Algebra, A Modern Introduction' by David Poole&lt;/p&gt;

        20 credits
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 2&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/16-17/dpt/cxinfr08014.htm"&gt;Object-oriented Programming&lt;/a&gt;&lt;/summary&gt;
        &lt;p&gt;Honestly, I didn't really pay attention to this course. It taught introductory Java.&lt;/p&gt;

        &lt;p&gt;Textbook: 'The Java Tutorial: A Short Course on the Basics' - Raymond Gallardo et al.&lt;/p&gt;

        &lt;p&gt;10 credits&lt;/p&gt;
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="https://blog.inf.ed.ac.uk/da18/"&gt;Data and Analysis&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;SQL/Basic databases&lt;/li&gt;
          &lt;li&gt;XML&lt;/li&gt;
          &lt;li&gt;Text corpora&lt;/li&gt;

          &lt;p&gt;Textbook: Unknown. The lecturer handed out notes for this course&lt;/p&gt;
        &lt;/ul&gt;

         10 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/16-17/dpt/cxmath08058.htm"&gt;Calculus and its Applications&lt;/a&gt;&lt;/summary&gt;
        &lt;p&gt;Introductory calculus course. It covered single variable integration, differentiation and series.&lt;/p&gt;

        &lt;p&gt;Textbook: 'Essential Calculus: Early Transcendentals' - James Stewart&lt;/p&gt;

        &lt;p&gt;20 credits&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Full Year&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course optional full-year"&gt;&lt;summary&gt;Russian Studies (optional)&lt;/summary&gt;
        &lt;p&gt;Russian course for beginners. One of the reasons that I went to university in Scotland was so that I could do languages alongside my degree. Scotland has more of a "liberal arts" system than the rest of the UK.&lt;/p&gt;

        &lt;p&gt;&lt;b&gt;40 credits&lt;/b&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class="csdegree-year"&gt;
  &lt;h2&gt;Year 2&lt;/h2&gt;
  &lt;p&gt;Year 2 only had 20 credits free. Bayes Theorem was a big theme in year 2.&lt;/p&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 1&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/fnlp/"&gt;Processing Formal and Natural Languages&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Natural language processing&lt;/li&gt;
          &lt;li&gt;Hidden Markov Models&lt;/li&gt;
          &lt;li&gt;Grammars&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Speech and Language Processing, Jurafsky, D and J. H. Martin&lt;/p&gt;

         20 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/inf2c-cs/"&gt;Introduction to Computer Systems&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Instruction sets and assembly&lt;/li&gt;
          &lt;li&gt;C programming&lt;/li&gt;
          &lt;li&gt;Basic processor and digital logic design&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Computer Organisation and Design, D.A. Patterson and J.L. Hennessy&lt;/p&gt;

         10 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/16-17/dpt/cxinfr08019.htm"&gt;Introduction to Software Engineering&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Software lifecycle&lt;/li&gt;
          &lt;li&gt;UML&lt;/li&gt;
          &lt;li&gt;Design patterns&lt;/li&gt;
          &lt;li&gt;Intro to testing&lt;/li&gt;
          &lt;li&gt;Version control&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Software Engineering, Ian Sommerville&lt;/p&gt;

         10 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/16-17/dpt/cxinfr08023.htm"&gt;Discrete Mathematics and Mathematical Reasoning&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Number systems&lt;/li&gt;
          &lt;li&gt;Proofs by induction&lt;/li&gt;
          &lt;li&gt;Counting (combinatorics)&lt;/li&gt;
          &lt;li&gt;Graphs&lt;/li&gt;
          &lt;li&gt;Trees&lt;/li&gt;
          &lt;li&gt;Discrete probabilities&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Discrete Mathematics and its Applications, Kenneth Rosen&lt;/p&gt;

         20 credits
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 2&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/inf2b/"&gt;Algorithms, Data Structures, Learning&lt;/a&gt;&lt;/summary&gt;
      This was actually two courses combined. Firstly the algorithms and data structures component contained:
        &lt;ul&gt;
          &lt;li&gt;Asymptotic notation and algorithms (Big O etc)&lt;/li&gt;
          &lt;li&gt;Data structures&lt;/li&gt;
          &lt;li&gt;Sorting algorithms&lt;/li&gt;
          &lt;li&gt;Graphs&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;Textbook: &lt;a href="http://ww0.java4.datastructures.net/"&gt;Data Structures and Algorithms in Java&lt;/a&gt;, Goodrich, M T and Tamassia&lt;/p&gt;

        The other half of the course was focused on machine learning.
        &lt;ul&gt;
          &lt;li&gt;Dimensionality in data (PCA etc)&lt;/li&gt;
          &lt;li&gt;Naive Bayes&lt;/li&gt;
          &lt;li&gt;Clustering algorithms&lt;/li&gt;
          &lt;li&gt;Gaussians&lt;/li&gt;
          &lt;li&gt;Neural networks&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: &lt;a href="http://www.dcs.gla.ac.uk/~srogers/firstcourseml/"&gt;A First Course in Machine Learning&lt;/a&gt; - second edition, Simon Rogers and Mark Girolami&lt;/p&gt;

         20 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/16-17/dpt/cxmath08067.htm"&gt;Probability with Applications&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Combinatorics&lt;/li&gt;
          &lt;li&gt;Bayes theorem&lt;/li&gt;
          &lt;li&gt;Discrete random variable distributions&lt;/li&gt;
          &lt;li&gt;Continuous random variable distributions&lt;/li&gt;
          &lt;li&gt;Jointly distributed random variables&lt;/li&gt;
          &lt;li&gt;Covariance and correlation&lt;/li&gt;
          &lt;li&gt;Discrete Markov chains&lt;/li&gt;
          &lt;li&gt;Birth and death processes&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: A First Course in Probability, Sheldon Ross&lt;/p&gt;

         20 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/inf2d/"&gt;Reasoning and Agents&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Intelligent agents&lt;/li&gt;
          &lt;li&gt;Problem solving through search&lt;/li&gt;
          &lt;li&gt;Adversarial search&lt;/li&gt;
          &lt;li&gt;Knowledge bases and inference&lt;/li&gt;
          &lt;li&gt;First order logic&lt;/li&gt;
          &lt;li&gt;Planning&lt;/li&gt;
          &lt;li&gt;Bayes Theorem&lt;/li&gt;
          &lt;li&gt;Markov decision processes&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Artificial Intelligence: A Modern Approach, Russell R &amp; Norvig P&lt;/p&gt;

         20 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="http://www-test.drps.ed.ac.uk/17-18/dpt/cxscee08007.htm"&gt;Signals and Communications Systems&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Types of signals (continuous vs discrete, periodic vs aperiodic etc)&lt;/li&gt;
          &lt;li&gt;Power and energy&lt;/li&gt;
          &lt;li&gt;Fourier analysis&lt;/li&gt;
          &lt;li&gt;Convolution&lt;/li&gt;
          &lt;li&gt;Nyquist's sampling theorem&lt;/li&gt;
          &lt;li&gt;Modulation techniques (OOK, FSK, PSK)&lt;/li&gt;
          &lt;li&gt;Multiplexing (FDM and TDM)&lt;/li&gt;
          &lt;li&gt;Information theory&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Digital Signal Processing, John G. Proakis &amp; Dimitris G. Manolakis&lt;/p&gt;

         10 credits
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class="csdegree-year"&gt;
  &lt;h2&gt;Year 3&lt;/h2&gt;
  &lt;p&gt;Year 3 was the first honours year, and so courses start to get more specific in comparison to the foundational courses of years 1 and 2. It had 50 free credits&lt;/p&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 1&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/18-19/dpt/cxinfr10022.htm"&gt;Professional Issues&lt;/a&gt;&lt;/summary&gt;
        &lt;p&gt;This course was focused on "soft" skills such as ethical, legal and financial issues for software development businesses.&lt;/p&gt;
        &lt;p&gt;Textbook: Professional Issues in Information Technology, Frank Bott&lt;/p&gt;
        &lt;p&gt;10 credits&lt;/p&gt;
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/18-19/dpt/cxinfr09051.htm"&gt;Informatics Large Practical&lt;/a&gt;&lt;/summary&gt;
        &lt;p&gt;This was a project course that required students to build an Android game. There was a specification, but some room for creativity. &lt;a href="https://github.com/notexactlyawe/coinz"&gt;My code from this course is on GitHub&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;No textbook&lt;/p&gt;

        &lt;p&gt;20 credits&lt;/p&gt;
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/18-19/dpt/cxinfr09046.htm"&gt;Computer Design&lt;/a&gt; (optional)&lt;/summary&gt;
      Course on digital logic and processor design.
        &lt;ul&gt;
          &lt;li&gt;Combinatorial logic&lt;/li&gt;
          &lt;li&gt;Sequential logic&lt;/li&gt;
          &lt;li&gt;Processor design&lt;/li&gt;
          &lt;li&gt;Memory system design&lt;/li&gt;
          &lt;li&gt;I/O controller design&lt;/li&gt;
          &lt;li&gt;Synchronisation issues&lt;/li&gt;
          &lt;li&gt;RISC&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: 'Computer Organization', V. C. Hamacher, Z. G. Vranesic &amp; S. G. Zaky&lt;/p&gt;

         20 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/ivr/"&gt;Introduction to Vision and Robotics&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Robotic actuators and sensors&lt;/li&gt;
          &lt;li&gt;Control theory&lt;/li&gt;
          &lt;li&gt;Image thresholding, filtering and classification&lt;/li&gt;
          &lt;li&gt;Active vision and attention&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Russell &amp; Norvig Chapters 24 &amp; 25 in Artificial Intelligence: A Modern Approach&lt;/p&gt;

         10 credits
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 2&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
    &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/18-19/dpt/cxinfr09032.htm"&gt;System Design Project&lt;/a&gt;&lt;/summary&gt;
        &lt;p&gt;This was a group project to build a lego robot with the theme of "assistive technology". My team chose to build a chess-playing robot to enable disabled players to compete in tournaments more easily. See the &lt;a href="https://github.com/deeper-blue/"&gt;Deeper Blue GitHub organisation&lt;/a&gt; for more details.&lt;/p&gt;

        &lt;p&gt;No textbook&lt;/p&gt;

        &lt;p&gt;20 credits&lt;/p&gt;
      &lt;/div&gt;
      &lt;div class="csdegree-course"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/cs/index-2017.html"&gt;Computer Security&lt;/a&gt;&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Network security&lt;/li&gt;
          &lt;li&gt;Usable security (the human factor)&lt;/li&gt;
          &lt;li&gt;Symmetric and asymmetric ciphers&lt;/li&gt;
          &lt;li&gt;MACs and hash functions&lt;/li&gt;
          &lt;li&gt;Digital signatures&lt;/li&gt;
          &lt;li&gt;SSL/TLS&lt;/li&gt;
          &lt;li&gt;TOR&lt;/li&gt;
          &lt;li&gt;OS Security&lt;/li&gt;
          &lt;li&gt;Web security&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Introduction to Computer Security, Michael Goodrich and Robert Tamassia Pearson&lt;/p&gt;

         20 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/18-19/dpt/cxinfr09047.htm"&gt;Operating Systems&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Process management&lt;/li&gt;
          &lt;li&gt;Resource allocation and deadlocks&lt;/li&gt;
          &lt;li&gt;The Kernel&lt;/li&gt;
          &lt;li&gt;Memory management (virtual memory, paging etc)&lt;/li&gt;
          &lt;li&gt;Scheduling&lt;/li&gt;
          &lt;li&gt;File management&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: 'Operating Systems, Internals and Design Principles', W. Stallings&lt;/p&gt;

         20 credits
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class="csdegree-year"&gt;
  &lt;h2&gt;Year 4&lt;/h2&gt;
  &lt;p&gt;Year 4 really frees up with 80 credits for optional courses. Of course, most of these credits have to be spent in the area of computer science unlike years 1 and 2.&lt;/p&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 1&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/cg/index2019.html"&gt;Computer Graphics&lt;/a&gt; (optional)&lt;/summary&gt;
      This was a coursework-only course which is unusual. Most courses have at least 60% of their mark come from an exam. There were three courseworks.
        &lt;ul&gt;
          &lt;li&gt;Compositing using Blender, PBRT and Gimp&lt;/li&gt;
          &lt;li&gt;Build a raytracer&lt;/li&gt;
          &lt;li&gt;Implement a new sampling method and material in PBRT&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: &lt;a href="http://www.pbr-book.org/"&gt;'Physically Based Rendering'&lt;/a&gt;, Matt Pharr, Greg Humphreys &amp; Wenzel Jakob&lt;/p&gt;

         10 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="http://www.drps.ed.ac.uk/18-19/dpt/cxinfr11088.htm"&gt;Extreme Computing&lt;/a&gt; (optional)&lt;/summary&gt;
      Course on cloud computing and web-scale technologies.
        &lt;ul&gt;
          &lt;li&gt;Distributed file systems&lt;/li&gt;
          &lt;li&gt;Virtualisation&lt;/li&gt;
          &lt;li&gt;Fault tolerance&lt;/li&gt;
          &lt;li&gt;Hadoop/MapReduce&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Data Intensive Text Processing with MapReduce, Jimmy Linn &amp; Chris Dyer&lt;/p&gt;

         10 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="http://www.speech.zone/courses/speech-processing/"&gt;Speech Processing&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Speech signals (source-filter model)&lt;/li&gt;
          &lt;li&gt;Text processing (POS tagging, expanding abbreviations etc)&lt;/li&gt;
          &lt;li&gt;Pronunciation (letter-to-sound models, prosody prediction)&lt;/li&gt;
          &lt;li&gt;Waveform generation in speech synthesis&lt;/li&gt;
          &lt;li&gt;Speech recognition features&lt;/li&gt;
          &lt;li&gt;Hidden Markov models&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Speech and Language Processing, Jurafsky, D and J. H. Martin&lt;/p&gt;

         20 credits
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Semester 2&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="https://www.inf.ed.ac.uk/teaching/courses/comn/"&gt;Computer Communications and Networks&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;The Internet layer model&lt;/li&gt;
          &lt;li&gt;UDP and TCP&lt;/li&gt;
          &lt;li&gt;IP&lt;/li&gt;
          &lt;li&gt;Routing algorithms&lt;/li&gt;
          &lt;li&gt;Link layer protocols&lt;/li&gt;
          &lt;li&gt;Software defined networking&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Computer Networking: A Top-Down Approach, J. F. Kurose and K. W. Ross&lt;/p&gt;

         20 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="http://www.inf.ed.ac.uk/teaching/courses/asr/"&gt;Automatic Speech Recognition&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Gaussian mixture models&lt;/li&gt;
          &lt;li&gt;EM algorithm&lt;/li&gt;
          &lt;li&gt;Hidden Markov models&lt;/li&gt;
          &lt;li&gt;Speech signal analysis and features&lt;/li&gt;
          &lt;li&gt;Context dependent modelling&lt;/li&gt;
          &lt;li&gt;Hybrid HMM/DNN systems&lt;/li&gt;
          &lt;li&gt;Large vocabulary ASR&lt;/li&gt;
          &lt;li&gt;TDNN and LSTM architectures&lt;/li&gt;
          &lt;li&gt;Speaker adaptation&lt;/li&gt;
          &lt;li&gt;Multi-lingual speech recognition&lt;/li&gt;
          &lt;li&gt;Speaker verification and diarization&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Speech and Language Processing, Jurafsky, D and J. H. Martin&lt;/p&gt;

         10 credits
      &lt;/div&gt;
      &lt;div class="csdegree-course optional"&gt;&lt;summary&gt;&lt;a href="http://www.inf.ed.ac.uk/teaching/courses/st/2017-18/index.html"&gt;Software Testing&lt;/a&gt; (optional)&lt;/summary&gt;
        &lt;ul&gt;
          &lt;li&gt;Unit testing&lt;/li&gt;
          &lt;li&gt;Combinatorial testing&lt;/li&gt;
          &lt;li&gt;Structural testing&lt;/li&gt;
          &lt;li&gt;Coverage&lt;/li&gt;
          &lt;li&gt;Mutation testing&lt;/li&gt;
          &lt;li&gt;TDD&lt;/li&gt;
          &lt;li&gt;Regression testing&lt;/li&gt;
        &lt;/ul&gt;

        &lt;p&gt;Textbook: Software Testing and Analysis: Process, Principles and Techniques, Pezze and Young&lt;/p&gt;

         10 credits
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="csdegree-sem"&gt;
    &lt;h3&gt;Full Year&lt;/h3&gt;
    &lt;div class="csdegree-sem-wrap"&gt;
      &lt;div class="csdegree-course full-year"&gt;&lt;summary&gt;Honours Project&lt;/summary&gt;
        &lt;p&gt;This is the course that represents our undergraduate dissertation. At the end of third year, you have to produce a preference list of five topics from around 300 or you can propose your own. You then get assigned a topic and a supervisor and work on it through fourth year. Mine was on Kubernetes and mobile networks. You can &lt;a href="/dissertation.pdf"&gt;read it here&lt;/a&gt;.&lt;/p&gt;

        &lt;p&gt;40 credits&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><category term="Blogging"></category><category term="Degrees"></category><category term="Career"></category><category term="Computer science"></category></entry><entry><title>Build a snake game on the BBC micro:bit</title><link href="https://www.cameronmacleod.com/blog/microbit-snake" rel="alternate"></link><published>2020-05-18T00:00:00+01:00</published><updated>2020-05-18T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2020-05-18:/blog/microbit-snake</id><summary type="html">&lt;p&gt;&lt;img alt="" src="/images/microbitsnake/header.png"&gt; By the end of this tutorial, you'll have built your very own game and learned not only about game development, but Python and the BBC micro:bit too. What's more, you don't even need to own a micro:bit to follow along!&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="" src="/images/microbitsnake/header.png"&gt;&lt;/p&gt;
&lt;p&gt;By the end of this tutorial, you'll have built your very own game and learned not only about game development, but Python and the BBC micro:bit too. What's more, you don't even need to own a micro:bit to follow along!&lt;/p&gt;
&lt;p&gt;Below is a preview of what we'll be building.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/snakepreview.mp4"&gt;Snake game running on the BBC micro:bit&lt;/video&gt;

&lt;h2 id="pre-requisites"&gt;Pre-requisites&lt;a class="headerlink" href="#pre-requisites" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This tutorial will assume that you know what Python is, and that you know some basic programming concepts. But don't worry, if you consider yourself a beginner then this tutorial is for you. In fact, I will try to over-explain things and provide solutions to every exercise, so there's nothing to fear!&lt;/p&gt;
&lt;p&gt;With that out the way, let's get started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick links&lt;/strong&gt;&lt;/p&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#pre-requisites"&gt;Pre-requisites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#what-is-a-microbit"&gt;What is a micro:bit?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#setting-up-environment"&gt;Setting up environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#snake"&gt;Snake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#skeleton-code"&gt;Skeleton code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#creating-the-game-state"&gt;Creating the game state&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#python-lists"&gt;Python lists&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-microbit-co-ordinate-system"&gt;The micro:bit co-ordinate system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#your-turn-1-filling-in-the-state"&gt;Your turn 1: Filling in the state&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#drawing-to-the-screen"&gt;Drawing to the screen&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#drawing-the-food"&gt;Drawing the food&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#python-for-loops"&gt;Python for loops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#your-turn-2-drawing-the-snake"&gt;Your turn 2: Drawing the snake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#running-our-code"&gt;Running our code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#moving-the-snake"&gt;Moving the snake&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#python-if-statements"&gt;Python if statements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#your-turn-3-calculating-the-new-head"&gt;Your turn 3: Calculating the new head&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#your-turn-4-removing-the-tail"&gt;Your turn 4: Removing the tail&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#wrapping"&gt;Wrapping&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#your-turn-5-wrap-the-screen"&gt;Your turn 5: Wrap the screen&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#getting-input"&gt;Getting input&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#accelerometers"&gt;Accelerometers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#simulator-only-how-to-test-with-the-accelerometer"&gt;Simulator only: How to test with the accelerometer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#your-turn-6-updating-direction"&gt;Your turn 6: Updating direction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#food"&gt;Food&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#your-turn-7-eating-food"&gt;Your turn 7: Eating food&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#random-numbers"&gt;Random numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#your-turn-8-generating-food"&gt;Your turn 8: Generating food&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#ending-the-game"&gt;Ending the game&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#loop-control"&gt;Loop control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#your-turn-9-ending-the-game"&gt;Your turn 9: Ending the game&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#final-notes"&gt;Final notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#extensions"&gt;Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#useful-links"&gt;Useful links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#copyright"&gt;Copyright&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id="what-is-a-microbit"&gt;What is a micro:bit?&lt;a class="headerlink" href="#what-is-a-microbit" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;a href="https://microbit.org/"&gt;micro:bit&lt;/a&gt; is a tiny computer that you can program in Python. It comes with an LED grid (for a screen), an accelerometer (to detect motion), electrical outputs (for controlling motors, lights etc) and a bunch of other cool features. They're also pretty cheap (£20/$20 at time of writing on Amazon), and programming them is as simple as dragging a file to a USB stick.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A micro:bit" src="/images/microbitsnake/microbit.svg"&gt;&lt;/p&gt;
&lt;p&gt;If you don't already have one, don't worry! There are simulators available online and you'll be able to follow along with this whole tutorial.&lt;/p&gt;
&lt;h2 id="setting-up-environment"&gt;Setting up environment&lt;a class="headerlink" href="#setting-up-environment" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whether you have a micro:bit or not, the first step is to open the &lt;a href="https://www.cameronmacleod.com/createwithcode/"&gt;create.withcode editor&lt;/a&gt;. When you do, you should see a screen like the one below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="create.withcode editor" src="/images/microbitsnake/createwithcode.png"&gt;&lt;/p&gt;
&lt;p&gt;Most of the screen (1) is taken up with the editor. This is where you'll put your code. In the lower-right corner (2) is a button to run your code which looks like a green triangle. Alternatively, you can run your code by pressing &lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;Enter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Your editor will auto-backup your code to your computer. You can access these backups by clicking the "+" button in the lower-right corner then clicking the button that looks like a clock.&lt;/p&gt;
&lt;p&gt;Running your code will produce a pop-up that looks like the below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="micro:bit create.withcode interface" src="/images/microbitsnake/microbitmodal.png"&gt;&lt;/p&gt;
&lt;p&gt;Your code will be running on the virtual micro:bit you can see, and there are tabs along the top for your to control various inputs to the device, which will become important later.&lt;/p&gt;
&lt;p&gt;If you are working with a physical micro:bit, then the "Download HEX" button in the top-left of the pop-up will allow you to download the file you need to program your micro:bit. You can drag this file to the "MICROBIT" device in your file manager to program it.&lt;/p&gt;
&lt;p&gt;Before we start looking at some code, let's take a quick detour to look at the game of Snake.&lt;/p&gt;
&lt;h2 id="snake"&gt;Snake&lt;a class="headerlink" href="#snake" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fun fact - if you go to Google and search for "snake", a snake game should appear. Just in case it doesn't appear for you, you can play it &lt;a href="https://www.google.com/fbx?fbx=snake_arcade"&gt;here&lt;/a&gt;. Below you can see it in action.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/snakedemo.mp4"&gt;Google Snake game demo&lt;/video&gt;

&lt;p&gt;This is a good example of a snake game. It has all the necessary components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A grid on which the snake moves.&lt;ul&gt;
&lt;li&gt;The grid has cells, which can be identified by their X and Y co-ordinates.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A snake.&lt;ul&gt;
&lt;li&gt;The snake occupies some cells of the grid (you can think of it as a list of co-ordinates).&lt;/li&gt;
&lt;li&gt;The snake has a direction (up, down, left, right).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Some randomly generated food on a cell.&lt;/li&gt;
&lt;li&gt;An end condition.&lt;ul&gt;
&lt;li&gt;In the video this happens when the snake crashes into itself, losing the game.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So in theory, if we can build all these components, then we should have a snake game!&lt;/p&gt;
&lt;h2 id="skeleton-code"&gt;Skeleton code&lt;a class="headerlink" href="#skeleton-code" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To make it easier to get started, we'll be working from a skeleton file. Find it at &lt;a href="https://github.com/notexactlyawe/microbit-snake/blob/master/skeleton.py"&gt;this link&lt;/a&gt; and copy the contents into the &lt;a href="https://www.cameronmacleod.com/createwithcode/"&gt;create.withcode editor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let's take a look at each part of the code to figure out what it does.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;microbit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Snake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At the very top of the file is an import. An import allows us to use code someone else has written in our own script. In this case, the other code we're using is the &lt;code&gt;microbit&lt;/code&gt; library, which allows us to control various components of the micro:bit including the display and accelerometer.&lt;/p&gt;
&lt;p&gt;Skipping the class definition for now, at the bottom of the file is our game loop.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The game loop is the entry point for the game (the first bit of code to be executed). It defines a series of actions that we do repeatedly in order to make the game work. In this case, we get some input from the user, update the &lt;dfn title="The state of a program is another word for its variables"&gt;game state&lt;/dfn&gt; accordingly and then re-draw the screen. Updating the screen twice a second feels about right, and so at the end we sleep for 500 milliseconds (half a second).&lt;/p&gt;
&lt;p&gt;The functions called in the game loop are part of the Snake class, which is defined above the game loop.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Snake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; This class contains the functions that operate&lt;/span&gt;
&lt;span class="sd"&gt;        on our game as well as the state of the game.&lt;/span&gt;
&lt;span class="sd"&gt;        It&amp;#39;s a handy way to link the two.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Snake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A class is a handy way of keeping some state, like the position of our snake, alongside the functions that operate on it. Our class has a few different functions that we'll have to write.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;__init__(self)&lt;/code&gt;: This is a special Python function that is called when an "instance" of a class is created. We'll see an example of creating an instance later on. The function will initialise our variables ready for the game to run. For example, we need an initial position for the snake and a direction it will first be heading.&lt;/p&gt;
&lt;p&gt;You may notice that it takes an argument, &lt;code&gt;self&lt;/code&gt;. This argument is a Python requirement for functions that are part of a class, and represents the instance of the class. This means we can use it to access the state that's part of the class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;handle_input(self)&lt;/code&gt;: This function gets input from the user and uses it to change the direction of the snake. We'll be using the accelerometer for this which means that the user will be tilting the device to make the snake move. Again, this function takes &lt;code&gt;self&lt;/code&gt; since Python requires it to.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update(self)&lt;/code&gt;: Once we have the direction that the user wants the snake to move, we can update the game state. This means moving the snake, "eating" food and ending the game.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;draw(self)&lt;/code&gt;: The &lt;code&gt;draw&lt;/code&gt; function allows us to see the game state on the screen. In this function we'll be lighting up LEDs to represent the snake and the food.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below the &lt;code&gt;Snake&lt;/code&gt; class definition, we create an instance of it and assign it to the variable &lt;code&gt;game&lt;/code&gt;. If you're not yet comfortable with classes and instances, take a look at the expandable panel below.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Classes and instances&lt;/summary&gt;
&lt;p&gt;
A class is a way of linking functions to variables, but you can also think of it as defining the 'type' of something. Let's use cars as an analogy:


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colour&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;This is a &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;astra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Vauxhall&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Astra&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;white&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;golf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Volkswagen&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Golf&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;blue&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;astra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Prints &amp;quot;This is a white Vauxhall Astra&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;golf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Prints &amp;quot;This is a blue Volkswagen Golf&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



In this example, we've defined a `Car` _class_ that contains the make, model and colour of a car. The class represents the concept of a car, but note that it does not represent any actual cars, that's what instances are for.

Below the class definition, we create two _instances_ of `Car`. One that represents an Astra, and one that represents a Golf. An instance represents a specific thing, instead of the concept of a thing. So our `astra` variable does not represent the idea of a car, but instead a specific white Vauxhall Astra.

One thing you may notice about the class is that both of its functions take `self` as the first argument. This represents the instance of the class that the function is being called on. When we call `astra.print_info()`, Python passes the `astra` instance as the first argument to the `print_info()` function.

One other thing that often confuses people with classes is the `__init__` method. It's a special method in Python that gets called when you create an instance of a class. When we create an instance, you can think of the following happening.

1. We call `astra = Car("Vauxhall", "Astra", "white")`
2. Python creates an empty `Car`, let's call it `temp_car`.
3. Python runs `astra = Car.__init__(temp_car, "Vauxhall", "Astra", "white")`
4. `astra` now has the correct variables and `temp_car` is discarded.

For more discussion of classes, take a look a [Jeff Knupp's blog post](https://jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/).
&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;Now that we have a template, our first step will be to initialise the game state.&lt;/p&gt;
&lt;h2 id="creating-the-game-state"&gt;Creating the game state&lt;a class="headerlink" href="#creating-the-game-state" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we can intialise the game state, we need to learn a little bit about Python and the micro:bit. Feel free to skip these sections if you have done this before.&lt;/p&gt;
&lt;h3 id="python-lists"&gt;Python lists&lt;a class="headerlink" href="#python-lists" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Lists are a way to store multiple values in the same variable in Python. If you haven't come across them before there's a more &lt;a href="http://openbookproject.net/thinkcs/python/english3e/lists.html"&gt;detailed introduction here&lt;/a&gt;. You may be able to get by with the cheat sheet below though.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# define a list&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# access elements of a list (start counting at 0)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="c1"&gt;# -1 is the last element of the list&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="c1"&gt;# Add an item to a list&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Remove an item from a list&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# list of lists&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list_of_coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list_of_coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list_of_coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="the-microbit-co-ordinate-system"&gt;The micro:bit co-ordinate system&lt;a class="headerlink" href="#the-microbit-co-ordinate-system" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On the front of a micro:bit is a grid of LEDs. When drawing something on the screen, we need a way to describe individual LEDs within this grid so that we know which one to light up. This is where the co-ordinate system comes in.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of the micro:bit co-ordinate system" src="/images/microbitsnake/displaycoords.svg"&gt;&lt;/p&gt;
&lt;p&gt;Display co-ordinates on the micro:bit work a little like the graphs you may have plotted in school. There's an X axis that goes from left to right and a Y axis that goes from top to bottom. The main difference from the graphs in school is that the Y axis increases going down (towards the pins) whereas in school it would have increased going up.&lt;/p&gt;
&lt;p&gt;In the image above, point A is at the co-ordinates &lt;code&gt;[3, 0]&lt;/code&gt;. The left number in these co-ordinates is the position along the X axis and the right number is the position along the Y axis. Similarly, point B is at co-ordinates &lt;code&gt;[1, 2]&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="your-turn-1-filling-in-the-state"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 1: Filling in the state&lt;a class="headerlink" href="#your-turn-1-filling-in-the-state" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you now feel comfortable with these concepts, you'll notice that the &lt;code&gt;__init__&lt;/code&gt; function has some commented out code. When you uncomment that, it looks like the below. Feel free to have a go at completing it and we'll look at the solution in the next section.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# direction is a string with up, down, left or right&lt;/span&gt;
&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="c1"&gt;# snake is a list of the pixels that the snake is at&lt;/span&gt;
&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="c1"&gt;# food is the co-ords of the current food&lt;/span&gt;
&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="c1"&gt;# whether or not to end the game (boolean)&lt;/span&gt;
&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;
Let's talk through the solution line by line.


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;up&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



We defined `self.direction` to be a string and I've chosen to make the snake go up at first. If you've put any other direction that's fine too.


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



I've put my food in the middle of the left-most column to start with. If you've put it elsewhere, that's fine as long as you've defined valid co-ordinates that aren't the same as the snake's starting position.


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



This variable tells us whether the game has finished or not. It wouldn't be very helpful to set it to `True` before it's even started!
&lt;/p&gt;
&lt;/details&gt;

&lt;h2 id="drawing-to-the-screen"&gt;Drawing to the screen&lt;a class="headerlink" href="#drawing-to-the-screen" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We have some initial state, but if you ran this code right now, you wouldn't see much happening. That's because we're not actually drawing anything to the screen.&lt;/p&gt;
&lt;p&gt;So what do we need to draw on the screen? Thinking back to our Google Snake example, the snake, food and grid were visible. On the micro:bit the grid is already built for us since the LEDs form a grid naturally. That leaves the snake and the food to be drawn.&lt;/p&gt;
&lt;h3 id="drawing-the-food"&gt;Drawing the food&lt;a class="headerlink" href="#drawing-the-food" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ideally, then, we need a function to light up an individual LED using its co-ordinates. The snake and the food are both stored using co-ordinates, so if we can draw these on one at a time then we'll have completed our &lt;code&gt;draw&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;The micro:bit offers us a number of different ways to &lt;a href="https://microbit-micropython.readthedocs.io/en/latest/display.html"&gt;draw to the screen&lt;/a&gt;, including one that does what we need. &lt;code&gt;display.set_pixel(x, y, value)&lt;/code&gt; will set the pixel at &lt;code&gt;[x, y]&lt;/code&gt; to the level of brightness &lt;code&gt;value&lt;/code&gt;. &lt;code&gt;value&lt;/code&gt; can be between 0 and 9, where 0 is off and 9 is fully on.&lt;/p&gt;
&lt;p&gt;Now that we have a function to turn on a pixel, we can start to fill in our &lt;code&gt;draw&lt;/code&gt; function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_pixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# TODO: draw snake&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first thing we do in this function is &lt;code&gt;display.clear()&lt;/code&gt;. This sets all the LEDs off, which is important because this draw function will be called repeatedly and we want to draw something different each time. If we didn't clear the display, then the game state from the last iteration of the game loop would still be there.&lt;/p&gt;
&lt;p&gt;Next we use the &lt;code&gt;self.food&lt;/code&gt; co-ordinates to draw the food. We use the intensity &lt;code&gt;5&lt;/code&gt; because that will help us to distinguish it from the snake.&lt;/p&gt;
&lt;p&gt;Before we draw the snake, let's take a look at &lt;code&gt;for&lt;/code&gt; loops in Python. Again, if you've done this before, feel free to skip it.&lt;/p&gt;
&lt;h3 id="python-for-loops"&gt;Python &lt;code&gt;for&lt;/code&gt; loops&lt;a class="headerlink" href="#python-for-loops" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;for&lt;/code&gt; loops are great for when you want to do the same thing to multiple elements. In Python they're written like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;variable_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;iterable_thing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;do_things&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;variable_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;iterable_thing&lt;/code&gt; is something with multiple elements, for example a list, tuple or even a &lt;code&gt;range(start, stop)&lt;/code&gt;. &lt;code&gt;variable_name&lt;/code&gt; is the name that you assign to the element that you are currently working on in the loop. To make this example more concrete, here's how you would print the numbers from one to five.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="c1"&gt;# alternatively&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="your-turn-2-drawing-the-snake"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 2: Drawing the snake&lt;a class="headerlink" href="#your-turn-2-drawing-the-snake" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now you know how to light up individual pixels, and how to use a &lt;code&gt;for&lt;/code&gt; loop to go over a list, you should be able to draw the snake to the screen. You'll want to draw the snake with intensity &lt;code&gt;9&lt;/code&gt; so that it looks different from the food we drew earlier. As before, the solution is below.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_pixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_pixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



Since `self.snake` is a list of co-ordinates, we can use it in a `for` loop to get every co-ordinate that's part of the snake. We then use the `set_pixel` function to draw each part to the screen.
&lt;/p&gt;
&lt;/details&gt;

&lt;h3 id="running-our-code"&gt;Running our code&lt;a class="headerlink" href="#running-our-code" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At this point, we should see some output, so let's go ahead and run our code. Whether or not you have a physical micro:bit, press &lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;Enter&lt;/code&gt; in the editor to bring up the micro:bit pop up. You can also click the cross in the corner and then click the green play button.&lt;/p&gt;
&lt;p&gt;If you want to run the code on your physical micro:bit, click the "Download HEX" button in the top left corner. You can refer to the &lt;a href="#setting-up-environment"&gt;Setting up environment section&lt;/a&gt; if you can't find it.&lt;/p&gt;
&lt;p&gt;You should see something that looks like the below. If so, well done! If not, go back and check the solutions match your code exactly. Whitespace matters in Python as well, so check that carefully.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Snake game state being drawn to the screen" src="/images/microbitsnake/drawingtoscreen.svg"&gt;&lt;/p&gt;
&lt;p&gt;Our snake is in the centre of the screen as a bright dot, and our food is at the left of the screen as a less bright dot. Yours might be in different positions depending on what you chose in &lt;a href="#your-turn-1-filling-in-the-state"&gt;Creating the game state&lt;/a&gt;. The next step is to make the snake move.&lt;/p&gt;
&lt;h2 id="moving-the-snake"&gt;Moving the snake&lt;a class="headerlink" href="#moving-the-snake" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To understand how to move the snake, let's take a look at an example.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/calcheadcuttail.mp4"&gt;Animation showing how the snake moves.&lt;/video&gt;

&lt;p&gt;The animation shows a snake before and after moving down one cell. At first glance, moving the snake in this situation looks like it would be complicated, especially since it moves around a corner. Luckily for us though, it only consists of two actions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Calculate the new head&lt;/li&gt;
&lt;li&gt;Cut the tail off&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So our first step will be to calculate the new head. We can do that with the help of &lt;code&gt;if&lt;/code&gt; statements.&lt;/p&gt;
&lt;h3 id="python-if-statements"&gt;Python &lt;code&gt;if&lt;/code&gt; statements&lt;a class="headerlink" href="#python-if-statements" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sometimes we want to choose what to do based on a condition. For example, you would want to wear a coat if it was raining outside, but if it was sunny you would put on sunglasses. You can do this in Python using &lt;code&gt;if&lt;/code&gt; statements.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# rain is a boolean variable (True or False)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;put_on_coat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# it must be sunny&lt;/span&gt;
    &lt;span class="n"&gt;put_on_sunglasses&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# we&amp;#39;ll go outside whether it&amp;#39;s raining or sunny&lt;/span&gt;
&lt;span class="n"&gt;go_outside&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can even choose between more than two options. Sticking with the weather example, maybe it could be windy, rainy, or sunny. Being the responsible person that you are, you have clothes for each different possibility.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# in this case, weather is a string&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;rain&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;put_on_raincoat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;wind&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;put_on_jumper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;sunny&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;put_on_sunglasses&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;go_outside&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="your-turn-3-calculating-the-new-head"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 3: Calculating the new head&lt;a class="headerlink" href="#your-turn-3-calculating-the-new-head" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We're going to be working in the &lt;code&gt;update()&lt;/code&gt; function here, since that updates the game state before we draw the screen.&lt;/p&gt;
&lt;p&gt;There are a few steps in calculating the new head of the snake.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get current head of the snake (last item of the list)&lt;/li&gt;
&lt;li&gt;Calculate a new head based on &lt;code&gt;self.direction&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add this head to the end of the list (&lt;code&gt;.append(item)&lt;/code&gt; function)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The most difficult step here will be figuring out what to do for each direction. You might find this easier if you draw it out on a co-ordinate grid like the one in the &lt;a href="#the-microbit-co-ordinate-system"&gt;micro:bit co-ordinate system section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; - If you create the new head from the old head (e.g. &lt;code&gt;new_head = self.snake[-1]&lt;/code&gt;), you will need to copy it. Otherwise you will have a really annoying bug. You can copy a list like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# call list() to copy the old head&lt;/span&gt;
&lt;span class="n"&gt;new_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;details&gt;&lt;summary&gt;What is the really annoying bug? (optional)&lt;/summary&gt;
&lt;p&gt;
If you don't copy the head of the snake, then you will modify the existing head as well as creating a new one. Let's illustrate this with an example:

Let's imagine a snake at `[[1, 1], [2, 1], [2, 2]]` where `[2, 2]` is the head of the snake. Also let's say that the snake is moving south.

![Snake at `[[1, 1], [2, 1], [2, 2]]`](/images/microbitsnake/bugex1.png)

When we update the snake, we will take the head and update it to `[2, 3]` since it is heading downwards. Ordinarily this would mean the snake looks like this:

![Snake at `[[2, 1], [2, 2], [2, 3]]`](/images/microbitsnake/bugex2.png)

However, since we didn't copy the head, we'll end up modifying the existing one **and** appending it, so our snake will be at `[[2, 1], [2, 3], [2, 3]]`:

![Snake at `[[2, 1], [2, 3], [2, 3]]`](/images/microbitsnake/bugex3.png)

This is because lists in Python are _mutable_, which means that you can modify their values. When we type `new_head = self.snake[-1]` we're not creating a new list called `new_head`, we're assigning a new name to `self.snake[-1]`.

Some objects in Python, for example tuples, are _immutable_ which means their values cannot be changed. If we used tuples for the co-ordinates we would not have this problem. However, if we used tuples, the code wouldn't be as simple as incrementing or decrementing a co-ordinate, and it would introduce another concept to a beginner-friendly tutorial, something I'd like to keep to a minimum.
&lt;/p&gt;
&lt;/details&gt;

&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;new_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;up&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Y decreases as you go up the screen&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;down&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;left&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# X decreases as you go left&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;right&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="c1"&gt;# put the head on the end of the list&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



Firstly we copy the old head of the snake, remember `-1` is the index of the last element of a list. Next we check what the current direction is, and increment or decrement the appropriate co-ordinate. Finally, we append the new head to the end of the snake list.
&lt;/p&gt;
&lt;/details&gt;

&lt;h3 id="your-turn-4-removing-the-tail"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 4: Removing the tail&lt;a class="headerlink" href="#your-turn-4-removing-the-tail" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Remember the &lt;code&gt;.pop(idx)&lt;/code&gt; method on lists from before? You can remove an item from a list by calling this function with the index of the item that you want to remove.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_list&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your task is to remove the tail of the snake. Here's a hint, if we add the new head to the end of the snake list, then the tail will be at the start of it.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;
The full `update` function should now look like this:


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;new_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;up&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;down&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;left&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;right&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# cut the tail of the snake&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;We're now ready to run this again, and our snake should now move!&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/microbiterror.mp4"&gt;Demonstration of the wrapping error with the snake moving code.&lt;/video&gt;

&lt;p&gt;Except, we get an error. The error on the simulator isn't very helpful, but on the micro:bit it makes more sense.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ValueError: index out of bounds&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;What's happening here is that our &lt;code&gt;new_head&lt;/code&gt; is going out of bounds, which means it's becoming a value that can't be displayed on the screen. If you remember our co-ordinates diagram, the Y co-ordinate is getting smaller until it gets to -1. When we then try and call &lt;code&gt;display.set_pixel&lt;/code&gt; with -1 as an input, it gives us an error, since there's no LED at &lt;code&gt;[2, -1]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We could fix this by ending the game if the snake goes out of bounds, but on a screen this small that wouldn't be much fun. Instead, let's fix it by wrapping our snake around the board.&lt;/p&gt;
&lt;h2 id="wrapping"&gt;Wrapping&lt;a class="headerlink" href="#wrapping" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Wrapping just means that if the snake hits one side of the screen, then it should appear on the other side of the screen.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/wrappingdemo.mp4"&gt;Demonstration of wrapping the snake to the other side of the screen.&lt;/video&gt;

&lt;p&gt;In the above example, the snake is moving up the screen. After the snake's head reaches &lt;code&gt;[3, 0]&lt;/code&gt; it goes to &lt;code&gt;[3, 4]&lt;/code&gt; and appears at the bottom of the screen. The snake continues to move upwards, just starting from the bottom of the screen now. We can implement this behaviour by checking the bounds of the &lt;code&gt;new_head&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="your-turn-5-wrap-the-screen"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 5: Wrap the screen&lt;a class="headerlink" href="#your-turn-5-wrap-the-screen" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The bounds of our screen are 0-4 in both the X and Y axes. So you'll want to check if the X co-ordinate or the Y co-ordinate are less than/greater than these boundaries. If they are, reset them to the other edge of the screen.&lt;/p&gt;
&lt;p&gt;Again, for this task we'll be working in our &lt;code&gt;update&lt;/code&gt; method.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# the code from above&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;right&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="c1"&gt;# X co-ordinate&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="c1"&gt;# Y co-ordinate&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



We add a new `if` statement for both the X co-ordinate and the Y co-ordinate. In it, we check if they are above 4 or below 0 since those are the maximum co-ordinates of our display. This would also work if you only have a single `if/elif` block for both the X and Y since we'll only be changing one at a time.
&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;When we add this code to our game, it should look like the below.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/wrapping.mp4"&gt;The game successfully wrapping the snake round the screen.&lt;/video&gt;

&lt;h2 id="getting-input"&gt;Getting input&lt;a class="headerlink" href="#getting-input" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this point, we have an image on the screen and the snake moves! The user can't yet control the snake though, so let's add that in.&lt;/p&gt;
&lt;p&gt;The micro:bit has a few different ways of getting input from the user and the world around it. Most visibly, it has two buttons on the front that the user can press. While we could use these for input, four directions doesn't map into two buttons in an intuitive way. Other inputs the micro:bit has include an accelerometer for detecting tilt and motion, a compass for direction relative to the earth, pins on the bottom for electrical input and a temperature sensor. Out of these, the accelerometer can provide the simplest method of control.&lt;/p&gt;
&lt;h3 id="accelerometers"&gt;Accelerometers&lt;a class="headerlink" href="#accelerometers" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We won't try to understand how accelerometers work here, that would take a post of its own. However, we can understand what an accelerometer can do for us. If you've never heard of an accelerometer before, it's the bit of your phone that detects orientation and makes your videos landscape when you tilt your phone.&lt;/p&gt;
&lt;p&gt;In short, an accelerometer can measure the tilt of a device, and we can use this for our game. To see how this will work, take a look at the video below.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/accelphysdemo.mp4"&gt;Using the accelerometer to move a dot around the micro:bit screen&lt;/video&gt;

&lt;p&gt;The dot follows the tilt of the device here. When we tilt the device away from us, the dot heads up the screen (away from us). Tilting the device to the left, right or towards us have similar efects.&lt;/p&gt;
&lt;h3 id="simulator-only-how-to-test-with-the-accelerometer"&gt;Simulator only: How to test with the accelerometer&lt;a class="headerlink" href="#simulator-only-how-to-test-with-the-accelerometer" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/accelvirtdemo.mp4"&gt;Controlling the micro:bit accelerometer with the mouse in the create.withcode editor&lt;/video&gt;

&lt;p&gt;The micro:bit pop-up in the simulator has some tabs along the top for controlling the various components of the micro:bit. One of these tabs controls the accelerometer (seen in the video above). In the tab you will see three sliders, one for each axis, and a checkbox labelled "Move accelerometer with mouse". Go ahead and tick that checkbox.&lt;/p&gt;
&lt;p&gt;With the checkbox ticked, you'll notice that moving your mouse over the micro:bit's screen will change the accelerometer values in the sliders. As you head towards the lower-right corner of the micro:bit the values will increase, and as you head towards the upper-left corner they will decrease.&lt;/p&gt;
&lt;h3 id="your-turn-6-updating-direction"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 6: Updating direction&lt;a class="headerlink" href="#your-turn-6-updating-direction" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We're working in the &lt;code&gt;handle_input&lt;/code&gt; function for this section.&lt;/p&gt;
&lt;p&gt;The accelerometer will return you values in the X and Y axes using the following functions. We don't care about the Z axis since our screen is 2D.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# These functions return values in the range -2000 to 2000&lt;/span&gt;
&lt;span class="n"&gt;accelerometer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_x&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;accelerometer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_y&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The X axis increases going left to right, and the Y axis increases from top to bottom, just like the screen co-ordinates.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Accelerometer axes on the micro:bit" src="/images/microbitsnake/acceldir.png"&gt;&lt;/p&gt;
&lt;p&gt;We'll calculate the direction the user wants to go in by taking the biggest value from X and Y and then seeing if it's positive or negative (see diagram above). Careful though! X and Y can both be negative, so you can't do a simple &lt;code&gt;X &amp;gt; Y&lt;/code&gt;. To see why, imagine that X was 500 and Y was -1000. Even though Y is a bigger number than X, &lt;code&gt;X &amp;gt; Y == True&lt;/code&gt; because X is positive.&lt;/p&gt;
&lt;p&gt;You may find the absolute function, &lt;code&gt;abs(x)&lt;/code&gt;, useful.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# abs (the absolute function) will remove a minus sign if present&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;span class="c1"&gt;# ... and leave the value alone if not&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# get accelerometer values&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accelerometer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_x&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accelerometer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_y&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# compare the magnitude of X and Y&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# X is bigger than Y (maybe negative)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;left&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;right&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Y is bigger than X (maybe negative)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;up&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;down&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



First we get the raw values from the accelerometer for X and Y, and then we compare their magnitudes using `abs()`. If X is bigger than Y, then we see whether X is negative (left) or positive (right). We do the equivalent checks when Y is bigger than X.
&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;The user can now tilt the micro:bit to make the snake move! You may notice while playing with this that the snake will never grow, so now it's time to figure out how food works.&lt;/p&gt;
&lt;h2 id="food"&gt;Food&lt;a class="headerlink" href="#food" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the snake approaches some food, we want the snake to grow and the food to appear again in another location. Ideally, the process would look like the following animation.&lt;/p&gt;
&lt;video autoplay loop muted playsinline src="/images/microbitsnake/food.mp4"&gt;Animation showing the snake eating food and growing&lt;/video&gt;

&lt;p&gt;Building our food mechanic can be split into two steps. Detecting and reacting to food being eaten, and then generating new food.&lt;/p&gt;
&lt;h3 id="your-turn-7-eating-food"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 7: Eating food&lt;a class="headerlink" href="#your-turn-7-eating-food" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The snake "eats" the food when its head is in the same cell as the food. When it eats the food, it should grow by one cell which can be done by not cutting the tail from the snake.&lt;/p&gt;
&lt;p&gt;In the case that the snake is eating the food, you should leave some space in your code to generate the new food.&lt;/p&gt;
&lt;p&gt;Hint: In Python you can compare two lists just with &lt;code&gt;list1 == list2&lt;/code&gt;.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_head&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# generate new food&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# cut the tail of the snake&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



We compare the new head and the food. If they are the same then we leave some space in the code to generate the new food.

We cut the tail in the else block, which means that we only cut it when the snake isn't eating food.
&lt;/p&gt;
&lt;/details&gt;

&lt;h3 id="random-numbers"&gt;Random numbers&lt;a class="headerlink" href="#random-numbers" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We want to generate new food randomly so that the game is interesting. We can do this using the &lt;code&gt;random&lt;/code&gt; module in Python. Here's an example that guesses (poorly) how old you are.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# import the function randint from the module random&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;

&lt;span class="c1"&gt;# generate a random integer between 0 and 100 (inclusive)&lt;/span&gt;
&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I bet you are &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; years old&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this program we import then call the &lt;code&gt;randint(start, stop)&lt;/code&gt; function. This will give us back a number between 1 and 100. We then print it out using an &lt;a href="https://realpython.com/python-f-strings/"&gt;f-string&lt;/a&gt;. Side note: if you haven't come across f-strings before, check out that link. They are very cool.&lt;/p&gt;
&lt;p&gt;We'll be using this &lt;code&gt;randint&lt;/code&gt; function to generate random co-ordinates.&lt;/p&gt;
&lt;h3 id="your-turn-8-generating-food"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 8: Generating food&lt;a class="headerlink" href="#your-turn-8-generating-food" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To generate new food, you'll need to generate an X co-ordinate and a Y co-ordinate. Remember, the axes run from 0 to 4.&lt;/p&gt;
&lt;p&gt;One edge case you'll have to think about is what happens if the co-ordinates you generate are covered by the snake. The solution I'll recommend is to keep re-generating the co-ordinates until they are not in the snake, but feel free to come up with your own solutions here. You can check if the co-ordinates are inside the snake using the &lt;code&gt;in&lt;/code&gt; keyword (&lt;code&gt;co_ords in self.snake&lt;/code&gt;) and a &lt;code&gt;while&lt;/code&gt; loop.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_head&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



We use a `while` loop to check if the food we've generated is inside the snake or not. We keep generating the food until it's visible on the screen.
&lt;/p&gt;
&lt;/details&gt;

&lt;details&gt;&lt;summary&gt;Is this really a valid solution?&lt;/summary&gt;
&lt;p&gt;
In case you're having trouble believing that re-generating the food is a valid solution, let's take a look at some probabilities.

We can model generating the food successfully as a binomially distributed random variable. If the snake is of length two (the smallest case after eating food), then the probability of generating the food in an empty space is 23/25. This means that we have a 92% chance of success on the first go and a 99% chance of succeeding within two tries. The expected number of generations before our first success is just over 1.

Taking the worst case where the snake is length 24 after eating food, the probability of generating the food correctly is 1/25. This means that the expected number of trials before our first success is 25. In fact 63% of the time it will take 25 tries or fewer and 99% of the time it will take fewer than 113.

These numbers are very small for a computer, and we won't notice much delay in the snake's motion.
&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;Our snake game is almost finished! The only step left is figuring out how to end the game.&lt;/p&gt;
&lt;h2 id="ending-the-game"&gt;Ending the game&lt;a class="headerlink" href="#ending-the-game" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The game of snake ends in two situations. Firstly, if you crash into yourself (or the wall, in some versions) then you lose. Secondly, if you fill the entire grid with your snake then you win.&lt;/p&gt;
&lt;p&gt;We'll worry about losing the game for now, and I'll leave winning the game as an exercise for you.&lt;/p&gt;
&lt;h3 id="loop-control"&gt;Loop control&lt;a class="headerlink" href="#loop-control" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We've seen &lt;code&gt;while&lt;/code&gt; loops a couple of times so far. Most recently we used them to generate food, but at the very start we saw how our game loop is made of a &lt;code&gt;while&lt;/code&gt; loop.&lt;/p&gt;
&lt;p&gt;In Python, you have the ability to run a loop forever with &lt;code&gt;while True&lt;/code&gt;. The complementary feature is that you have the ability to cut a loop short early using the &lt;code&gt;break&lt;/code&gt; statement. Let's take a quick look at how it works.&lt;/p&gt;
&lt;p&gt;In the below example, we wait for the micro:bit's button A to be pressed then show a happy face.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# import all the microbit code&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;microbit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# check if button A was pressed since we last called this function&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;button_a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;was_pressed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# if so, exit the loop&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="c1"&gt;# wait for 100 milliseconds&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# show a happy face&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HAPPY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="your-turn-9-ending-the-game"&gt;&lt;i class="fa fa-lightbulb-o"&gt;&lt;/i&gt; Your turn 9: Ending the game&lt;a class="headerlink" href="#your-turn-9-ending-the-game" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are three steps to implementing losing the game.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect if you've crashed into yourself using the &lt;code&gt;in&lt;/code&gt; keyword. This will be part of the &lt;code&gt;update&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;self.end&lt;/code&gt; to &lt;code&gt;True&lt;/code&gt; if the player has lost.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;break&lt;/code&gt; in the game loop if the player has lost. &lt;code&gt;self.end&lt;/code&gt; will be &lt;code&gt;game.end&lt;/code&gt; inside the game loop.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You could also display a sad face, or scroll some text if the player loses. Get creative!&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Solution&lt;/summary&gt;
&lt;p&gt;
In our update function:


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# after we calculate new_head&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_head&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



We've just added an `if` statement here to check whether the new head is already part of the snake.

In our game loop:


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# now game.end will be set&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SAD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



We add an if statement to check whether `game.end` was set to `True` in `update` and if so we break. I've also added a sad face in there to make it more obvious that the player has lost, but that is optional.
&lt;/p&gt;
&lt;/details&gt;

&lt;h2 id="final-notes"&gt;Final notes&lt;a class="headerlink" href="#final-notes" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Well done on making it this far! You've just built your very own game, and can be really proud of yourself. I hope you've enjoyed the experience and that it's inspired you to keep going with micro:bit, programming or game development. If you want some more material for learning, then check out the &lt;a href="#useful-links"&gt;Useful links&lt;/a&gt; below. If you'd like to check your solution, I've put the &lt;a href="https://github.com/notexactlyawe/microbit-snake/blob/master/snake.py"&gt;final code for this tutorial on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This tutorial was based off a workshop that I wrote while part of the Embedded and Robotics Society (EaRS) at Edinburgh. It was one of my favourite workshops there, and it's been fun to re-visit it and turn it into a full tutorial. If you like this, then they have more resources &lt;a href="http://ears-edi.com/"&gt;on their website&lt;/a&gt; so you should check them out.&lt;/p&gt;
&lt;p&gt;If you found any issues in this tutorial, have ideas for new tutorials you'd like to see, or have a question then feel free to &lt;a href="/contact"&gt;contact me&lt;/a&gt;. I'd love to hear from you.&lt;/p&gt;
&lt;h2 id="extensions"&gt;Extensions&lt;a class="headerlink" href="#extensions" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Just because you finished this tutorial doesn't mean you have to stop working on the game. There are a number of extensions you could make to it, and you may even think of some of your own.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement a winning condition.&lt;/li&gt;
&lt;li&gt;Stop the player from going in a direction opposite to the current one they're going in. This annoyed me while playing the game, since it means you lost when you go back into yourself, and most snake implementations prevent you from being able to do this.&lt;/li&gt;
&lt;li&gt;Expand the game grid. Maybe you could have a grid that is bigger than the screen, and display a part of it at a time. You could show this by moving the food instead of the snake, or by flashing the screen when you drew a different area.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="useful-links"&gt;Useful links&lt;a class="headerlink" href="#useful-links" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://python.microbit.org/v/2.0"&gt;Official Python editor for micro:bit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://makecode.microbit.org/"&gt;MakeCode for micro:bit (JavaScript/Scratch)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ears-edi.com/"&gt;Embedded and Robotics Society Edinburgh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.google.com/presentation/d/1d5NM7Sf5eOalQJ1Otfe1fJ4gtUMj3hgkXyFJPTEyZik/edit?usp=sharing"&gt;Original workshop slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://microbit-challenges.readthedocs.io/en/latest/introduction/getting_started.html"&gt;UCL BBC micro:bit tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="copyright"&gt;Copyright&lt;a class="headerlink" href="#copyright" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pretty micro:bit SVG images were modified from the &lt;a href="https://github.com/microbit-foundation/microbit-svg/"&gt;Micro:bit Educational Foundation's GitHub repo&lt;/a&gt; under the &lt;a href="https://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;CC BY-NC-SA 4.0 license&lt;/a&gt;. Those images on this page are therefore licensed under the same terms.&lt;/p&gt;
&lt;p&gt;All other content on this page is licensed under the &lt;a rel="license" href="http://creativecommons.org/licenses/by/4.0/"&gt;CC BY 4.0 license&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The create.withcode editor hosted on my site is courtesy of Pete Dring of &lt;a href="https://blog.withcode.uk/"&gt;blog.withcode.uk&lt;/a&gt;. It has been slightly modified, hence this tutorial not using the official site.&lt;/p&gt;</content><category term="Tutorials"></category><category term="micro:bit"></category><category term="Python"></category><category term="Games"></category></entry><entry><title>Disappearing documentation (a Kubernetes war story)</title><link href="https://www.cameronmacleod.com/blog/disappearing-k8s-docs" rel="alternate"></link><published>2020-05-03T00:00:00+01:00</published><updated>2020-05-03T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2020-05-03:/blog/disappearing-k8s-docs</id><summary type="html">&lt;p&gt;I like a good war story, and I like them even more when I get to tell them. While writing my dissertation, I ran into a case of disappearing documentation. Here's what happened.&lt;/p&gt;
&lt;p&gt;As part of my undergraduate dissertation, I was working with Kubernetes, and more specifically, the &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/"&gt;controllers&lt;/a&gt; within …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I like a good war story, and I like them even more when I get to tell them. While writing my dissertation, I ran into a case of disappearing documentation. Here's what happened.&lt;/p&gt;
&lt;p&gt;As part of my undergraduate dissertation, I was working with Kubernetes, and more specifically, the &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/"&gt;controllers&lt;/a&gt; within Kubernetes. A controller can be thought of as a generalised thermostat. Just as a thermostat maintains temperature in a house, a controller maintains state in a cluster.&lt;/p&gt;
&lt;p&gt;A controller has some desired state, some world state, and a way of affecting the world state (an actuator). In the example of the thermostat, the world state would be the current temperature, the desired state would be the user-set temperature, and the actuator would be a heating system.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of a thermostat as a controller" src="/images/thermostat_controller.png"&gt;&lt;/p&gt;
&lt;p&gt;Kubernetes has lots of controllers, but the one relevant for this story is the Horizontal Pod Autoscaler. Its job is to check the average CPU used by a set of pods. If the usage is higher or lower than the target usage, then it will create new pods or delete them appropriately. A &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod/"&gt;pod&lt;/a&gt; in Kubernetes is a 'logical host', the equivalent of a virtual machine.&lt;/p&gt;
&lt;p&gt;Controllers in Kubernetes are implemented as control loops. This means that every &lt;code&gt;N&lt;/code&gt; seconds they will collect information about the state of the world, act on it if necessary and then go back to sleep for the next &lt;code&gt;N&lt;/code&gt; seconds.&lt;/p&gt;
&lt;p&gt;For this part of my dissertation, I needed to measure the time it takes for each iteration of the autoscaler control loop to run. This was to test whether it was correlated with the load on the Kubernetes cluster, something that an attacker may be interested in. The loop execution time, in theory, should be influenced by how much work the autoscaler (and, more generally, the master node) is doing.&lt;/p&gt;
&lt;p&gt;I planned to do this from within the cluster, and the flow roughly went as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Record the startup time&lt;/strong&gt; of a worker pod.&lt;/li&gt;
&lt;li&gt;That pod busy loops with high CPU usage.&lt;/li&gt;
&lt;li&gt;A short while later, the first iteration of the autoscaler loop runs and decides that more pods need to be scheduled.&lt;/li&gt;
&lt;li&gt;A new worker pod is brought up and &lt;strong&gt;the startup time recorded&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The two worker pods busy loop with low CPU usage.&lt;/li&gt;
&lt;li&gt;The autoscaler runs again but this time decides it needs to destroy a pod.&lt;/li&gt;
&lt;li&gt;A worker pod receives SIGTERM and &lt;strong&gt;records the time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The process starts again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The time between the startups and terminations of the nodes (in &lt;strong&gt;bold&lt;/strong&gt; above) should be correlated with how long it takes the autoscaler control loop to run. However, a problem with this flow is that the controller has a stabilization window to prevent pods from being created and deleted too often. The window ensures that after making a change, Kubernetes won't make another change until the stabilization window has expired. Luckily this can be set, and this is where the disappearing documentation comes in.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/"&gt;documentation for the Horizontal Pod Autoscaler&lt;/a&gt; (HPA) contains a section on the stabilization window above, which included the following text at the time I was looking at it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Starting from v1.17 the downscale stabilization window can be set on a per-HPA basis by setting the &lt;code&gt;behavior.scaleDown.stabilizationWindowSeconds&lt;/code&gt; field in the v2beta2 API. See Support for configurable scaling behavior.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This was what I was looking for, so I set this field in my HPA deployment file and... got an error. I double and triple checked the code I was using to make sure it matched the spec on that page, but every time I tried to &lt;code&gt;kubectl apply&lt;/code&gt; this file, I got the error below.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;some-autoscale.yaml
error&lt;span class="w"&gt; &lt;/span&gt;validating&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;some-autoscale.yaml&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;error&lt;span class="w"&gt; &lt;/span&gt;validating&lt;span class="w"&gt; &lt;/span&gt;data:&lt;span class="w"&gt; &lt;/span&gt;ValidationError&lt;span class="o"&gt;(&lt;/span&gt;HorizontalPodAutoscaler.spec&lt;span class="o"&gt;)&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;unknown&lt;span class="w"&gt; &lt;/span&gt;field&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;behavior&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Just to make this even more confusing, the Kubernetes API docs agreed with the error that the &lt;code&gt;behavior&lt;/code&gt; field didn't exist. At this point, I had exhausted all other options and had to turn to StackOverflow for help. At 7PM I posted a question, &lt;a href="https://stackoverflow.com/questions/60045564/kubernetes-unknown-field-behavior"&gt;Kubernetes unknown field "behavior"&lt;/a&gt;, and at 11PM I received an answer. The answerer said that I was reading the docs wrong and that the field didn't exist in the first place. Ready to drag this unsuspecting samaritan into my confusion, I went to sleep and planned to reply the following day.&lt;/p&gt;
&lt;p&gt;When I checked the docs the following day, the quote from above was missing. After considering whether I'd imagined it all, I discovered that the Kubernetes docs were in a public GitHub repository that was linked from the bottom of each page. It turned out that a couple of hours before I had posted my question, the text about the &lt;code&gt;behavior&lt;/code&gt; object had been &lt;a href="https://github.com/kubernetes/website/pull/18963"&gt;removed in a PR&lt;/a&gt;. The &lt;code&gt;behavior&lt;/code&gt; object was meant to be introduced in V1.18 instead of V1.17 that was current at the time. The reason I hadn't seen the updated docs was probably due to my browser caching the old version since I'd visited that page a lot.&lt;/p&gt;
&lt;p&gt;Finding out that I wasn't imagining things and that the docs were wrong was very satisfying. Everything had fallen nicely into place. I answered the Stack Overflow question with a shortened version of this story and carried on with my dissertation.&lt;/p&gt;
&lt;p&gt;Now that 1.18 is out, this will no longer be a problem, but I thought it would be an interesting story to share. This was only a small part of my dissertation, which was on mobile networks and Kubernetes. It's now finished (exhausted hooray). If you're interested, you can read it &lt;a href="/dissertation.pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;</content><category term="Blogging"></category><category term="Kubernetes"></category><category term="Documentation"></category></entry><entry><title>Why I chose product management over software development</title><link href="https://www.cameronmacleod.com/blog/why-pm-over-software" rel="alternate"></link><published>2020-03-03T00:00:00+00:00</published><updated>2020-03-03T00:00:00+00:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2020-03-03:/blog/why-pm-over-software</id><summary type="html">&lt;p&gt;I'm about to finish my degree and have been fortunate enough to receive two graduate job offers. One was an associate product manager position at Google, and the other was software engineering at Bloomberg. Both companies and jobs excited me, which left me with a difficult choice. In this post …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I'm about to finish my degree and have been fortunate enough to receive two graduate job offers. One was an associate product manager position at Google, and the other was software engineering at Bloomberg. Both companies and jobs excited me, which left me with a difficult choice. In this post, I'll explain why I chose product management, even after years of wanting to be a software developer.&lt;/p&gt;
&lt;p&gt;The factors that were important to my decision won't be the same for everyone, and this post is not a recommendation of one career path over the other. Choosing a job is deeply personal, and both career paths could potentially be right for any given individual. However, I do hope that this post helps someone else if confronted with a similar decision.&lt;/p&gt;
&lt;h2 id="background"&gt;Background&lt;a class="headerlink" href="#background" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the time of this decision, I:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;was a final year Computer Science student.&lt;/li&gt;
&lt;li&gt;had completed a few software development internships (including an enjoyable one at Bloomberg).&lt;/li&gt;
&lt;li&gt;had applied mostly to software jobs and only to a single product management position (the Google one).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had applied to the Google APM program on a whim and only because I spotted it while looking for software engineering roles. Of course, I liked the sound of the position but had never really thought about working in product before. I wasn't that clear on what product meant.&lt;/p&gt;
&lt;p&gt;Throughout the interview process and up until I signed the contract, I did my best to understand product management. I think that I ended up with a reasonably clear idea of what it is, but of course, I have never actually &lt;em&gt;done&lt;/em&gt; it. After my research, the points below are the biggest reasons that justified my decision.&lt;/p&gt;
&lt;h2 id="generalism"&gt;Generalism&lt;a class="headerlink" href="#generalism" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have always identified with the label 'generalist' for a couple of reasons. Firstly, it means that I enjoy novelty, something that shows up most in my hobbies. I rarely stick with one for longer than a few months. Whether or not this is a character flaw (something I'm still unsure about), it's a part of me that I have to take into account when making big decisions.&lt;/p&gt;
&lt;p&gt;Another reason that I identify with this label is a fear of specialisation. The idea of becoming a professional expert in a single field or skill scares me. I worry that it would be boring. I'm not saying that I don't want to be an expert in anything, more that I'm afraid of becoming a one-trick pony.&lt;/p&gt;
&lt;p&gt;Software development has the potential to provide new challenges each day. I enjoy writing software, and hopefully always will, but I worry about getting pigeonholed as one type of developer. While development would tick the generalism box at the start of my career, I'm not sure that it still would as my career progressed.&lt;/p&gt;
&lt;p&gt;Product management, in itself, would be new for me. From everything I've read, the job involves a lot of context switching and many different skills. Most importantly, it doesn't look like this generalist quality of the job disappears as you move up the career ladder.&lt;/p&gt;
&lt;h2 id="communication"&gt;Communication&lt;a class="headerlink" href="#communication" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Communication is getting an idea out of your head and into someone else's, but this can cover a wide range of skills due to the many ways we convey information. Writing, presenting, face-to-face conversation, education and visual design can all fall under the communication umbrella.&lt;/p&gt;
&lt;p&gt;As I've gotten older, I have placed more importance on communication, mostly as a result of enjoying related activities. Writing for this blog, teaching, giving presentations and finding sponsorship for a hackathon have all brought me a lot of satisfaction, and all of them have been communication heavy. Improving my communication is something I find difficult but pleasantly so.&lt;/p&gt;
&lt;p&gt;Product management relies heavily on communication. Attempting to align people from different backgrounds requires understanding all perspectives and then translating them into the language of other people around you. I find this challenge exciting.&lt;/p&gt;
&lt;h2 id="rounding-my-skill-set"&gt;Rounding my skill-set&lt;a class="headerlink" href="#rounding-my-skill-set" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have been programming for a long time and over that time have acquired a decent level of technical skill. While there is still a lot of room for me to grow as a developer, there are many other skills that I have never had the chance to focus on improving. Becoming a product manager would allow me to focus on different skills than those I have been able to so far.&lt;/p&gt;
&lt;p&gt;Product managers sit in the middle of developers, designers and business people, allowing them to learn a little about each of these areas. This learning would be alongside getting more in-depth knowledge about product management. Product management should broaden my knowledge base in a way few other positions could.&lt;/p&gt;
&lt;h2 id="the-google-effect"&gt;The Google effect&lt;a class="headerlink" href="#the-google-effect" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It might reduce my credibility in some circles, but "working for Google" was also attractive to me. When I first started programming, I idolised Google. Their products were everywhere, and the narrative they cultivated around their working culture seemed exciting and fun, especially for someone who knew little about the working world.&lt;/p&gt;
&lt;p&gt;Of course, after having a few jobs and being exposed to more perspectives, my initial infatuation faded. Every year of university, however, I applied religiously to Google for internships. As time went on, some of my friends started getting in, but I still never even received an interview.&lt;/p&gt;
&lt;p&gt;So finally getting in after all this time did feel a little unbelievable and I had to be very careful not to accept the offer on that basis alone. I stand by this excitement forming part of my final decision, but it was important to me that it wasn't the whole reason for making my choice.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="headerlink" href="#conclusion" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I justified my final decision with the points above, but it was still painful to reject the software engineering position. The decision process was long and involved a series of pros/cons lists and talking to many people before I settled on an answer. Alongside friends and family, one of my interviewers from Google also offered to answer any questions I had, which was really helpful.&lt;/p&gt;
&lt;p&gt;I'm pleased to say that I'm very sure about my decision, and I feel confident that I made the right choice for me. Of course, it's not possible to know that one job or the other is better overall, because that would require predicting not just one future, but two. That said, I have no regrets.&lt;/p&gt;
&lt;p&gt;My start date is in August, and I'm super excited.&lt;/p&gt;</content><category term="Blogging"></category><category term="Career"></category><category term="Product Management"></category></entry><entry><title>Easy Python speed wins with functools.lru_cache</title><link href="https://www.cameronmacleod.com/blog/python-lru-cache" rel="alternate"></link><published>2019-06-10T00:00:00+01:00</published><updated>2019-06-10T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2019-06-10:/blog/python-lru-cache</id><summary type="html">&lt;p&gt;Recently, I was reading an &lt;a href="https://datawhatnow.com/things-you-are-probably-not-using-in-python-3-but-should/"&gt;interesting article&lt;/a&gt; on some under-used Python features. In the article, the author mentioned that from Python version 3.2, the standard library came with a built in decorator &lt;code&gt;functools.lru_cache&lt;/code&gt; which I found exciting as it has the potential to speed up a lot of …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently, I was reading an &lt;a href="https://datawhatnow.com/things-you-are-probably-not-using-in-python-3-but-should/"&gt;interesting article&lt;/a&gt; on some under-used Python features. In the article, the author mentioned that from Python version 3.2, the standard library came with a built in decorator &lt;code&gt;functools.lru_cache&lt;/code&gt; which I found exciting as it has the potential to speed up a lot of applications with very little effort.&lt;/p&gt;
&lt;p&gt;That's great and all, you may be thinking, but what is it? Well, the decorator provides access to a ready-built cache that uses the Least Recently Used (LRU) replacement strategy, hence the name &lt;code&gt;lru_cache&lt;/code&gt;. Of course, that sentence probably sounds a little intimidating, so let's break it down.&lt;/p&gt;
&lt;h2 id="what-is-a-cache"&gt;What is a cache?&lt;a class="headerlink" href="#what-is-a-cache" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A cache is a place that is quick to access where you store things that are otherwise slow to access. To demonstrate this, let's take your web browser as an example.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of a web browser using a cache" src="/images/browser_cache.png"&gt;&lt;/p&gt;
&lt;p&gt;Getting a web page from the internet can take up to a few seconds, even on a fast internet connection. In computer time this is an eternity. To solve this, browsers store the web pages you've already visited in a cache on your computer which can be thousands of times faster to access.&lt;/p&gt;
&lt;p&gt;Using a cache, the steps to download a webpage are as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check the local cache for the page. If it's there, return that.&lt;/li&gt;
&lt;li&gt;Go find the web page on the internet and download it from there.&lt;/li&gt;
&lt;li&gt;Store that web page in the cache to make it faster to access in future.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While this doesn't make things faster the first time you visit a web page, often you'll find yourself visiting a page more than once (think Facebook, or your email) and every subsequent visit will be faster.&lt;/p&gt;
&lt;p&gt;Web browsers aren't the only place caches are used. They are used everywhere from servers to computer hardware between the CPU and your hard disk/SSD. Getting things from a cache is quick, and so when you are getting something more than once, it can speed up a program a lot.&lt;/p&gt;
&lt;h2 id="what-does-lru-mean"&gt;What does LRU mean?&lt;a class="headerlink" href="#what-does-lru-mean" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A cache can only ever store a finite amount of things, and often is much smaller than whatever it is caching (for example, your hard drive is much smaller than the internet). This means that sometimes you will need to swap something that is already in the cache out for something else that you want to put in the cache. The way you decide what to take out is called a replacement strategy.&lt;/p&gt;
&lt;p&gt;That's where LRU comes in. LRU stands for Least Recently Used and is a commonly used replacement strategy for caches.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Why does a replacement strategy matter?&lt;/summary&gt;
&lt;p&gt;
A cache performs really well when it contains the thing you are trying to access, and not so well when it doesn't. The percentage of times that the cache contains the item you are looking for is called the hit rate. The primary factor in hit rate (apart from cache size) is replacement strategy.

Think about it this way: Using the browser example, if your most accessed site was `www.facebook.com` and your replacement strategy was to get rid of the most accessed site, then you are going to have a low hit rate. However if it was LRU, the hit rate would be much better.
&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;The idea behind Least Rececntly Used replacement is that if you haven't accessed something in a while, you probably won't any time soon. To use the strategy, you just get rid of the item that was used longest ago when the cache is full.&lt;/p&gt;
&lt;p&gt;&lt;img alt="LRU diagram" src="/images/lru.png"&gt;&lt;/p&gt;
&lt;p&gt;In the above diagram each item in the cache has an associated access time. LRU chooses the item at 2:55PM to be replaced since it was accessed longest ago. If there were two objects with the same access time, then LRU would pick one at random.&lt;/p&gt;
&lt;p&gt;It turns out that there is an optimal strategy for choosing what to replace in a cache and that is to get rid of the thing that won't be used for longest. This is called &lt;a href="https://en.wikipedia.org/wiki/Page_replacement_algorithm#The_theoretically_optimal_page_replacement_algorithm"&gt;Bélády's optimal algorithm&lt;/a&gt; but unfortunately requires knowing the future. Thankfully, in many situations LRU provides near optimal performance .&lt;/p&gt;
&lt;h2 id="how-do-i-use-it"&gt;How do I use it?&lt;a class="headerlink" href="#how-do-i-use-it" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;functools.lru_cache&lt;/code&gt; is a decorator, so you can just place it on top of your function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;

&lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The Fibonacci example is really commonly used here because the speed-up is so dramatic for so little effort. Running this on my machine, I got the following results for with and without cache versions of this function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;timeit&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;from fib_test import fib&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fib(30)&amp;#39;&lt;/span&gt;
&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;loops,&lt;span class="w"&gt; &lt;/span&gt;best&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;282&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;msec&lt;span class="w"&gt; &lt;/span&gt;per&lt;span class="w"&gt; &lt;/span&gt;loop
$&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;timeit&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;from fib_test import fib_cache&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fib_cache(30)&amp;#39;&lt;/span&gt;
&lt;span class="m"&gt;10000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;loops,&lt;span class="w"&gt; &lt;/span&gt;best&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.0791&lt;span class="w"&gt; &lt;/span&gt;usec&lt;span class="w"&gt; &lt;/span&gt;per&lt;span class="w"&gt; &lt;/span&gt;loop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That's a &lt;strong&gt;3,565,107x&lt;/strong&gt; speed increase for a single line of code.&lt;/p&gt;
&lt;p&gt;Of course, I think it can be hard to see how you'd actually use this in practice, since it's quite rare to need to calculate the Fibonacci series. Going back to our example with web pages, we can take the slightly more realistic example of caching rendered templates.&lt;/p&gt;
&lt;p&gt;In server development, usually individual pages are stored as templates that have placeholder variables. For example, the following is a template for a page that displays the results of various football matches for a given day.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Matches for {{day}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;matches&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Home team&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Away team&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Score&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      {% for match in matches %}
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{match[&amp;quot;home&amp;quot;]}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{match[&amp;quot;away&amp;quot;]}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{match[&amp;quot;home_goals&amp;quot;]}} - {{match[&amp;quot;away_goals&amp;quot;]}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      {% endfor %}
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the template is rendered, it looks like the below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Match template rendered" src="/images/match.png"&gt;&lt;/p&gt;
&lt;p&gt;This is a prime target for caching because the results for each day won't change and it's likely that there will be multiple hits on each day. Below is a Flask app that serves this template. I've introduced a 50ms delay to simulate getting the match dictionary over a network/from a large database.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;match.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;match_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# simulate network/database delay&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;match_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/matches/&amp;lt;day&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;matches.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;requests&lt;/code&gt; to get three match days without caching takes on average 171ms running locally on my computer. This isn't bad, but we can do better, even considering the artificial delay.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/matches/&amp;lt;day&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;matches.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I've set &lt;code&gt;maxsize=4&lt;/code&gt; in this example, because my test script only gets the same three days and it's best to set a power of two. Using this makes the average come down to 13.7ms over 10 loops.&lt;/p&gt;
&lt;h2 id="anything-else-i-should-know"&gt;Anything else I should know?&lt;a class="headerlink" href="#anything-else-i-should-know" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://docs.python.org/3/library/functools.html#functools.lru_cache"&gt;Python docs&lt;/a&gt; are pretty good, but there are a few things worth highlighting.&lt;/p&gt;
&lt;h3 id="built-in-functions"&gt;Built in functions&lt;a class="headerlink" href="#built-in-functions" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The decorator comes with some built-in functions that you may find useful. &lt;code&gt;cache_info()&lt;/code&gt; will help you figure out how big &lt;code&gt;maxsize&lt;/code&gt; should be by giving you information on hits, misses and the current size of the cache. &lt;code&gt;cache_clear()&lt;/code&gt; will delete all elements in the cache.&lt;/p&gt;
&lt;h3 id="sometimes-you-shouldnt-use-a-cache"&gt;Sometimes you shouldn't use a cache&lt;a class="headerlink" href="#sometimes-you-shouldnt-use-a-cache" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In general a cache can only be used when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data doesn't change for the lifetime of the cache.&lt;/li&gt;
&lt;li&gt;The function will always return the same value for the same arguments (so &lt;code&gt;time&lt;/code&gt; and &lt;code&gt;random&lt;/code&gt; don't make sense to cache).&lt;/li&gt;
&lt;li&gt;The function has no side effects. If the cache is hit, then the function never gets called, so make sure you're not changing any state in it.&lt;/li&gt;
&lt;li&gt;The function doesn't return distinct mutable objects. For example, functions that return lists are a bad idea to cache since the reference to the list will be cached, not the list contents.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Whilst it's not suitable for every situation, caching can be a super simple way to gain a large performance boost, and &lt;code&gt;functools.lru_cache&lt;/code&gt; makes it even easier to use. If you're interested to learn more then check out some of the links below.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cachetools.readthedocs.io/en/stable/"&gt;More powerful caching library&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/python/cpython/blob/3.7/Lib/functools.py"&gt;Python 3.7 functools source&lt;/a&gt;&lt;/p&gt;</content><category term="Tutorials"></category><category term="Caching"></category><category term="Python"></category><category term="Performance"></category></entry><entry><title>Better parameter validation in Flask with marshmallow</title><link href="https://www.cameronmacleod.com/blog/better-validation-flask-marshmallow" rel="alternate"></link><published>2019-04-25T00:00:00+01:00</published><updated>2019-04-25T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2019-04-25:/blog/better-validation-flask-marshmallow</id><summary type="html">&lt;p&gt;Recently I've had two Flask projects with endpoints that take lots of parameters. While working on the first project, I noticed that I was writing a lot of code for validation in each method, and it ended up looking ugly and probably full of bugs. When I started the second …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently I've had two Flask projects with endpoints that take lots of parameters. While working on the first project, I noticed that I was writing a lot of code for validation in each method, and it ended up looking ugly and probably full of bugs. When I started the second project, I thought that there had to be a way to fix this, and it turns out that there was!&lt;/p&gt;
&lt;p&gt;To illustrate what I'm talking about, imagine you need to implement the following endpoint for a note taking app.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/api/note - POST

Parameters:
 &lt;span class="k"&gt;-&lt;/span&gt; title (str) No longer than 60 characters
 &lt;span class="k"&gt;-&lt;/span&gt; note (str) No longer than 1000 characters
 &lt;span class="k"&gt;-&lt;/span&gt; user_id (int) No smaller than 1
 &lt;span class="k"&gt;-&lt;/span&gt; time_created (datetime) Not in the future
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Flask without a library for validation, you might end up writing the following view function to implement this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/api/note&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_note&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# we don&amp;#39;t know that the &amp;#39;title&amp;#39; parameter exists yet&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# we have to do manual validation on business requirements&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# now we have to do it again?!&lt;/span&gt;
    &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;note&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# more validation&lt;/span&gt;

    &lt;span class="n"&gt;actually_create_note&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The two parameters I validated up there were strings which made this easier, the &lt;code&gt;integer&lt;/code&gt; parameter would have additionally needed a type check and don't even think about parsing and validating the &lt;code&gt;datetime&lt;/code&gt; there. The above code is long and very prone to bugs. Of course, you could abstract it all to cleaner methods, but that wouldn't solve the underlying problem of having to write all of this manually.&lt;/p&gt;
&lt;p&gt;Thankfully, it turns out there is a library that does validation like this straight out of the box. It's called &lt;a href="https://marshmallow.readthedocs.io/"&gt;marshmallow&lt;/a&gt; and is meant for object serialization. Alongside parsing and dumping, it also comes with some powerful validation functionality built-in.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;What is object serialization?&lt;/summary&gt;
&lt;p&gt;
[Serialization](https://en.wikipedia.org/wiki/Serialization) is the process of converting objects and data from the format used internally in your program into a format that can be stored or transmitted. For example, JSON data can be represented and easily accessed as a dictionary in Python, but it needs to be **serialized** to a string to send it anywhere. The reverse operation is called **deserialization** and is what we'll be dealing with in this article.
&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;The core idea in marshmallow is that data structure is represented with a schema. A schema is a class that defines what format the data comes in. It dictates what fields exist, their types and validation on them. You create a schema by sub-classing &lt;code&gt;marshmallow.Schema&lt;/code&gt; and creating attributes that will represent the fields in your data.&lt;/p&gt;
&lt;p&gt;Using the note-taking endpoint as an example, we'll create a schema that represents the structure of incoming data to the endpoint.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;marshmallow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateNoteInputSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; /api/note - POST&lt;/span&gt;

&lt;span class="sd"&gt;    Parameters:&lt;/span&gt;
&lt;span class="sd"&gt;     - title (str)&lt;/span&gt;
&lt;span class="sd"&gt;     - note (str)&lt;/span&gt;
&lt;span class="sd"&gt;     - user_id (int)&lt;/span&gt;
&lt;span class="sd"&gt;     - time_created (time)&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c1"&gt;# the &amp;#39;required&amp;#39; argument ensures the field exists&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time_created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a pretty simple class, but already it contains a lot of magic. This will check both existence of fields and their types for you. It's important to note that it won't do any business logic validation yet. You can use this in a view function like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;create_note_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CreateNoteInputSchema&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/api/note&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_note&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_note_schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# now all required fields exist and are the right type&lt;/span&gt;
    &lt;span class="c1"&gt;# business requirements aren&amp;#39;t necessarily satisfied (length, time bounds, etc)&lt;/span&gt;
    &lt;span class="n"&gt;actually_create_note&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We don't have all of the functionality we need, but even still this has cleaned up the code considerably. The other thing we need to do is to add validation methods for the business requirements. You can do this in two ways with marshmallow. Firstly you could create a method in your schema that has the &lt;code&gt;@validates&lt;/code&gt; decorator, or for simple cases, you could give the &lt;code&gt;validate&lt;/code&gt; keyword argument to the field.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;marshmallow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;
&lt;span class="c1"&gt;# import built-in validators&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;marshmallow.validate&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Range&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateNoteInputSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# no longer than 60 chars&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# no longer than 1000 chars&lt;/span&gt;
    &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# at least 1&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;time_created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will notice above that marshmallow comes with a bunch of handy validators built-in. You can see a full list of them in the &lt;a href="https://marshmallow.readthedocs.io/en/3.0/api_reference.html#module-marshmallow.validate"&gt;API docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We're still missing a validator for checking that the date is not in the future. Luckily we can use the &lt;code&gt;@validates&lt;/code&gt; decorator to write our own.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;marshmallow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateNoteInputSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;time_created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@validates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;time_created&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_not_in_future&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;#39;value&amp;#39; is the datetime parsed from time_created by marshmallow&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Can&amp;#39;t create notes in the future!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# if the function doesn&amp;#39;t raise an error, the check is considered passed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We don't even have to use any extra code in our view function now to use the extra validation, we still just call the &lt;code&gt;validate&lt;/code&gt; method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/api/note&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_note&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_note_schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;actually_create_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="extra-information"&gt;Extra information&lt;a class="headerlink" href="#extra-information" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;marshmallow is very powerful and contains much more than what I have covered here. Thankfully there are many good resources on the internet for you to research further.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marshmallow.readthedocs.io/en/3.0/quickstart.html"&gt;marshmallow Quickstart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flask-marshmallow.readthedocs.io/en/latest/"&gt;Flask-Marshmallow&lt;/a&gt; - An integration library that adds extra functionality (automatic schemas from models, extra field types, etc)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codementor.io/dongido/how-to-build-restful-apis-with-python-and-flask-fh5x7zjrx"&gt;How to Build RESTful APIs with Python and Flask | Codementor&lt;/a&gt; - A wider look at API building as opposed to just the validation.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Tutorials"></category><category term="Python"></category><category term="Flask"></category><category term="marshmallow"></category><category term="Web apps"></category></entry><entry><title>How to configure DNS for custom domains on GitHub Pages</title><link href="https://www.cameronmacleod.com/blog/github-pages-dns" rel="alternate"></link><published>2018-11-20T00:00:00+00:00</published><updated>2018-11-20T00:00:00+00:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2018-11-20:/blog/github-pages-dns</id><summary type="html">&lt;p&gt;Recently-ish GitHub &lt;a href="https://blog.github.com/2018-05-01-github-pages-custom-domains-https/"&gt;announced&lt;/a&gt; that HTTPS would now work on custom domains with GitHub Pages. This was a great bit of news, because the web is slowly moving towards a HTTPS-only state of being and it's nice not to be left behind.&lt;/p&gt;
&lt;p&gt;Unfortunately for me, for this new feature to work …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently-ish GitHub &lt;a href="https://blog.github.com/2018-05-01-github-pages-custom-domains-https/"&gt;announced&lt;/a&gt; that HTTPS would now work on custom domains with GitHub Pages. This was a great bit of news, because the web is slowly moving towards a HTTPS-only state of being and it's nice not to be left behind.&lt;/p&gt;
&lt;p&gt;Unfortunately for me, for this new feature to work you had to be using either &lt;strong&gt;CNAME&lt;/strong&gt; or &lt;strong&gt;ALIAS records&lt;/strong&gt; to point to your GitHub Pages domain. At the time, I was using &lt;strong&gt;A records&lt;/strong&gt; pointed at the old GitHub Pages servers, which didn't have the new features enabled. All these terms can be quite confusing if you're not familiar with DNS, so let's take a look at some basics.&lt;/p&gt;
&lt;h3 id="what-is-dns"&gt;What is DNS?&lt;a class="headerlink" href="#what-is-dns" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;DNS stands for Domain Name System and it's the internet's way of mapping &lt;strong&gt;URLs&lt;/strong&gt; (like &lt;code&gt;cameronmacleod.com&lt;/code&gt;) to the actual &lt;strong&gt;IP addresses&lt;/strong&gt; (like &lt;code&gt;185.199.108.153&lt;/code&gt;) of the computers that can serve you the website. By means of an analogy, a &lt;strong&gt;URL&lt;/strong&gt; is a bit like a postal address, for example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1 Castlehill&lt;br&gt;
Edinburgh&lt;br&gt;
EH1 2NG&lt;br&gt;
United Kingdom  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unless you have been to Edinburgh, you would have no idea how to get to that address. You'd need something to take that address and give you directions back. Similarly, if you give a browser a &lt;strong&gt;URL&lt;/strong&gt; then it would need something (in this case, a &lt;strong&gt;nameserver&lt;/strong&gt;) to give it directions (an &lt;strong&gt;IP address&lt;/strong&gt;) to find a website. Just as humans need directions to find a place, browsers need IP addresses to find a website.&lt;/p&gt;
&lt;p&gt;DNS is the system by which these &lt;strong&gt;URLs&lt;/strong&gt; are translated into &lt;strong&gt;IP addresses&lt;/strong&gt;. For a basic mental model of how it works, you can think of these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;browser&lt;/strong&gt; asks a &lt;strong&gt;nameserver&lt;/strong&gt; for the &lt;strong&gt;IP address&lt;/strong&gt; related to a &lt;strong&gt;URL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;nameserver&lt;/strong&gt; looks through some information it holds known as &lt;strong&gt;records&lt;/strong&gt; to find out&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;nameserver&lt;/strong&gt; then tells the &lt;strong&gt;browser&lt;/strong&gt; the &lt;strong&gt;IP&lt;/strong&gt; and the &lt;strong&gt;browser&lt;/strong&gt; goes away and fetches the website&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A &lt;strong&gt;nameserver&lt;/strong&gt; is just the DNS name for a computer that keeps &lt;strong&gt;records&lt;/strong&gt; and can do the job of translating &lt;strong&gt;URLs&lt;/strong&gt; to &lt;strong&gt;IP addresses&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is not comprehensive or 100% accurate, but it's a good enough model for our purposes. If you would like to know more, or you're still a bit confused then Cloudflare have a &lt;a href="https://www.cloudflare.com/learning/dns/what-is-dns/"&gt;really good intro to the topic&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="what-are-dns-records"&gt;What are DNS records?&lt;a class="headerlink" href="#what-are-dns-records" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So far, we know that things called &lt;strong&gt;records&lt;/strong&gt; store the mapping between &lt;strong&gt;URLs&lt;/strong&gt; and &lt;strong&gt;IP addresses&lt;/strong&gt;. Presumably this means that if we want to make our site point at the GitHub Pages servers, then we need to change these. But what are &lt;strong&gt;records&lt;/strong&gt; and how do they work?&lt;/p&gt;
&lt;p&gt;At their core, &lt;strong&gt;records&lt;/strong&gt; are just text files written in a certain way, that tell the &lt;strong&gt;nameserver&lt;/strong&gt; how to find the &lt;strong&gt;IP address&lt;/strong&gt; for a certain website. There are multiple different types of DNS &lt;strong&gt;records&lt;/strong&gt; and all of them serve a slightly different purpose. Alongside the information below, all records have a &lt;code&gt;TTL&lt;/code&gt; or &lt;code&gt;Time To Live&lt;/code&gt; value that contains the number of seconds that the record is still valid for.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A records&lt;/strong&gt; - These are possibly the simplest type of DNS records and hold the IP address for either a sub-domain (like &lt;code&gt;blog.cameronmacleod.com&lt;/code&gt;) or your root domain (like &lt;code&gt;cameronmacleod.com&lt;/code&gt;). Similarly, you may see &lt;strong&gt;AAAA records&lt;/strong&gt; which are the same, but they hold an &lt;a href="https://en.wikipedia.org/wiki/IPv6"&gt;IPv6 address&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CNAME records&lt;/strong&gt; - &lt;strong&gt;CNAME&lt;/strong&gt; stands for &lt;strong&gt;C&lt;/strong&gt;anonical &lt;strong&gt;N&lt;/strong&gt;ame and points one domain name to another. The idea behind these records is to have a single official domain name and to let lots of other domains point to it. They are frequently used to redirect &lt;code&gt;www.&lt;/code&gt; subdomains to the root domain. e.g. &lt;code&gt;www.cameronmacleod.com -&amp;gt; cameronmacleod.com&lt;/code&gt;. They can only be used on sub-domains, not on root domains.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ALIAS records&lt;/strong&gt; - These are similar to &lt;strong&gt;CNAME records&lt;/strong&gt; in that they point your domain to another domain but there are a couple of subtle differences. &lt;strong&gt;ALIAS records&lt;/strong&gt; can only be used on the root domain, not sub-domains, and they are not standardised across all DNS providers, so yours may or may not support them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Other record types&lt;/strong&gt; - There are many other record types, including &lt;strong&gt;MX&lt;/strong&gt; for pointing to mail servers, &lt;strong&gt;NS&lt;/strong&gt; for nameserver data and even &lt;strong&gt;TXT&lt;/strong&gt; for general notes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="how-can-i-change-my-dns-records"&gt;How can I change my DNS records?&lt;a class="headerlink" href="#how-can-i-change-my-dns-records" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you know your DNS provider already, then great! If not, you will need to either figure out which one you are using, or set one up if your site is not yet live. For those of you who have yet to set up DNS, often your domain name provider will have a DNS service as part of purchasing your domain name, if not then there are many free DNS providers out there.&lt;/p&gt;
&lt;p&gt;If you (like me) can't remember what DNS provider you used, then a really useful tool can be found &lt;a href="https://www.dnsstuff.com/tools"&gt;from DNSstuff&lt;/a&gt;. On that page there is a "DNS Lookup" tool that you can type your domain into.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DNS lookup tool screenshot" src="/images/dnsstufflookup.png"&gt;&lt;/p&gt;
&lt;p&gt;On the page that follows, under the heading "Referral path:" you will see the words "Response from &lt;strong&gt;some.dns.server&lt;/strong&gt;" where &lt;strong&gt;some.dns.server&lt;/strong&gt; is your DNS server. You can then type that URL into Google and the results should tell you your provider.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DNS lookup results screenshot" src="/images/dnslookupresults.png"&gt;&lt;/p&gt;
&lt;p&gt;The actual process of changing your DNS records varies from provider to provider, but most will have an admin panel that you can log into and add/remove records. The records I have configured for my GitHub pages domain and by extension ones that should work for your GitHub Pages site are as follows:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Host name&lt;/th&gt;
&lt;th&gt;Points to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;www&lt;/td&gt;
&lt;td&gt;notexactlyawe.github.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALIAS&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;notexactlyawe.github.io&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The first is necessary to redirect &lt;code&gt;www.cameronmacleod.com&lt;/code&gt; to the GitHub Pages servers and the second redirects the "apex" domain (&lt;code&gt;cameronmacleod.com&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;When you come to configure this for yourself, you may find that there are no &lt;strong&gt;ALIAS records&lt;/strong&gt; available under your provider. Unfortunately your best option in this case may be to change DNS provider.&lt;/p&gt;
&lt;h3 id="how-do-i-know-it-worked"&gt;How do I know it worked?&lt;a class="headerlink" href="#how-do-i-know-it-worked" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;DNS is notoriously slow to make changes to. Most providers recommend waiting 24 hours (some up to 72!) before you will be able to see your changes. When checking to see whether or not your changes have propagated, you can do one of two things. Use a DNS tool (like the one from &lt;a href="https://www.dnsstuff.com/tools"&gt;DNSstuff&lt;/a&gt; above) or use the unix command &lt;code&gt;dig&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dig&lt;/code&gt; is a command that can find and list the DNS records for a given domain name. On Linux and Mac machines, you can run the following in terminal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cameron&lt;/span&gt;&lt;span class="nv"&gt;@isla&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameronmacleod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;noall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;

&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DiG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;9.10.3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;P4&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Ubuntu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameronmacleod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;noall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;
&lt;span class="p"&gt;;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;
&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameronmacleod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;655&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;192.30.252.154&lt;/span&gt;
&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameronmacleod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;655&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;192.30.252.153&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output might look a bit confusing, but it's the last two lines that count. Those are the records that &lt;code&gt;dig&lt;/code&gt; has found for your domain. In my case, this output was from before I changed my DNS settings, so it still shows the old &lt;strong&gt;A records&lt;/strong&gt; that I was using. Running it now would give something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cameron&lt;/span&gt;&lt;span class="nv"&gt;@isla&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameronmacleod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;noall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;

&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DiG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;9.10.3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;P4&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Ubuntu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameronmacleod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;noall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;
&lt;span class="p"&gt;;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;
&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameronmacleod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ow"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CNAME&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;notexactlyawe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;notexactlyawe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3599&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;185.199.108.153&lt;/span&gt;
&lt;span class="n"&gt;notexactlyawe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3599&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;185.199.109.153&lt;/span&gt;
&lt;span class="n"&gt;notexactlyawe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3599&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;185.199.111.153&lt;/span&gt;
&lt;span class="n"&gt;notexactlyawe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3599&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ow"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;185.199.110.153&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You might notice that there are a lot of &lt;strong&gt;A records&lt;/strong&gt;, but that is because &lt;strong&gt;ALIAS records&lt;/strong&gt; will resolve automatically to &lt;strong&gt;A records&lt;/strong&gt; in the DNS server, meaning that when you run &lt;code&gt;dig&lt;/code&gt; it just shows you the final output.&lt;/p&gt;
&lt;p&gt;You will want to check that the &lt;strong&gt;IP addresses&lt;/strong&gt; in the last column of the &lt;code&gt;dig&lt;/code&gt; output look similar to the ones above. For a complete and up to date list of GitHub Pages &lt;strong&gt;IP addresses&lt;/strong&gt;, you can check &lt;a href="https://help.github.com/articles/setting-up-an-apex-domain/#configuring-a-records-with-your-dns-provider"&gt;their documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If this worked, then great! Your DNS is properly configured to redirect your custom domain to GitHub Pages, and you should be able to visit your domain in a browser and see your page being served!&lt;/p&gt;
&lt;h3 id="https-for-custom-domains-in-github-pages"&gt;HTTPS for custom domains in GitHub Pages&lt;a class="headerlink" href="#https-for-custom-domains-in-github-pages" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you are trying to set up HTTPS on your site, then the changes above are necessary because they will point you at GitHub's content delivery network which has support for HTTPS. See &lt;a href="https://blog.github.com/2018-05-01-github-pages-custom-domains-https/"&gt;GitHub's announcement&lt;/a&gt; for more details. You will also need to perform a couple more steps.&lt;/p&gt;
&lt;p&gt;Firstly, if you used to be using &lt;strong&gt;A records&lt;/strong&gt; then you may need to remove and re-add your custom subdomain from your repository's settings. This will generate you a certificate for your domain. To do this, firstly go to your repository settings.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of repository settings" src="/images/repositorysettings.png"&gt;&lt;/p&gt;
&lt;p&gt;Next, head to the GitHub Pages settings and remove your domain from the "Custom domain" box and click "Save". Put your domain back in that box and click Save again.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of GitHub Pages settings" src="/images/githubpagessettings.png"&gt;)&lt;/p&gt;
&lt;p&gt;After doing this you will need to wait about an hour, but after that you should be able to go to &lt;code&gt;https://siteinthecustomdomainbox.com&lt;/code&gt; and see a green padlock in the address bar. If the domain you put in the "Custom domain" box had a &lt;code&gt;www.&lt;/code&gt; in front of it, then you will need to add that to the URL when visiting your site else it won't work.&lt;/p&gt;
&lt;p&gt;For full and up to date instructions on the above see &lt;a href="https://help.github.com/articles/adding-or-removing-a-custom-domain-for-your-github-pages-site/"&gt;GitHub's documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One thing to note is that HTTPS will only work for either &lt;code&gt;www.yoursite.com&lt;/code&gt; or &lt;code&gt;yoursite.com&lt;/code&gt; but not both depending on which one you put in the "Custom domain" box. GitHub are aware of the issue, but haven't given any public timeline for fixing it, see &lt;a href="https://github.community/t5/GitHub-Pages/Does-GitHub-Pages-Support-HTTPS-for-www-and-subdomains/td-p/7116"&gt;this GitHub Community thread&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="if-something-didnt-work"&gt;If something didn't work&lt;a class="headerlink" href="#if-something-didnt-work" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;GitHub have a really useful &lt;a href="https://help.github.com/articles/troubleshooting-custom-domains/"&gt;troubleshooting guide&lt;/a&gt; for custom domains and HTTPS.&lt;/p&gt;</content><category term="Tutorials"></category><category term="Github Pages"></category><category term="DNS"></category></entry><entry><title>5 Mistakes companies make recruiting software interns</title><link href="https://www.cameronmacleod.com/blog/recruiting-software-interns" rel="alternate"></link><published>2018-11-14T00:00:00+00:00</published><updated>2018-11-14T00:00:00+00:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2018-11-14:/blog/recruiting-software-interns</id><summary type="html">&lt;p&gt;August to November is internship application season for computer science undergraduates and over the past few months I've been applying to a good number of internships myself. Through these applications I've learnt a few things about what makes for a good recruitment process and what doesn't and I hope to …&lt;/p&gt;</summary><content type="html">&lt;p&gt;August to November is internship application season for computer science undergraduates and over the past few months I've been applying to a good number of internships myself. Through these applications I've learnt a few things about what makes for a good recruitment process and what doesn't and I hope to share a few of them here.&lt;/p&gt;
&lt;p&gt;Having a good process is really important in software engineering roles because candidates hold so much power. A good candidate will be progressing with at least 3 or 4 companies that are likely to hire them at any one time. If they find any reason to dismiss your company, then they will. Below I've listed five common mistakes I've seen in recruiting processes and why each one is likely to lose you a good candidate.&lt;/p&gt;
&lt;h1 id="1-timing-the-process-wrong"&gt;1. Timing the process wrong&lt;a class="headerlink" href="#1-timing-the-process-wrong" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The big companies that recruit computer science interns (Google, Amazon, Facebook etc) tend to start their processes around September. This means that all the candidates they wanted have been snapped up by December at the latest. If you are starting your recruiting process any later than September, then you are losing a lot of good candidates to bigger companies that got in earlier.&lt;/p&gt;
&lt;p&gt;The earlier you list your positions, the better. If a candidate receives an offer from you whilst they are only in an early stage of the process with other companies, then they are much more likely to take your position. Of course, the same applies to other companies, which makes it even more necessary to get in early.&lt;/p&gt;
&lt;p&gt;All this is not to say that there aren't still good candidates left later on, it's just that you will have a harder time finding them. When a candidate receives an offer, they're likely to take it.&lt;/p&gt;
&lt;h1 id="2-bad-communication"&gt;2. Bad communication&lt;a class="headerlink" href="#2-bad-communication" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;There is nothing worse from a candidate's perspective than not hearing anything back from an application. Not being clear on what happens next in a recruitment process comes in a close second. It's crucial when recruiting to be prompt in replying to candidates and exceedingly clear on what the process will entail.&lt;/p&gt;
&lt;p&gt;Often companies make this mistake by opening applications for a position, stockpiling them and then come back to review them a few weeks later. Alongside being an unpleasant candidate experience, this will also lose you candidates to other companies that have reviewed and made offers faster.&lt;/p&gt;
&lt;p&gt;In terms of making sure the candidate is aware of what the process entails, it's generally a good idea to let them know as early as possible in an email that lists the steps, and then reinforce this at the end of every step. Candidates really appreciate having all the information and will reward you for your effort with a better image of your organisation.&lt;/p&gt;
&lt;h1 id="3-unhelpful-interviews"&gt;3. Unhelpful interviews&lt;a class="headerlink" href="#3-unhelpful-interviews" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;An interview is your best chance to get to know the candidate and their experience. Something that's often overlooked, however, is that it's also your best chance to sell the candidate your position.&lt;/p&gt;
&lt;p&gt;Lists of what is important to software engineers in their job often start with working with smart colleagues. For the candidate to know that your team are excellent at what they do, it's important that the questions they get asked in interview are smart and relevant. Asking candidates to recite some facts from a textbook is unhelpful for you and them, but asking them to solve a problem that's relevant to your business both gives you greater insight into them and demonstrates your team's skill.&lt;/p&gt;
&lt;h1 id="4-not-reimbursing-travel"&gt;4. Not reimbursing travel&lt;a class="headerlink" href="#4-not-reimbursing-travel" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Hiring software engineers is expensive. The amount of recruiting and engineering time that goes into a single candidate is huge. But the reason for this is that hiring the wrong engineer is even more expensive. So why would you vastly narrow your pool of candidates and exclude many good engineers by not paying for a train fare or flight? The price will pale in comparison to what you're already spending on the candidate. Personally I would not attend an interview without travel reimbursement and I know many people that feel the same.&lt;/p&gt;
&lt;p&gt;This is also a problem in terms of recruiting diverse candidates. I'm sure that I don't need to go into the benefits of a diverse workforce, but not reimbursing travel expenses reduces the diversity of your talent pool. Only candidates from the local area, or those who can afford to travel would then attend. Assuming that you'll find enough good engineers this way can be a costly mistake.&lt;/p&gt;
&lt;h1 id="5-ignoring-early-year-undergraduates"&gt;5. Ignoring early year undergraduates&lt;a class="headerlink" href="#5-ignoring-early-year-undergraduates" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This is quite possibly the most common mistake I see companies making today. A candidate not in their penultimate year of study can have an awful experience with all the auto-rejections and non-replies sent their way. This is a real shame because there are a lot of really good candidates that get ignored by companies that prefer to hire penultimate year students.&lt;/p&gt;
&lt;p&gt;Understandably, some companies run internships for the express purpose of channelling interns into their graduate program. The argument goes that candidates are more likely to return for a graduate job if they get the offer just after their internship ends and you can only offer graduate jobs to students expecting to graduate the following year. Whilst this is true, I would disagree with this implying excluding earlier year candidates because in my opinion the early bird gets the worm. If you are a company that doesn't ignore non-penultimate year students then those early students will have a more favourable image of you. On top of this many interns return for another internship at the same company, meaning you still get the funnelling effect, just they're even more productive when they join full time due to the extra experience working in your environment.&lt;/p&gt;
&lt;p&gt;It's also worth mentioning that a lot of the most talented students I know are not penultimate-year. Whilst university is valuable, some students have been programming for years beforehand and come into university well above their expected level. It would be a shame to ignore these candidates, especially at a time in their career when other companies are less interested.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start looking early  &lt;/li&gt;
&lt;li&gt;Review applications as they come in  &lt;/li&gt;
&lt;li&gt;Be clear with how the process works  &lt;/li&gt;
&lt;li&gt;Put a lot of thought into your interview questions  &lt;/li&gt;
&lt;li&gt;Reimburse travel (at least nationally)  &lt;/li&gt;
&lt;li&gt;Don't ignore first and second years  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Internships are a fantastic thing for all parties involved. Interns get great experience and companies get access to some of the smartest engineers at an early stage in their career. Getting the recruiting process right can lead to great things for your organisation, and it's a shame to see simple mistakes being made.&lt;/p&gt;
&lt;p&gt;If you found this useful or interesting, why not &lt;a href="https://twitter.com/notexactlyawe"&gt;follow me on Twitter&lt;/a&gt;?&lt;/p&gt;</content><category term="Blogging"></category><category term="Career"></category><category term="Recruiting"></category></entry><entry><title>How we ran Welcome Week events</title><link href="https://www.cameronmacleod.com/blog/running-freshers-events" rel="alternate"></link><published>2018-10-06T00:00:00+01:00</published><updated>2018-10-06T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2018-10-06:/blog/running-freshers-events</id><summary type="html">&lt;p&gt;At the University of Edinburgh we call the week before lectures begin each year Welcome Week. It's designed as a time for new students to meet each other and get settled into university and often a new city. Most societies will run events to try and attract new students to …&lt;/p&gt;</summary><content type="html">&lt;p&gt;At the University of Edinburgh we call the week before lectures begin each year Welcome Week. It's designed as a time for new students to meet each other and get settled into university and often a new city. Most societies will run events to try and attract new students to join them and we (&lt;a href="http://ears-edi.com/"&gt;EaRS&lt;/a&gt;) were no different. After all, a society dies when no-one is interested any more.&lt;/p&gt;
&lt;p&gt;I wanted to write down some of our experience running events during Welcome Week because it is different to running events any other time of the year and it may be useful to anyone who is new to running a society in future. If you are not running a society, hopefully it should be an interesting insight into what goes on behind the scenes.&lt;/p&gt;
&lt;p&gt;We ran three separate events during Welcome Week 2018. There was a pub quiz, a workshop and an evening of board games. We attempted to make each event as inclusive to various skill levels as possible, because we wanted to attract first year undergraduates and the level of technical knowledge amongst new students varies wildly.&lt;/p&gt;
&lt;h2 id="choosing-events-to-run"&gt;Choosing events to run&lt;a class="headerlink" href="#choosing-events-to-run" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It's important to be as inclusive as possible during your first events as you want to encourage people to join whatever their experience level. For this reason, we chose to run two socials alongside our workshop, which everyone could turn up to and didn't reward technical knowledge to level the playing field. The workshop was aimed squarely at beginners as well. This is especially important for technical societies like ours, since we can often seem intimidating to beginners.&lt;/p&gt;
&lt;h4 id="mistake-1-wrong-level-of-workshop"&gt;Mistake #1 - Wrong level of workshop&lt;a class="headerlink" href="#mistake-1-wrong-level-of-workshop" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The workshop that we ran was aimed at complete programming beginners. We decided to do this since we weren't sure what skill level the freshers coming in would be at. Unfortunately we ended up attracting a lot of experienced people to the workshop and had to split it into two sessions at the same time; one for beginners and one for more advanced people. This made it harder to run the workshop as we could give less attention to each group.&lt;/p&gt;
&lt;h6 id="potential-solutions"&gt;Potential solutions&lt;a class="headerlink" href="#potential-solutions" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;More heavily emphasise the beginner nature of the event in the advertising.&lt;/li&gt;
&lt;li&gt;Advertise the event through other tangentially related societies with potential interests in learning to code. This would attract people that wouldn't have gone out of their way to find a technical society like ours.&lt;/li&gt;
&lt;li&gt;Keep the event at a beginner level, but in a less popular topic such as electronics or mechanical engineering.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="advertising-events"&gt;Advertising events&lt;a class="headerlink" href="#advertising-events" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our student union is the best place to advertise fresher's events in Edinburgh as they put a lot of money into printing booklets and directing students towards the places to find these events. Because we submitted late we didn't get to make full use of this, but we still got a few sign-ups from the online version of their events brochure.&lt;/p&gt;
&lt;h4 id="mistake-2-late-submission-to-the-student-union"&gt;Mistake #2 - Late submission to the student union&lt;a class="headerlink" href="#mistake-2-late-submission-to-the-student-union" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This mistake fell squarely on my shoulders. In April before the last academic year ended, our student union sent us an email with details on how to include our Welcome Week events in their advertising. I didn't pay enough attention to the email to see this and so we missed the deadline for submission by a few months.&lt;/p&gt;
&lt;h5 id="potential-solutions_1"&gt;Potential solutions&lt;a class="headerlink" href="#potential-solutions_1" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Read all emails from the student union.&lt;/li&gt;
&lt;li&gt;Find a better secretary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite this, the channel that gained us the most sign-ups to the events was without a doubt our Activities Fair, which is an event every year where societies gather in an expo format to advertise to new students. Since we scheduled all our events for after the fair, we were able to direct people to them at the fair and a lot of the people present at the events found out there.&lt;/p&gt;
&lt;p&gt;Facebook was also a very useful tool for us due to the network effects. If one person is interested in our events, then there's a good chance that their friends are too, so it acts like improved word of mouth advertising. There's no need to spend money on it if you build your page using off-line methods.&lt;/p&gt;
&lt;p&gt;We also used our mailing list from last year, but that generally produces quite low engagement rates.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Make sure to delegate&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Having someone responsible for marketing and another person responsible for each individual event helps enormously. The year before, there were only 3 of us and the events wouldn't have run if we didn't draft some friends in. This year having 9 members on the committee in specific roles made things a whole lot easier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;We had great fun during Welcome Week, and despite the mistakes we made it was very successful for the society. The turn-out to events was great and we managed to increase our reach with mailing lists and Facebook dramatically!&lt;/p&gt;
&lt;p&gt;Questions? Contact me using the links on the left!&lt;/p&gt;</content><category term="Blogging"></category><category term="Societies"></category><category term="Events"></category></entry><entry><title>Diagnosing performance issues in a Flask app</title><link href="https://www.cameronmacleod.com/blog/sqlalchemy-speed" rel="alternate"></link><published>2018-04-22T00:00:00+01:00</published><updated>2018-04-22T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2018-04-22:/blog/sqlalchemy-speed</id><summary type="html">&lt;p&gt;I was recently part of the team that ran &lt;a href="https://createdhack.github.io"&gt;CreatED&lt;/a&gt;, the UK’s first hardware hackathon of its kind. Organising it was stressful at times and incredibly rewarding at others, especially when it came to the event actually happening. During the run up to the event, we learned a lot …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I was recently part of the team that ran &lt;a href="https://createdhack.github.io"&gt;CreatED&lt;/a&gt;, the UK’s first hardware hackathon of its kind. Organising it was stressful at times and incredibly rewarding at others, especially when it came to the event actually happening. During the run up to the event, we learned a lot about what actually needs to happen for a hackathon to run and one of those things was the setting up of event software.&lt;/p&gt;
&lt;p&gt;It’s easy to overlook when you’re thinking about running your first hackathon, but things like registration systems, mentor allocation systems and hardware distribution systems all need to be put in place. Since we were a hardware hackathon, the last one was of particular importance to us. Thankfully HackMIT and MakeMIT, mostly through &lt;a href="http://noahmoroze.com/"&gt;Noah Moroze&lt;/a&gt; had already put together a fantastic system for this, &lt;a href="https://github.com/techx/cog"&gt;Cog&lt;/a&gt;, which had been battle tested at much larger hackathons than ours.&lt;/p&gt;
&lt;p&gt;Cog was a great gift to us, as we’d only started to think about a system for this quite late in the game (2 or 3 weeks to go) and it had pretty much all of the functionality we needed. There were, however, a few things that needed to be added and so I set about doing so.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We needed MyMLH, an OAuth provider, integrated since Cog only worked with HackMIT’s Quill registration system which we weren't using.&lt;/li&gt;
&lt;li&gt;We needed participants to be able to upload CVs since Eventbrite had no functionality for this and it had been promised to sponsors.&lt;/li&gt;
&lt;li&gt;We wanted a list of admin emails to be recognised by Cog so that not everyone had to use the same credentials.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of these were quite easy to integrate, thanks to some logical design on the part of the team at MIT. The CV upload functionality took me longer than the others though mostly due to my inexperience with front-end development. Before I was comfortable signing this off for use during the hack (due to it being so critical), I wanted to do some manual testing, and part of that was load testing since I was worried about a huge amount of users at the start of the hackathon.&lt;/p&gt;
&lt;p&gt;I wrote up a quick script that started up a number of threads and used them all to fire a request at the home page of the application in a staggered fashion, hoping to simulate actual usage.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;threading&lt;/span&gt;

&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;http://localhost:5000&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;time_to_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_users&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;t_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_users&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;time_to_get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;t_pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Waiting&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;t_pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;load_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The results I got from this quick test were mildly alarming. Each of these times (in seconds) is how long it took to receive a full response from the server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cameron&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;isla&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cog&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;load_test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="n"&gt;Waiting&lt;/span&gt;
&lt;span class="mf"&gt;1.971983&lt;/span&gt;
&lt;span class="mf"&gt;3.797295&lt;/span&gt;
&lt;span class="mf"&gt;5.694615&lt;/span&gt;
&lt;span class="mf"&gt;7.800551&lt;/span&gt;
&lt;span class="mf"&gt;9.669649&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To put this into context, this isn’t a particularly demanding page. It displays to the user a list of available hardware and does little else.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The main page for Cog" src="/images/cog_main_page.png" title="The main page for Cog"&gt;&lt;/p&gt;
&lt;p&gt;However, I wasn’t sure whether the problem was with my script or with Cog itself. To investigate this, I started Googling around profiling in Flask (the framework Cog is written in) and &lt;a href="https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xvi-debugging-testing-and-profiling"&gt;this article&lt;/a&gt; pointed me to some &lt;a href="http://werkzeug.pocoo.org/docs/0.14/contrib/profiler/"&gt;Werkzeug middleware&lt;/a&gt; that did what I wanted. The output is below from one of the endpoint calls - pay attention to the top call.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;1761654&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1645404&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;primitive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;2.829&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Ordered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reduced&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1183&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;due&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;restriction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;ncalls&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;tottime&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;percall&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;cumtime&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;percall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;759&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.375&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.377&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;4536&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.062&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.280&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cameron&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cog&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;python2&lt;/span&gt;&lt;span class="m m-Double"&gt;.7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;898&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;6426&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.056&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.070&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m m-Double"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cameron&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cog&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;python2&lt;/span&gt;&lt;span class="m m-Double"&gt;.7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;674&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The 2.829 seconds it took to serve the page suggested that the problem was somewhere in the application as opposed to my script and looked at the output. The most suspicious line to my eyes was the one that ended &lt;code&gt;{method 'execute' of 'psycopg2.extensions.cursor' objects}&lt;/code&gt;, mostly because it was the most expensive, but also because it meant the database calls were taking a long time to execute. After a while going down the rabbit hole of trying different deployment options with the database in case the problem was with the database itself I went back and saw the &lt;code&gt;ncalls&lt;/code&gt; column had 759 in it. This was fishy as all the page was doing was displaying a list of hardware available to the user, which shouldn’t have taken more than a couple of queries at most.&lt;/p&gt;
&lt;p&gt;I was stumped as to what could be making this many queries in the application as the code all looked sensible, so I set SQLAlchemy to echo all queries to see whether I could see a pattern. Sparing you the output of 759 queries, a lot of COUNTs were popping up suggesting that the problem may be in the quantities of items since those were the only numbers on the page. Sure enough, looking at the code I found this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InventoryEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Returns quantity of items that have not been &amp;#39;claimed&amp;#39; by a request&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; \
                   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
                   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hardwarecheckout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
                   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hardwarecheckout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APPROVED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
                   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_entities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scalar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the database, there was a table for inventory entries (roughly corresponding to 'classes' of items) and items (think instances of these classes). When calculating how many of a particular inventory entry was available it would check how many of an inventory entry existed in total and then subtract the number of approved requests for that item. This looks fine at first glance, and indeed it gives the correct result. However the issue is that this is calculated on a per-item basis, meaning each item makes 2 queries, when this could be done in a single query on a table basis.&lt;/p&gt;
&lt;p&gt;The solution comes through the use of the GROUP BY clause. The following code selects all items that no user has claimed and groups them by the ID in &lt;code&gt;inventory_entry&lt;/code&gt; before counting the groups (effectively returning how many of each inventory entry are free). The bottom line is a dictionary comprehension that puts them in a nice format for the Jinja template.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;\
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;\
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;\
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The one important thing to note with this code is that &lt;code&gt;counts[“some_id”]&lt;/code&gt; will error if &lt;code&gt;some_id&lt;/code&gt; has no items free since &lt;code&gt;counts&lt;/code&gt; won't have it as a key. The way to fix this is by using &lt;code&gt;counts.get(“some_id”, 0)&lt;/code&gt; where &lt;code&gt;0&lt;/code&gt; is a default value that’s returned when there’s no entry in the dictionary for &lt;code&gt;some_id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And as thought, the profiler output looks much better. The ncalls to &lt;code&gt;cursor.execute&lt;/code&gt; has gone down to 4, which is much more sensible!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/inventory&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;30824&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;29237&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;primitive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.055&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Ordered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;internal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reduced&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;677&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;due&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;restriction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;ncalls&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;tottime&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;percall&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;cumtime&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;percall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;297&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.003&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.010&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cameron&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cog&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;loading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.003&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.003&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;execute&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;psycopg2.extensions.cursor&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;162&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.003&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.000&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.025&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.025&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;join&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unicode&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The final code that we deployed is now sitting on &lt;a href="https://github.com/notexactlyawe/cog"&gt;GitHub&lt;/a&gt; so you can feel free to take a look.&lt;/p&gt;</content><category term="Blogging"></category><category term="Python"></category><category term="Databases"></category><category term="SQLAlchemy"></category></entry><entry><title>Useful links relating to abracadabra</title><link href="https://www.cameronmacleod.com/blog/abracadabra-links" rel="alternate"></link><published>2016-07-18T00:00:00+01:00</published><updated>2016-07-18T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2016-07-18:/blog/abracadabra-links</id><summary type="html">&lt;p&gt;Here is a collection of links relating to abracadabra that may be useful for anyone looking to do something similar.&lt;/p&gt;
&lt;h2 id="github-repositories"&gt;Github Repositories&lt;a class="headerlink" href="#github-repositories" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/notexactlyawe/abracadabra"&gt;abracadabra&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/worldveil/dejavu"&gt;dejavu&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="academic-papers"&gt;Academic Papers&lt;a class="headerlink" href="#academic-papers" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://musicweb.ucsd.edu/~sdubnov/CATbox/Reader/ThumbnailingMM05.pdf"&gt;Audio Thumbnailing of Popular Music Using Chroma-Based Representations&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The above paper was more about feature detection for search and retrieval as opposed to robust …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here is a collection of links relating to abracadabra that may be useful for anyone looking to do something similar.&lt;/p&gt;
&lt;h2 id="github-repositories"&gt;Github Repositories&lt;a class="headerlink" href="#github-repositories" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/notexactlyawe/abracadabra"&gt;abracadabra&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/worldveil/dejavu"&gt;dejavu&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="academic-papers"&gt;Academic Papers&lt;a class="headerlink" href="#academic-papers" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://musicweb.ucsd.edu/~sdubnov/CATbox/Reader/ThumbnailingMM05.pdf"&gt;Audio Thumbnailing of Popular Music Using Chroma-Based Representations&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The above paper was more about feature detection for search and retrieval as opposed to robust fingerprinting for search after distortion.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://slsp.kaist.ac.kr/~swkim/papers/journal_TIFS_2009.pdf"&gt;Pairwise Boosted Audio Fingerprint&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The above paper is about a new approach to finding filters and quantisers for fingerprinting music.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.cs.cmu.edu/~yke/musicretrieval/cvpr2005-mr.pdf"&gt;Computer Vision for Music Identification&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dejavu works in a similar way to the paper outlined above.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf"&gt;An Industrial Strength Audio Search Algorithm&lt;/a&gt; - The original Shazam paper&lt;/p&gt;
&lt;p&gt;Papers referencing Wang:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://scholar.google.co.uk/scholar?client=ubuntu&amp;amp;channel=fs&amp;amp;oe=utf-8&amp;amp;gfe_rd=cr&amp;amp;um=1&amp;amp;ie=UTF-8&amp;amp;lr&amp;amp;cites=11824032630980601393"&gt;https://scholar.google.co.uk/scholar?client=ubuntu&amp;amp;channel=fs&amp;amp;oe=utf-8&amp;amp;gfe_rd=cr&amp;amp;um=1&amp;amp;ie=UTF-8&amp;amp;lr&amp;amp;cites=11824032630980601393&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="similar-projects"&gt;Similar Projects&lt;a class="headerlink" href="#similar-projects" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Probably the closest in scope and implementation is &lt;a href="https://github.com/worldveil/dejavu"&gt;dejavu&lt;/a&gt; by Will Drevo.&lt;/p&gt;
&lt;p&gt;An algorithm that takes a slightly different approach is &lt;a href="https://acoustid.org/chromaprint"&gt;Chromaprint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://wiki.musicbrainz.org/Fingerprinting"&gt;Musicbrainz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://aubio.org/news/20091111-2339_shazam"&gt;Playing with Shazam fingerprints&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://echoprint.me/"&gt;Echoprint&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="use-cases"&gt;Use Cases&lt;a class="headerlink" href="#use-cases" title="Link to heading"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://static1.squarespace.com/static/53f7940ae4b05c506d396373/t/5669c81ba2bab86d89ef3dec/1449773083824/Koh_30x40.pdf"&gt;Alignment of videos of same event using audio fingerprinting&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://news.ycombinator.com/item?id=8303713"&gt;De-duplicating music library&lt;/a&gt; &lt;/p&gt;</content><category term="Link collections"></category><category term="Python"></category><category term="abracadabra"></category></entry><entry><title>How to read WAVE files in Python</title><link href="https://www.cameronmacleod.com/blog/reading-wave-python" rel="alternate"></link><published>2016-04-07T00:00:00+01:00</published><updated>2016-04-07T00:00:00+01:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2016-04-07:/blog/reading-wave-python</id><summary type="html">&lt;p&gt;Any project that uses audio will usually start out using WAVE files for its on-disk presence, and as with many things in Python, there's a standard library module for that. Now don't get me wrong in the rest of this article - &lt;code&gt;wave&lt;/code&gt; does the job. The thing is that it …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Any project that uses audio will usually start out using WAVE files for its on-disk presence, and as with many things in Python, there's a standard library module for that. Now don't get me wrong in the rest of this article - &lt;code&gt;wave&lt;/code&gt; does the job. The thing is that it can be a bit confusing to get started with and it's not &lt;em&gt;always&lt;/em&gt; the best tool for the job. This post will go over my journey in reading WAVEs and the various approaches I found.&lt;/p&gt;
&lt;p&gt;When you read &lt;a href="https://docs.python.org/2/library/wave.html"&gt;the documentation&lt;/a&gt; for &lt;code&gt;wave&lt;/code&gt;, you quickly find the &lt;code&gt;readframes()&lt;/code&gt; function for reading the meat of the file. This just as quickly poses a problem, that of how to parse the data it returns.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;#39;\xd04\xd52\xd63\x824...&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course, this wouldn't pose even the hint of a problem if we bothered to read &lt;a href="http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html"&gt;the spec&lt;/a&gt; for wave files, but who reads those? As a result, I found myself writing some convoluted string parser that was the textbook example of treating the symptom as opposed to the cause. It also didn't really work. It would technically be a valid solution, but there is little point getting it up and running because there are better ways.&lt;/p&gt;
&lt;p&gt;The next level up is discovering the &lt;code&gt;struct&lt;/code&gt; module. &lt;code&gt;struct&lt;/code&gt; is a module that allows for the reading of raw binary data into native Python types that I discovered - after some pained googling - in [this Stack Overflow question]. &lt;code&gt;struct.unpack()&lt;/code&gt; contains all of the magic that we need. It takes a format string and the data that you want to extract. The below is an example from the git history of &lt;a href="http://github.com/notexactlyawe/abracadabra"&gt;abracadabra&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_whole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;wav_r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;wav_r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;wav_r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getnframes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;h&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wav_r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readframes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a quick explanation of the format string, the &lt;code&gt;&amp;lt;&lt;/code&gt; indicates little-endian data (defined in the spec) and the &lt;code&gt;h&lt;/code&gt; 1 signed 16-bit int. Before you think "Hooray, a code snippet! I can leave now.", there are a few problems with this approach. Firstly, wave data is not guaranteed to contain int16s and so this would fall down on a good number of files. Second, it's horrendously slow.&lt;/p&gt;
&lt;p&gt;Tackling the second issue first, you could minimise the number of calls to &lt;code&gt;unpack()&lt;/code&gt;. Calling it with fmt as &lt;code&gt;"&amp;lt;hh"&lt;/code&gt; it would expect two int16s, &lt;code&gt;"&amp;lt;hhhhh"&lt;/code&gt; would expect 5. You could change the above code to use this as so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;chunk_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;wav_r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;wav_r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getnframes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;h&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wav_r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readframes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# (w.getnframes() - w.tell()) &amp;lt; chunk_size&lt;/span&gt;
        &lt;span class="n"&gt;tmp_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getnframes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;tmp_fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;h&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;
        &lt;span class="n"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp_fmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wav_r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readframes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Yet again, as this article is a journey as opposed to a straight out answer, this ugly hack does not come recommended. In general, I find that ugly code == doing things wrong. This example is no different, but is easily fixed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;&lt;/span&gt;&lt;span class="si"&gt;{0}&lt;/span&gt;&lt;span class="s2"&gt;h&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As an aside, if you are not already using this way to construct format strings, please take the time to internalise it. But back to the change, this works due to an unremarkable looking line in the docs for &lt;code&gt;struct&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A format character may be preceded by an integral repeat count. For example, the format string '4h' means exactly the same as 'hhhh'.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Much better, no?&lt;/p&gt;
&lt;p&gt;Our next issue was that the data isn't guaranteed to come in signed 16-bit integers. The good news is that we can get what format it is in programmatically with our old friend, the &lt;code&gt;wave&lt;/code&gt; module. The below code is from abracadabra.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;sizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;h&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;i&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wav&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getnchannels&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fmt_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wav&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsampwidth&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fmt_size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you have read the WAVE spec you will see that BitsPerSample is 2 bytes, suggesting that getsampwidth() could return an arbitrary value between 0 and 8192. In reality, you are not likely to encounter greater than 32 bit audio in the wild and &lt;code&gt;sizes&lt;/code&gt; reflects this. Saying this, it would probably be good practice to catch KeyError when setting fmt_size and raising a more readable error.&lt;/p&gt;
&lt;p&gt;So we now have a working solution, and I have used this in production code before now. There is, however, another optimisation you could make assuming that the files you are loading in aren't overly large and it involves the &lt;code&gt;array&lt;/code&gt; module. &lt;code&gt;array&lt;/code&gt; is advertised in the docs as memory efficent arrays, but for our purposes we care more that its implemented in straight C and is lightning fast. It's also pretty easy to use.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fmt_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;itemsize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You just pass it a format string (of the same format as struct) and then call &lt;code&gt;fromfile&lt;/code&gt; with a file object and the size of it. According to &lt;a href="http://stackoverflow.com/questions/5804052/improve-speed-of-reading-and-converting-from-binary-file-with-python"&gt;this&lt;/a&gt; SO question it is up to 40X faster than &lt;code&gt;struct.unpack&lt;/code&gt; &lt;em&gt;YMMV&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This has been my journey in attempting to read WAVE files and hopefully it will help. Most of the code in here has been adapted from my repository &lt;a href="https://github.com/notexactlyawe/abracadabra"&gt;abracadabra&lt;/a&gt; and if you are looking for an up-to-date version of what I am using there might be a good place to look. This article is &lt;a href="https://github.com/notexactlyawe/cameronmacleod.com"&gt;also on Github&lt;/a&gt; so if you see something wrong, please submit an issue.&lt;/p&gt;</content><category term="Tutorials"></category><category term="Python"></category><category term="Audio"></category></entry><entry><title>The importance of letting go of ideas</title><link href="https://www.cameronmacleod.com/blog/importance-of-letting-go" rel="alternate"></link><published>2016-03-13T00:00:00+00:00</published><updated>2016-03-13T00:00:00+00:00</updated><author><name>Cameron MacLeod</name></author><id>tag:www.cameronmacleod.com,2016-03-13:/blog/importance-of-letting-go</id><summary type="html">&lt;p&gt;On May 29 2015, I made my first entry into a fancy notebook I had bought. This was going to be it, I was never again going to struggle in vain to remember that killer idea I had earlier. Everything was getting written down from here on out.&lt;/p&gt;</summary><content type="html">&lt;p&gt;On May 29 2015, I made my first entry into a fancy notebook I had bought. This was going to be it, I was never again going to struggle in vain to remember that killer idea I had earlier. Everything was getting written down from here on out.&lt;/p&gt;
&lt;p&gt;Despite a decent start, by July the entries had started to get less frequent and at the start of August they had dried up completely. As with the vast majority of things that I start, this logbook concept hadn't got very far at all.&lt;/p&gt;
&lt;p&gt;Admittedly, some aspects were useful whilst it lasted. Thanks to this little book, I now have a complete record of my trip to Barcelona to a level of detail that photographs and memories could just never fill on their own. It was also great for keeping track of tasks that I had to do. Just to be clear, it didn't increase the speed with which I did them, just kept a record of them.&lt;/p&gt;
&lt;p&gt;The obvious question from here is, "Why did I stop writing in the notebook if it was useful?". I still have the notebook, and it still sits most of the time within easy reach, so it's not as if I lost it or anything. The answer to this I feel lies in the concept of value and the exchange of time for value. People, as a rule, only do things if they view the value of doing said thing to be greater than or equal to the value of the resources they use to do the thing. In this case, the only resource to be used was time, but I still didn't view the benefits I would gain from writing down an idea, or a task to be done as worth the time it would take.&lt;/p&gt;
&lt;p&gt;At this point, you may be calling me lazy. The time it takes to write a bullet point down is basically negligible, and all this hot air about value is just covering up a fundamental attitude of "can't be bothered". But that's the thing, "can't be bothered" stems from a lack of perceived value. I used to hate piano lessons. I would rarely practise, and would only do the minimum to get by due to what we would usually call not being bothered. In other terms, not seeing the value in doing the practice. &lt;/p&gt;
&lt;p&gt;When we don't do something that should be regular for a while, we fall out of the habit. This, I believe, is what happened with my notebook. I didn't see sufficient value in writing things down, despite forcing myself to do so for a time, and after a while just forgot all about the exercise. The question from here is: Even if I didn't see sufficient value in writing all my ideas down, was there sufficient value anyway?&lt;/p&gt;
&lt;p&gt;And here's where we get to the crux of the article. I would say no simply because the vast majority of ideas are absolute rubbish. Even the ones that we think are great (just look the failure rate of startups or the save-the-world ideas so often generated down the pub). As manner of example, one shamefully representative entry from July 2 in my notebook was as follows.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Peckish Rice Crackers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not overly enlightening stuff.&lt;/p&gt;
&lt;p&gt;No matter whether you are an extraordinary genius or you struggle with the puzzles on the backs of cereal boxes, you will give too much value to your own ideas. This narcissism is a fundamental and necessary component of human nature. If people weren't to have ideas that they believed in, then we wouldn't have any of the innovations that we have today. More than that, a lack of self-confidence in our ideas would lead to a lack of leaders, and society itself may not have developed - certainly not to the same extent it has.&lt;/p&gt;
&lt;p&gt;Beyond the cheapness of ideas, is there really any harm in scratching the itch of putting them somewhere? Not necessarily, but there is a freedom in learning to let them go. An idea that you are doing your best to remember is a bit of your mind that could be assigned to other things. Not only that, but good ideas tend to be remembered through their own virtue as opposed to needing to be explicitly remembered.&lt;/p&gt;
&lt;p&gt;Everyone goes through a diary phase or buys a notebook at some point, it's a natural thing to do. But don't beat yourself up for stopping, that much is logical.&lt;/p&gt;</content><category term="Blogging"></category></entry></feed>