<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Juha-Matti Santala - Community Builder. Dreamer. Adventurer.</title>
  <subtitle></subtitle>
  <link href="" rel="self" />
  <link href="https://hamatti.org" />
  <updated>2026-08-28T00:00:00Z</updated>
  <id></id>
  <author>
    <name>Juha-Matti Santala</name>
    <email>juhis@hamatti.org</email>
  </author> 
  
  <entry>
    <title>Y is for YAGNI - Python A to Z</title>
    <link href="https://hamatti.org/posts/y-is-for-yagni-python-a-to-z/" />
    <updated>2026-08-28T00:00:00Z</updated>
    <id>https://hamatti.org/posts/y-is-for-yagni-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  While I like writing code to solve my technical problems and fulfill my
  software needs, I don’t like writing unnecessary code. Writing something
  because you thought it could be useful is often — unless you learned something
  from it — wasteful in terms of time and effort and added complexity.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it&quot; class=&quot;notion-text-href&quot;&gt;YAGNI (You aren’t gonna need it)&lt;/a&gt;
  is a principle that arose from extreme programming methodology. While I have
  never worked in a project or company that followed any specific methodology to
  the letter, this one has been fundamental in my understanding of how to build
  software in a smart way. It hasn’t been without its faults though: one of my
  jobs ended due to a big clash with management around this idea.
&lt;/p&gt;

&lt;p&gt;
  The principle isn’t only helpful in focusing on what to do and by extent,
  saving money by not spending time and money on unnecessary things, it also
  brings about other positive effects. it’s not only about code we write but
  features we design.
&lt;/p&gt;

&lt;p&gt;
  Code you write stays simpler because less assumptions about future are
  introduced early. If you write code that is easy to extend and modify, you
  don’t need to prepare for every potential use case from day 1.
&lt;/p&gt;

&lt;p&gt;
  When it comes to designing software for its users (even if that’s just
  yourself), building something before you have a clear need for it and idea of
  how it needs to work, can lead to functionality that doesn’t match the
  expectations and can become more difficult to change in the future to match
  the actual expectations.
&lt;/p&gt;

&lt;p&gt;
  Remember,&lt;a href=&quot;https://xkcd.com/1172/&quot; class=&quot;notion-text-href&quot;&gt;
    there’s always someone who depends on already implemented workflows&lt;/a&gt;:
&lt;/p&gt;
&lt;img src=&quot;https://hamatti.org/assets/img/posts/y-is-for-yagni-python-a-to-z/1.png&quot; alt=&quot;XKCD comic of a software changelog that states: The CPU no longer overheats when you hold down space bar. A comment from LongTimeUser4: This update broke my workflow! My Control key is hard to reach, so I hold spacebar instead, and I configured Emacs to interpret a rapid temperature rise as “control”. Admin responds: That’s horrifying. LongTimeUser4 replies: Look, my setup works for me. Just add an option to re-enable spacebar heating. &quot; /&gt;
&lt;p&gt;
  I have so often — both in commercial context and my one-user hobby projects —
  encountered a situation where we think something needs to be built in a
  specific way. Only to then build it and later learn from actual user(s) using
  it that it doesn’t fit the need at all or that (almost) nobody uses it.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>X is for XML parsing - Python A to Z</title>
    <link href="https://hamatti.org/posts/x-is-for-xml-parsing-python-a-to-z/" />
    <updated>2026-08-27T00:00:00Z</updated>
    <id>https://hamatti.org/posts/x-is-for-xml-parsing-python-a-to-z/</id>
    <content type="html">
      
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Today I’m doing a bit of piggybacking: XML fits the “X” of the day but I’m
  also going to talk about HTML parsing because we can use the same tool. I’m
  also cheating a bit (honestly, I couldn’t come up with a new thing starting
  with X to write about) because I’ll mostly just point you towards my previous
  work. I’m doing this to bring new eyes to BeautifulSoup as my old blog posts
  don’t pop up so often in the feeds.
&lt;/p&gt;

&lt;p&gt;I’m starting with a story time:&lt;/p&gt;

&lt;p&gt;
  It’s probably one of the things I’ve written and spoken more about than
  anything else: parsing these formats with
  &lt;a href=&quot;https://beautiful-soup-4.readthedocs.io/en/latest/&quot; class=&quot;notion-text-href&quot;&gt;BeautifulSoup&lt;/a&gt;. My first ever
  &lt;a href=&quot;https://www.youtube.com/watch?v=Yi4uWzF6-Ug&quot; class=&quot;notion-text-href&quot;&gt;developer conference talk was in PyCon Finland 2016&lt;/a&gt;
  where I came with two slides: opening and closing and a demo without a backup
  that was using a live website in the Internet. Looking back now, I’m slightly
  happy I didn’t know better because there were so many things I would now be
  super stressed out but didn’t realise back then. Luckily, the demo went well.
  I haven’t dared to rewatch it in years though.
&lt;/p&gt;

&lt;p&gt;BeautifulSoup describes itself by saying&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  Beautiful Soup is a Python library for pulling data out of HTML and XML files.
  It works with your favorite parser to provide idiomatic ways of navigating,
  searching, and modifying the parse tree. It commonly saves programmers hours
  or days of work.
&lt;/blockquote&gt;

&lt;p&gt;
  I
  &lt;a href=&quot;https://hamatti.org/posts/how-to-scrape-website-with-python-beautifulsoup/&quot; class=&quot;notion-text-href&quot;&gt;wrote about it six years ago&lt;/a&gt;
  and not a ton has changed to be honest.
&lt;/p&gt;

&lt;p&gt;
  One thing I didn’t write about six years ago that I use all the time now is
  &lt;code class=&quot;notion-text-code&quot;&gt;select&lt;/code&gt; that allows
  &lt;a href=&quot;https://beautiful-soup-4.readthedocs.io/en/latest/#css-selectors&quot; class=&quot;notion-text-href&quot;&gt;directly querying with CSS selectors&lt;/a&gt;.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Parsing HTML&lt;/h2&gt;

&lt;p&gt;I’m borrowing the examples from documentation.&lt;/p&gt;

&lt;p&gt;We have a simple HTML doc with a couple of paragraphs and links.&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;html_doc = &quot;&quot;&quot;
&amp;#60;html&amp;#62;&amp;#60;head&amp;#62;&amp;#60;title&amp;#62;The Dormouse&#39;s story&amp;#60;/title&amp;#62;&amp;#60;/head&amp;#62;
&amp;#60;body&amp;#62;
&amp;#60;p class=&quot;title&quot;&amp;#62;&amp;#60;b&amp;#62;The Dormouse&#39;s story&amp;#60;/b&amp;#62;&amp;#60;/p&amp;#62;

&amp;#60;p class=&quot;story&quot;&amp;#62;Once upon a time there were three little sisters; and their names were
&amp;#60;a href=&quot;http://example.com/elsie&quot; class=&quot;sister&quot; id=&quot;link1&quot;&amp;#62;Elsie&amp;#60;/a&amp;#62;,
&amp;#60;a href=&quot;http://example.com/lacie&quot; class=&quot;sister&quot; id=&quot;link2&quot;&amp;#62;Lacie&amp;#60;/a&amp;#62; and
&amp;#60;a href=&quot;http://example.com/tillie&quot; class=&quot;sister&quot; id=&quot;link3&quot;&amp;#62;Tillie&amp;#60;/a&amp;#62;;
and they lived at the bottom of a well.&amp;#60;/p&amp;#62;

&amp;#60;p class=&quot;story&quot;&amp;#62;...&amp;#60;/p&amp;#62;
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To parse that, we do&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from bs4 import BeautifulSoup

soup = BeautifulSoup(html_doc, &#39;html.parser&#39;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  We can then start querying into
  &lt;code class=&quot;notion-text-code&quot;&gt;soup&lt;/code&gt; with
  &lt;code class=&quot;notion-text-code&quot;&gt;select&lt;/code&gt; :
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;soup.select(&quot;title&quot;) # [&amp;#60;title&amp;#62;The Dormouse&#39;s story&amp;#60;/title&amp;#62;]
soup.select(&quot;p &amp;#62; a&quot;)
# [&amp;#60;a class=&quot;sister&quot; href=&quot;http://example.com/elsie&quot; id=&quot;link1&quot;&amp;#62;Elsie&amp;#60;/a&amp;#62;,
#  &amp;#60;a class=&quot;sister&quot; href=&quot;http://example.com/lacie&quot;  id=&quot;link2&quot;&amp;#62;Lacie&amp;#60;/a&amp;#62;,
#  &amp;#60;a class=&quot;sister&quot; href=&quot;http://example.com/tillie&quot; id=&quot;link3&quot;&amp;#62;Tillie&amp;#60;/a&amp;#62;]

soup.select(&#39;a[href$=&quot;tillie&quot;]&#39;)
# [&amp;#60;a class=&quot;sister&quot; href=&quot;http://example.com/tillie&quot; id=&quot;link3&quot;&amp;#62;Tillie&amp;#60;/a&amp;#62;]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  If you have done frontend development and are familiar with CSS selectors,
  this is a really nice way compared to traversing the tree manually step by
  step.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Parsing XML&lt;/h2&gt;

&lt;p&gt;
  The same selectors work with XML documents. To test it out, let’s take a look
  at my RSS feed (I redacted the actual content of the blog posts for brevity):
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;xml_doc = &quot;&quot;&quot;&amp;#60;feed&amp;#62;
&amp;#60;title&amp;#62;
Juha-Matti Santala - Community Builder. Dreamer. Adventurer.
&amp;#60;/title&amp;#62;
&amp;#60;subtitle/&amp;#62;
&amp;#60;link href=&quot;&quot; rel=&quot;self&quot;/&amp;#62;
&amp;#60;link href=&quot;https://hamatti.org&quot;/&amp;#62;
&amp;#60;updated&amp;#62;2026-08-26T00:00:00Z&amp;#60;/updated&amp;#62;
&amp;#60;id/&amp;#62;
&amp;#60;author&amp;#62;
&amp;#60;name&amp;#62;Juha-Matti Santala&amp;#60;/name&amp;#62;
&amp;#60;email&amp;#62;juhis@hamatti.org&amp;#60;/email&amp;#62;
&amp;#60;/author&amp;#62;
&amp;#60;entry&amp;#62;
&amp;#60;title&amp;#62;W is for walrus operator - Python A to Z&amp;#60;/title&amp;#62;
&amp;#60;link href=&quot;https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/&quot;/&amp;#62;
&amp;#60;updated&amp;#62;2026-08-26T00:00:00Z&amp;#60;/updated&amp;#62;
&amp;#60;id&amp;#62;
https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/
&amp;#60;/id&amp;#62;
&amp;#60;content type=&quot;html&quot;&amp;#62;
[ redacted for brevity ]
&amp;#60;/content&amp;#62;
&amp;#60;/entry&amp;#62;
&amp;#60;entry&amp;#62;
&amp;#60;title&amp;#62;V is for visible characters - Python A to Z&amp;#60;/title&amp;#62;
&amp;#60;link href=&quot;https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/&quot;/&amp;#62;
&amp;#60;updated&amp;#62;2026-08-25T00:00:00Z&amp;#60;/updated&amp;#62;
&amp;#60;id&amp;#62;
https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/
&amp;#60;/id&amp;#62;
&amp;#60;content type=&quot;html&quot;&amp;#62;
[ redacted for brevity ]
&amp;#60;/content&amp;#62;
&amp;#60;/entry&amp;#62;&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To explore the XML document with CSS selectors:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from bs4 import BeautifulSoup
# You also need lxml installed to project

soup = BeautifulSoup(xml_doc, &quot;lxml&quot;)

soup.select(&#39;author&#39;)
# [&amp;#60;author&amp;#62;
# &amp;#60;name&amp;#62;Juha-Matti Santala&amp;#60;/name&amp;#62;
# &amp;#60;email&amp;#62;juhis@hamatti.org&amp;#60;/email&amp;#62;
# &amp;#60;/author&amp;#62;]

soup.select(&#39;entry id&#39;)
# [
# &amp;#60;id&amp;#62;https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/ &amp;#60;/id&amp;#62;, 
# &amp;#60;id&amp;#62;https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/ &amp;#60;/id&amp;#62;
# ]&lt;/code&gt;&lt;/pre&gt;

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


    </content>
  </entry>
   
  
  <entry>
    <title>W is for walrus operator - Python A to Z</title>
    <link href="https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/" />
    <updated>2026-08-26T00:00:00Z</updated>
    <id>https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Often in programming we end up in a situation where we want to check for an
  output of a function and then do something to it if it’s truthy.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;if get_next_step() is not None:
	execute(get_next_step())&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  To avoid calling the function twice and saving computing cycles, we would then
  call it first, save it and test against it:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;next_step = get_next_step()
if next_step is not None:
  execute(next_step)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  In Python 3.8, we got an assignment operator (&lt;code class=&quot;notion-text-code&quot;&gt;:=&lt;/code&gt;), nicknamed walrus operator because it looks like a cute walrus. It lets us
  shortcut this with
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;if (next_step := get_next_step()) is not None:
  execute(next_step)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It’s also handy in loops&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;while (next_step := get_next_step()) is not None:
  execute(next_step)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Now we skip two assignments: one before the loop for initial check and another
  at the end of the loop for next check.
&lt;/p&gt;

&lt;p&gt;
  It’s a great example of relatively small syntax thing that makes the code
  cleaner and easier to follow and less error prone.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>V is for visible characters - Python A to Z</title>
    <link href="https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/" />
    <updated>2026-08-25T00:00:00Z</updated>
    <id>https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  I recently built a website to display my
  &lt;a href=&quot;https://pokedex.hamatti.org/&quot; class=&quot;notion-text-href&quot;&gt;Pokédex Binder&lt;/a&gt;. I have a physical binder where I collect Pokémon TCG cards in a way where I
  aim to have one card for each Pokémon and their regional variants (and some
  forms when applicable).
&lt;/p&gt;

&lt;p&gt;For each card, I can write a short note to tell why it’s in my binder.&lt;/p&gt;

&lt;p&gt;
  Here’s an example of my most recent addition: an Onix card that’s signed by
  all the players in our
  &lt;a href=&quot;https://notes.hamatti.org/gaming/board-games/pokemon/events/pokemon-tcg-worlds-2006-retro-tournament,-august-22nd-2026&quot; class=&quot;notion-text-href&quot;&gt;recent retro tournament&lt;/a&gt;
  where I finished 2nd.
&lt;/p&gt;
&lt;img src=&quot;https://hamatti.org/assets/img/posts/v-is-for-visible-characters/1.png.webp&quot; alt=&quot;A web ui that mimics a card binder showing 9 Pokemon cards from Sabrina&#39;s Gengar to Electrode. On the left page of the binder, a Onix card is selected and shown as full page size with a custom note sharing it was a prize from a retro tournament.&quot; /&gt;
&lt;p&gt;
  The box has limited size so I need a way to control that in data entry: it
  should tell me when I’ve written too much. At the same time, I want to allow
  myself to use Markdown to add formatting and links so I can’t simply count the
  characters of the input.
&lt;/p&gt;

&lt;p&gt;
  I needed a way to count the characters that a user actually sees in the final
  note box. To justify this as an entry for the V day, I’m calling those
  “visible characters”.
&lt;/p&gt;

&lt;p&gt;I asked around in Mastodon and found two solutions.&lt;/p&gt;

&lt;p&gt;
  The first solution, that I ended up using is
  &lt;a href=&quot;https://pypi.org/project/strip-markdown/&quot; class=&quot;notion-text-href&quot;&gt;strip-markdown&lt;/a&gt;
  library.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;import strip_markdown

MAX_NOTE_LENGTH = 205

def is_valid_length(note: str) -&amp;#62; bool:
	visible = strip_markdown.strip_markdown(note)
	return len(visible) &amp;#60; 205

for card in pokedex:
  if not is_valid_length(card.note):
    raise ValueError(f&#39;{card.id} has too long note.&#39;
  
  add_to_binder(card) # imagine it exists	&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  A bit later, Jan-Erik
  &lt;a href=&quot;https://fnordig.de/til/Python/strip-markdown-syntax.html&quot; class=&quot;notion-text-href&quot;&gt;shared his solution&lt;/a&gt;
  which uses
  &lt;a href=&quot;https://pypi.org/project/Markdown/&quot; class=&quot;notion-text-href&quot;&gt;Python-Markdown&lt;/a&gt;:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from markdown import Markdown
from io import StringIO

def unmark_element(element, stream=None):
    if stream is None:
        stream = StringIO()
    if element.text:
        stream.write(element.text)
    for sub in element:
        unmark_element(sub, stream)
    if element.tail:
        stream.write(element.tail)
    return stream.getvalue()

Markdown.output_formats[&quot;plain&quot;] = unmark_element

__md = Markdown(output_format=&quot;plain&quot;)
__md.stripTopLevelTags = False

def strip_markdown(text):
    return __md.convert(text)&lt;/code&gt;&lt;/pre&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>U is for uv - Python A to Z</title>
    <link href="https://hamatti.org/posts/u-is-for-uv-python-a-to-z/" />
    <updated>2026-08-24T00:00:00Z</updated>
    <id>https://hamatti.org/posts/u-is-for-uv-python-a-to-z/</id>
    <content type="html">
      
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://docs.astral.sh/uv/&quot; class=&quot;notion-text-href&quot;&gt;uv&lt;/a&gt; is a
  Python package and project manager but in my day-to-day use it’s more of a
  script and improved REPL runner. In
  &lt;a href=&quot;https://hamatti.org/posts/tame-your-pesky-little-scripts/&quot; class=&quot;notion-text-href&quot;&gt;Tame your pesky little scripts&lt;/a&gt;, I wrote about my methods for dealing with different scripts I run from a
  command line and for the ones I write in Python, I use uv to
  &lt;a href=&quot;https://notes.hamatti.org/technology/single-file-executable-python-scripts-with-uv&quot; class=&quot;notion-text-href&quot;&gt;make them easy to run with dependencies&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  For example, the following script saved as
  &lt;code class=&quot;notion-text-code&quot;&gt;soup&lt;/code&gt;
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;#!/usr/bin/env -S uv run --quiet --script

# /// script
# requires-python = &quot;&amp;#62;=3.10&quot;
# dependencies = [
#    &quot;bs4&quot;,
# ]
# ///

from bs4 import BeautifulSoup

soup = BeautifulSoup(&#39;&amp;#60;ul&amp;#62;&amp;#60;li&amp;#62;First&amp;#60;/li&amp;#62;&amp;#60;li&amp;#62;Second&amp;#60;/li&amp;#62;&amp;#60;/ul&amp;#62;&#39;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  can be ran with &lt;code class=&quot;notion-text-code&quot;&gt;./soup&lt;/code&gt; and uv will take
  care of installing BeautifulSoup into a virtual environment that it will run
  this script in and I can import it into a script. I really like that.
&lt;/p&gt;

&lt;p&gt;
  I can also include external dependencies to my
  &lt;a href=&quot;https://hamatti.org/posts/r-is-for-repl-python-a-to-z/&quot; class=&quot;notion-text-href&quot;&gt;REPL sessions&lt;/a&gt;
  with
&lt;/p&gt;

&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;uv run --with bs4 python
&amp;#62;&amp;#62;&amp;#62; from bs4 import BeautifulSoup
&amp;#62;&amp;#62;&amp;#62; soup = BeautifulSoup(&#39;&amp;#60;ul&amp;#62;&amp;#60;li&amp;#62;First&amp;#60;/li&amp;#62;&amp;#60;li&amp;#62;Second&amp;#60;/li&amp;#62;&amp;#60;/ul&amp;#62;&#39;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or I can run a script with a specific Python version with&lt;/p&gt;

&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;uv run --python 3.12 script.py&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  This can be handy if you need to run with a specific version that you don’t
  default to. Sometimes I run stuff with older versions for example to make sure
  they work with a specific version or I run stuff with a newer version to test
  what’s coming in the future.
&lt;/p&gt;

&lt;p&gt;
  I’m not particularly excited about the future of uv now that
  &lt;a href=&quot;https://astral.sh/blog/openai&quot; class=&quot;notion-text-href&quot;&gt;OpenAI acquired it&lt;/a&gt;. But for now, it still seems to be a useful tool.
&lt;/p&gt;


    </content>
  </entry>
   
  
  <entry>
    <title>Reply to Dale’s comment on my IndieWeb post</title>
    <link href="https://hamatti.org/posts/reply-to-dales-comment-on-my-indieweb-post/" />
    <updated>2026-08-24T00:00:00Z</updated>
    <id>https://hamatti.org/posts/reply-to-dales-comment-on-my-indieweb-post/</id>
    <content type="html">
      &lt;p&gt;
  Yesterday, I shared my article
  &lt;a href=&quot;https://hamatti.org/posts/theres-no-membership-card-for-indieweb/&quot; class=&quot;notion-text-href&quot;&gt;There’s no membership card for IndieWeb&lt;/a&gt;
  and got the following comment from Dale:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
&lt;p&gt;Great article, I agree 99%.&lt;/p&gt;

&lt;p&gt;I do take issue, however, with your spiel that the IndieWeb is a recursive effort to ultimately converge everybody towards a set of ideas. I think it extremely important to the definition of an independent web that people can overtly diverge from the direction of the herd: be creative outside the box, be innovative, and, most of all, be brave and ground-breaking. Only then can the indie web grow and expand its boundaries.&lt;/p&gt;

&lt;p&gt;My own definition of a personal web site is one on which one person has the agency to do anything at all with the site without recourse to others. That does not preclude use of such systems as Wordpress, on proviso that the person using such system could in principle move off it--effectively being able to break the walls.&lt;/p&gt;

&lt;cite&gt;&lt;a class=&quot;u-in-reply-to&quot; href=&quot;https://khleedril.org/blog/comment/xclloh.html&quot;&gt;https://khleedril.org/blog/comment/xclloh.html&lt;/a&gt;&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;We don’t disagree here at all.&lt;/p&gt;

&lt;p&gt;
  I don’t think IndieWeb is “a recursive effort to ultimately converge everybody
  towards a set of ideas”. I apologise if my communication was lacking on that
  part. First of all, I don’t think &lt;b class=&quot;notion-text-bold&quot;&gt;everybody&lt;/b&gt; is
  the target of IndieWeb. I’m not even sure if I agree with the “converge [ - -
  ] towards a set of ideas part”. To encourage an adoption of certain ideas,
  sure. But not converge in a sense that these would be the only right things.
&lt;/p&gt;

&lt;p&gt;
  I also don’t think that the ideas or principles shared within IndieWeb
  community should be in any way a limiting set of ideas. Being creative outside
  the box and doing what
  &lt;b class=&quot;notion-text-bold&quot;&gt;you want for your site&lt;/b&gt; is exactly what I wish
  people do. I think IndieWeb — with its current definition, the definition that
  Belvadi proposed or the ideas I shared — encourages that a lot. IndieWeb is
  all about building new things. All I’m proposing is that we lower the floor of
  requirements even more than what it is by transforming the idea from a
  community to set of ideas.
&lt;/p&gt;

&lt;p&gt;
  As I wrote in my
  &lt;a href=&quot;https://hamatti.org/posts/where-are-we-going-indieweb/&quot; class=&quot;notion-text-href&quot;&gt;original post&lt;/a&gt;
  that I referenced, I made the case that it’s very difficult to draw a specific
  line on what is “IndieWeb enough” and mentioned:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  I want IndieWeb to be and to remain an open community where people can decide
  to be part of, regardless of how their website is built or hosted. I want the
  community to be inspiring and aspirational with its ideas and technical
  possibilities rather than limiting or gatekeeping.
&lt;/blockquote&gt;

&lt;p&gt;
  I have no problem with someone hosting their site on WordPress being part of
  IndieWeb. Zero.
&lt;/p&gt;

&lt;p&gt;
  I do however want to specify something. You mentioned “definition of a
  personal web site” while I was specifically speaking of a subset of the
  community that we call IndieWeb. I don’t think every site — even personal site
  — needs to have anything to do with IndieWeb ideas to be valid or accepted in
  the web.
&lt;/p&gt;

&lt;p&gt;
  I feel like my point is exactly in line with what you commented: I don’t think
  there should be any requirements to fulfill or a checklist to fill before
  you’re “in”.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>There’s no membership card for IndieWeb</title>
    <link href="https://hamatti.org/posts/theres-no-membership-card-for-indieweb/" />
    <updated>2026-08-23T00:00:00Z</updated>
    <id>https://hamatti.org/posts/theres-no-membership-card-for-indieweb/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;
&lt;img src=&quot;https://hamatti.org/assets/img/posts/theres-no-membership-card-for-indieweb/1.png.webp&quot; alt=&quot; &quot; /&gt;
&lt;p&gt;
  My good internet friend V.H. Belvadi — whose writing always gets my mind
  excited with his writing — published a great piece on his ideas on
  &lt;a href=&quot;https://vhbelvadi.com/re-defining-the-indieweb&quot; class=&quot;notion-text-href&quot;&gt;re-defining the IndieWeb&lt;/a&gt;
  and I have like half a dozen threads of ideas that sparked from that post.
  Here’s my first one.
&lt;/p&gt;

&lt;p&gt;
  He hosted
  &lt;a href=&quot;https://vhbelvadi.com/indieweb-carnival-round-up-dec-2025&quot; class=&quot;notion-text-href&quot;&gt;IndieWeb Carnival last December&lt;/a&gt;
  where a lot of people wrote about their ideas for where IndieWeb is going
  towards in the next five years. In this post, he takes a look at how IndieWeb
  movement defines itself and offers his thoughts on some of the issues in that
  and how he would define it.
&lt;/p&gt;

&lt;p&gt;
  While this blog post was inspired by his post, don’t consider this as any kind
  of rebuttal. There are some points where I point directly to his writing but
  everything else is just generally my ideas around the topic.
&lt;/p&gt;

&lt;p&gt;
  The starting point that Belvadi also quotes is this definition from
  &lt;a href=&quot;https://indieweb.org/&quot; class=&quot;notion-text-href&quot;&gt;IndieWeb wiki&lt;/a&gt;:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  The IndieWeb is a community of independent and personal websites based on the
  principles of: owning your domain and using it as your primary online
  identity, publishing on your own site first (optionally elsewhere), and owning
  your content.
&lt;/blockquote&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;A note on the tech club&lt;/h2&gt;

&lt;p&gt;
  In
  &lt;a href=&quot;https://hamatti.org/posts/where-are-we-going-indieweb/&quot; class=&quot;notion-text-href&quot;&gt;my entry from December&lt;/a&gt;, one of the things I said about IndieWeb was:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  I’m a technical person and I love building my own tools and tinkering with
  stuff on Saturday evenings. For me, IndieWeb is a community that encourages
  that and helps me find other people who care about the same things.
&lt;/blockquote&gt;

&lt;p&gt;
  I want to clear up something around that since it was commented on by some
  people in their blog posts between December and now and as this blog post
  today is not considering this aspect at all so it might seem contrary.
&lt;/p&gt;

&lt;p&gt;
  For me — at least for a long time, I’m not sure how strongly I hold that idea
  anymore for practical reasons that I’ll go to later — IndieWeb presented as a
  community of builders who like to tinker and make things and
  indie/personal/open/small web was a movement that was all about personal
  websites, separation from corporate controlled environments and all that.
&lt;/p&gt;

&lt;p&gt;
  That’s why I don’t consider that view as gatekeeping. The indie web is the
  important movement that I think we shouldn’t gatekeep. At the same time, I
  think it’s okay and valuable to have a community for people who like to tinker
  and build stuff.
&lt;/p&gt;

&lt;p&gt;And crucially importantly:&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;It’s more of a mindset than a skill check.&lt;/b&gt;
&lt;/blockquote&gt;

&lt;p&gt;That being said, let’s get to this post’s actual point.&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  There shouldn’t be a membership card
&lt;/h2&gt;

&lt;p&gt;
  The title of this blog post is more of my desire than the state of the world
  right now — depending maybe on whose definition you take to heart.
&lt;/p&gt;

&lt;p&gt;
  Coming back to Belvadi’s essay, he starts by pondering how we should reach a
  definition and if we should do it in inclusionary (what it is) or exclusionary
  (what it is not) terms.
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  Should the definition be inclusionary or exclusionary? Some, like Khürt
  Williams, are especially opposed to the exclusionary definition or what they
  call ‘Defining yourself by your enemy.’ Others, the majority by a cursory
  assessment, seem to define the IndieWeb specifically by contrasting its
  features with that of the CorpoWeb.
&lt;/blockquote&gt;

&lt;p&gt;
  I think the exclusionary is the easy way to convey the message to someone who
  is currently running their online presence in walled, controlled platforms
  like Facebook, Instagram, X and TikTok. It doesn’t go much further than that
  in my opinion though.
&lt;/p&gt;

&lt;p&gt;
  I don’t think i necessarily disagree with Belvadi with the definition he lands
  to. He makes great points at dissecting the IndieWeb wiki’s description for
  potential inaccuracies and issues and proposes new definitions. But I want to
  approach it from a very different perspective than what he wrote.
&lt;/p&gt;

&lt;p&gt;
  I think the problem with both the IndieWeb wiki definition and Belvadi’s new
  definition is that they seem to focus on “milestones” or checkboxes you need
  to fulfill to be consider part of the IndieWeb.
&lt;/p&gt;

&lt;p&gt;
  For the wiki’s definition, those are
  &lt;i class=&quot;notion-text-italic&quot;&gt;owning your domain and using it as your primary online identity, publishing
    on your own site first (optionally elsewhere), and owning your content. &lt;/i&gt;There are also a lot of other things people in the community like to talk
  about like
  &lt;a href=&quot;https://indieweb.org/microformats&quot; class=&quot;notion-text-href&quot;&gt;microformats&lt;/a&gt;
  and
  &lt;a href=&quot;https://indieweb.org/Webmention&quot; class=&quot;notion-text-href&quot;&gt;webmentions&lt;/a&gt;
  that many people feel too much gatekeeping or too technical and that people
  should chill out.
&lt;/p&gt;

&lt;p&gt;Belvadi’s definition&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  The IndieWeb is a space comprising websites whose domain ownership and
  organisational, operational and editorial sovereignty remain with an
  individual or non-corporate group of individuals, free from external
  obligations that compromise that sovereignty
&lt;/blockquote&gt;

&lt;p&gt;talks about ownership, sovereignty and obligations.&lt;/p&gt;

&lt;p&gt;One thing I want to focus on and that I strongly agree is when he said&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  So what &lt;i class=&quot;notion-text-italic&quot;&gt;is&lt;/i&gt; the IndieWeb? I do not believe
  the IndieWeb is a community.
&lt;/blockquote&gt;

&lt;p&gt;
  Even though I use the word community often with IndieWeb to talk about the
  people who gather together around it, I think the biggest value in IndieWeb is
  not a membership of such community. There are no membership cards and thus,
  &lt;b class=&quot;notion-text-bold&quot;&gt;there should not be membership requirements&lt;/b&gt;.
&lt;/p&gt;

&lt;p&gt;
  I think IndieWeb should be a set of ideas to work towards. How many of them
  you fill? Doesn’t matter. Doesn’t make you or your website more or less
  “IndieWeb”. I don’t think there should be any checklist we go through to
  assess whether someone belongs or not.
&lt;/p&gt;

&lt;p&gt;
  We should focus on sharing and discussing ideas and help build tools so that
  more people who want to implement them (for any reason they want), can do so.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;It’s a iterative journey&lt;/h2&gt;

&lt;p&gt;
  A strict definition that excludes a lot of people who could be &quot;almost
  matching&quot; leads to never getting those people in because you need to fill all
  the requirements before you&#39;re part of the group and the steps to get there
  can be too much to do at once.
&lt;/p&gt;

&lt;p&gt;
  I’ve had a website in the web for about 25 years by now. All of its iterations
  have been indie web but for some part of that 25 years, I didn’t have a domain
  or didn’t
  &lt;a href=&quot;https://indieweb.org/POSSE&quot; class=&quot;notion-text-href&quot;&gt;POSSE&lt;/a&gt;
  (Publish (on your) Own Site, Syndicate Elsewhere) or didn’t have webmentions
  or microformats.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://bobbyhiltz.com/posts/2025/12/indieweb-2030/&quot; class=&quot;notion-text-href&quot;&gt;Bobby Hiltz in his carnival entry&lt;/a&gt;
  in December wrote
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  I was pulled into the IndieWeb through Mastodon. I saw people talking about
  Gemini and had some free time and an extra Raspberry Pi, so I set it up. Then
  I figured that I could also host a regular static site. I thought it was great
  to own the words I was sharing and have them as physical files on my computer.
  Not having an account on some site (WordPress or Blogger) was a contributing
  factor, being able to ask people on the Internet to help me sort things out
  was a bonus.
&lt;/blockquote&gt;

&lt;p&gt;
  It’s a journey of many small steps. I don’t want us to turn people back when
  they first learn about IndieWeb ideas because they do not fulfill all the
  requirements for the non-existent membership.
&lt;/p&gt;

&lt;p&gt;
  Too strict requirements can be harmful to the goal. If we want people to start
  doing more sports to become healthier, it&#39;s not a great idea to have tryouts
  with minimum fitness requirements. If we want people to build more open,
  personal websites, we don&#39;t want there to be requirements for people to join
  the community so they can start learning how to do that.
&lt;/p&gt;

&lt;p&gt;
  In a blog post that has disappeared from the web, someone — let’s call them J
  — considered it too much work to &quot;set up POSSEing, microformats, webmentions
  and comments.” They wrote about how they want to be part of the indie web
  rather than IndieWeb and I can’t fault them for that.
&lt;/p&gt;

&lt;p&gt;
  When I first got interested in IndieWeb, I landed on a web page (that I can’t
  find anymore, maybe for the better) that presented IndieWeb as different
  levels of maturity. It was such a big turn off for me that I built a bit of
  resentment for the entire idea and spoke about the difference between indie
  web and IndieWeb and how the first is what matters more. I still hold that
  idea but I have found a lovely group of people through the latter as well.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  IndieWeb of principles rather than requirements
&lt;/h2&gt;

&lt;p&gt;
  My favourite part of the IndieWeb wiki is
  &lt;a href=&quot;https://indieweb.org/principles&quot; class=&quot;notion-text-href&quot;&gt;the 11 principles&lt;/a&gt;.
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  The IndieWeb Community is largely based on
  &lt;b class=&quot;notion-text-bold&quot;&gt;principles&lt;/b&gt; that we practice and aspire to [-
  -].
&lt;/blockquote&gt;

&lt;p&gt;
  I like the “aspire to” part. It’s not a checklist of requirements. It’s a list
  of ideas that we aspire to do: how many or how well you do them is up to you
  and not the important part.
&lt;/p&gt;

&lt;p&gt;
  I like them so much that I have written
  &lt;a href=&quot;https://hamatti.org/posts/indieweb-principles-and-i/&quot; class=&quot;notion-text-href&quot;&gt;my own post about them&lt;/a&gt;. In it, I note:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  Rather than being a binary yes / no categorisation, IndieWeb is a spectrum of
  many things where you can decide for yourself which one you care about and how
  much. There’s no point in evaluating anyone’s rate of participation or
  pointing fingers to “but you post on social media so you’re not really indie”.
&lt;/blockquote&gt;

&lt;p&gt;
  If you have a website or want to have one, I recommend you to check out what
  ideas and tools float around the IndieWeb: the wiki,
  &lt;a href=&quot;https://indieweb.org/IndieWeb_Carnival&quot; class=&quot;notion-text-href&quot;&gt;the Carnival&lt;/a&gt;
  (I love it!) and forums and social media discussions.
&lt;/p&gt;

&lt;p&gt;
  Don’t worry about if you’re “IndieWeb enough”. I’m giving you a permission to
  consider yourself part of the community. There are many ideas that are great.
  There are some ideas that I don’t particularly subscribe to but that’s fine,
  others can build them on their sites.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>T is for time-machine - Python A to Z</title>
    <link href="https://hamatti.org/posts/t-is-for-time-machine-python-a-to-z/" />
    <updated>2026-08-22T00:00:00Z</updated>
    <id>https://hamatti.org/posts/t-is-for-time-machine-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Quite a lot of our software has code in it that deals with time: either by
  displaying some time (current time, creation/modification time, countdowns
  etc.) or by doing different logic based on the current time (”show this for
  the week leading into Christmas” type of deal).
&lt;/p&gt;

&lt;p&gt;
  Time is wibbly wobbly — as
  &lt;a href=&quot;https://www.youtube.com/watch?v=q2nNzNo_Xps&quot; class=&quot;notion-text-href&quot;&gt;my favourite Doctor would say&lt;/a&gt;
  — and when writing tests, it means that we need tools to control time. If we
  let tests run against the system clock, they will fail sometimes and succeed
  other times. That doesn’t make them very good tests.
&lt;/p&gt;

&lt;p&gt;
  Adam Johnson’s
  &lt;a href=&quot;https://time-machine.readthedocs.io/en/latest/&quot; class=&quot;notion-text-href&quot;&gt;time-machine&lt;/a&gt;
  is a lovely library with a great logo to do exactly that (and
  &lt;a href=&quot;https://adamj.eu/tech/2026/08/03/python-time-machine-o1-freezegun-on/&quot; class=&quot;notion-text-href&quot;&gt;fast as well&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;
  The interface is nice and simple. A decorator lets you travel to a specific
  time per function/test:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;import datetime
import time_machine

# The first argument is the time to travel to
# tick defines if time should continue moving (True)
# or be frozen (False)
@time_machine.travel(&#39;2020-03-10&#39;, tick=False)
def test_a_good_day():
	assert datetime.date.today().isoformat() == &#39;2020-03-10&#39;&lt;/code&gt;&lt;/pre&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>S is for slicing - Python A to Z</title>
    <link href="https://hamatti.org/posts/s-is-for-slicing-python-a-to-z/" />
    <updated>2026-08-21T00:00:00Z</updated>
    <id>https://hamatti.org/posts/s-is-for-slicing-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Another 101 level primer day! Today, I write about
  &lt;b class=&quot;notion-text-bold&quot;&gt;slicing&lt;/b&gt; lists. Python has a very nice syntax
  for it, albeit one that can take a bit to get comfortable with.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Let’s start with index access
&lt;/h2&gt;

&lt;p&gt;
  To access a single item in a list, you use
  &lt;code class=&quot;notion-text-code&quot;&gt;[i]&lt;/code&gt; where
  &lt;code class=&quot;notion-text-code&quot;&gt;i&lt;/code&gt; is the index:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]

print(pokemon[0]) # Pikachu
print(pokemon[1]) # Snorlax
print(pokemon[2]) # Bulbasaur&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  If you’re new to programming, it’s important to note that the indexing starts
  at 0, not 1. This is common for a lot of programming languages but not
  universal.
&lt;/p&gt;

&lt;p&gt;
  If you try to access an index that is larger than the last available index,
  you’ll run into an &lt;code class=&quot;notion-text-code&quot;&gt;IndexError&lt;/code&gt; :
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]
print(pokemon[4]) # IndexError: list index out of range&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you use a negative index, Python will count from the end:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]

print(pokemon[-1]) # Bulbasaur
print(pokemon[-2]) # Snorlax
print(pokemon[-3]) # Pikachu
print(pokemon[-4]) # IndexError: list index out of range&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Here, the last element is at index
  &lt;code class=&quot;notion-text-code&quot;&gt;-1&lt;/code&gt; and the first one at
  &lt;code class=&quot;notion-text-code&quot;&gt;0 - len(pokemon)&lt;/code&gt; .
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Slicing&lt;/h2&gt;

&lt;p&gt;
  “Slicing” refers to accessing a sublist from a list rather than an individual
  element.
&lt;/p&gt;

&lt;p&gt;The syntax has three parts, all optional:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;list[start:stop:step]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For example, we can create a new list that has all but the first element:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]
first = pokemon[0] # &#39;Pikachu&#39;
rest = pokemon[1:] # [&#39;Snorlax&#39;, &#39;Bulbasaur&#39;]

# is same as 
rest = pokemon[1:3]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Slicing a list &lt;b class=&quot;notion-text-bold&quot;&gt;does not change the original&lt;/b&gt;!
  It creates a new copy with the elements we want.
&lt;/p&gt;

&lt;p&gt;
  As you can see in the example, the parameters of a slice are optional. If we
  want everything until the end, we can leave out
  &lt;code class=&quot;notion-text-code&quot;&gt;stop&lt;/code&gt;. If we want everything from the
  start, we can leave out &lt;code class=&quot;notion-text-code&quot;&gt;start&lt;/code&gt;:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]
all_but_last = pokemon[:2] # pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;]

# is same as 
all_but_last = pokemon[0:2]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The &lt;code class=&quot;notion-text-code&quot;&gt;stop&lt;/code&gt; parameter is
  &lt;b class=&quot;notion-text-bold&quot;&gt;exclusive&lt;/b&gt;, meaning we don’t include it.
&lt;/p&gt;

&lt;p&gt;Since both of them are optional, we can create a copy of a list with&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]
a_copy = pokemon[:] 
print(a_copy) # [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will slice everything from the beginning to the end of a list.&lt;/p&gt;

&lt;p&gt;We can use negative indices here as well:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;pokemon = [&#39;Pikachu&#39;, &#39;Snorlax&#39;, &#39;Bulbasaur&#39;]
all_but_last = pokemon[:-1] 
print(all_but_last) # [&#39;Pikachu&#39;, &#39;Snorlax&#39;]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The last parameter is &lt;code class=&quot;notion-text-code&quot;&gt;step&lt;/code&gt; and it allows
  us to create lists where we skip items or move backwards:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Take every other element
evens = numbers[::2] # [0, 2, 4, 6, 8, 10]
odds = numbers[1::2] # [1, 3, 5, 7, 9]

# Take every third element
every_third = numbers[::3] # [0, 3, 6, 9]

# Reverse a list
countdown = numbers[::-1] # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can then mix and match these to achieve all sorts of slices.&lt;/p&gt;

&lt;p&gt;
  You can also create slices up front with
  &lt;a href=&quot;https://docs.python.org/3/library/functions.html#slice&quot; class=&quot;notion-text-href&quot;&gt;the built-in slice function&lt;/a&gt;:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;reverse = slice(None, None, -1)
print(numbers[rev]) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

evens = slice(0, None, 2)
print(numbers[evens]) # [0, 2, 4, 6, 8, 10]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  I can’t remember a single time I would have used
  &lt;code class=&quot;notion-text-code&quot;&gt;slice&lt;/code&gt; but it does help with one of my
  favourites I’ll repeat many times this month: giving names to ideas is
  powerful.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;No IndexErrors!&lt;/h2&gt;

&lt;p&gt;
  A key difference between access by index and slicing is that there are no
  &lt;code class=&quot;notion-text-code&quot;&gt;IndexError&lt;/code&gt;s with slicing.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers[6:100]) # [6, 7, 8, 9, 10]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  When slicing with an end index too large, Python simply goes as far as it can.
  This can sometimes be a curse and a blessing as you can’t figure out the
  length of a new list based on the
  &lt;code class=&quot;notion-text-code&quot;&gt;stop&lt;/code&gt; parameter.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Use REPL to experiment and learn
&lt;/h2&gt;

&lt;p&gt;
  Yesterday
  &lt;a href=&quot;https://hamatti.org/posts/r-is-for-repl-python-a-to-z/&quot; class=&quot;notion-text-href&quot;&gt;I wrote about Python’s REPL&lt;/a&gt;. It’s really good for learning a new mechanic like this. You can start the
  REPL and keep on tinkering. Create lists, slice them in any and every way you
  can imagine and see if the results match your expectations.
&lt;/p&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>R is for REPL - Python A to Z</title>
    <link href="https://hamatti.org/posts/r-is-for-repl-python-a-to-z/" />
    <updated>2026-08-20T00:00:00Z</updated>
    <id>https://hamatti.org/posts/r-is-for-repl-python-a-to-z/</id>
    <content type="html">
      
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Completely by accident, I ended up with a four day streak of acronym titles.
  ORM, PEP, Q and today REPL. We developers sure love acronyms.
&lt;/p&gt;

&lt;p&gt;
  REPL (read-eval-print loop) is one of those things that make programming
  languages more approachable to me. I love being able to build my code
  bit-by-bit and have an easy way to test ideas out or double check on syntax
  without having to build all the infrastructure around them to run.
&lt;/p&gt;

&lt;p&gt;If you start Python without any arguments, you end up in a REPL:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;$ python
Python 3.14.6 (main, Jun 23 2026, 15:46:31) [Clang 22.1.3 ] on darwin
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&amp;#62;&amp;#62;&amp;#62;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The &lt;code class=&quot;notion-text-code&quot;&gt;&gt;&gt;&gt;&lt;/code&gt; tells you it is accepting your
  input and whatever you type there will be executed as Python code.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&amp;#62;&amp;#62;&amp;#62; 2+1
3
&amp;#62;&amp;#62;&amp;#62; print(&#39;Hello world!&#39;)
Hello world!&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  You can write practically any Python code in the REPL (&lt;i class=&quot;notion-text-italic&quot;&gt;I’m not sure if there are some special edge cases&lt;/i&gt;). You can define functions, run loops, read files and whatever you need.
&lt;/p&gt;

&lt;p&gt;
  It’s not only useful for iterating small parts of code that ends up in your
  source code. It’s also handy for doing interactive stuff: I often start a
  Python REPL instead of running commands in zsh shell because I’m so much more
  comfortable with Python than shell scripting.
&lt;/p&gt;

&lt;p&gt;
  Python’s REPL is not quite as good as Clojure’s but if you want a convincing
  argument for why REPLs are great, check out
  &lt;a href=&quot;https://www.youtube.com/watch?v=2MhTJMeUFns&quot; class=&quot;notion-text-href&quot;&gt;Ykä’s demo from Aurajoki Overflow showing how good Clojure has it&lt;/a&gt;. A lot of it applies to Python as well and is very close to the way I work
  iteratively with code!
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Django shell&lt;/h2&gt;

&lt;p&gt;
  Starting a regular REPL in your project is nice for general Python stuff but
  it’s hard to get the data of your program easily into it. That’s not a problem
  if you’re building with Django.
&lt;/p&gt;

&lt;p&gt;By running&lt;/p&gt;

&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ python manage.py shell
29 objects imported automatically (use -v 2 for details).

Python 3.14.6 (main, Jun 23 2026, 15:46:31) [Clang 22.1.3 ] on darwin
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&amp;#62;&amp;#62;&amp;#62; Card.objects.first()
&amp;#60;Card: Alakazam (1) &amp;#60;base1-1&amp;#62;&amp;#62;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  It loads all of your models and urls and stuff into the REPL session and
  connects to your database so you can immediately start writing queries to
  tinker with or explore your data.
&lt;/p&gt;

&lt;p&gt;
  I practically live in the Django shell when I’m developing my Django projects.
  Most of the code, before it lands into a source file, starts its life as
  experiments in the shell. It’s way faster to iterate over ideas and code as
  the feedback loops is immediate instead of running the full app or tests every
  time early in the development of a feature.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Debugging Python&lt;/h2&gt;

&lt;p&gt;
  When you use &lt;code class=&quot;notion-text-code&quot;&gt;breakpoint()&lt;/code&gt; to
  &lt;a href=&quot;https://hamatti.org/posts/debugging-python/&quot; class=&quot;notion-text-href&quot;&gt;debug your code&lt;/a&gt;, you land on a REPL with the context, stack and data of your running program
  and you can explore it, advance your code and figure out what’s wrong.
&lt;/p&gt;

&lt;p&gt;
  To become more efficient in debugging, it’s good to get familiar with REPL
  usage in general so when you’re in a stressful situation with a bug, you don’t
  have to focus on learning how to use the interface.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Startup script&lt;/h2&gt;

&lt;p&gt;
  Once you get more comfortable with Python, take a look at
  &lt;a href=&quot;https://hamatti.org/posts/bring-your-custom-toolkit-to-repl-sessions/&quot; class=&quot;notion-text-href&quot;&gt;PYTHONSTARTUP and writing your own startup script&lt;/a&gt;. With it, you can run code before the REPL session starts so you can import
  all the libraries you use all the time (mine starts with ~30 lines of imports
  for standard library stuff I’m reaching all the time and has a couple of “if
  installed, import this” imports as well) and set up helper functions.
&lt;/p&gt;

&lt;p&gt;
  One super handy tip
  &lt;a href=&quot;https://hamatti.org/python/startup&quot;&gt;from mine&lt;/a&gt;:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# With these, you can copy-paste JSON into REPL and 
# be considered a valid dictionary
null = None
true = True
false = False&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  For Django, I also import helpers like
  &lt;code class=&quot;notion-text-code&quot;&gt;Avg, Count, F, Max, Min, Q, Sum&lt;/code&gt; and
  &lt;code class=&quot;notion-text-code&quot;&gt;Value&lt;/code&gt; if Django is installed as I never
  remember where they are imported.
&lt;/p&gt;

&lt;p&gt;
  All in all, mine is about 350 lines, 90% of which is importing stuff if they
  exist in the environment.
&lt;/p&gt;

&lt;p&gt;Another cool trick is defining a pretty printer with some extra syntax.&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from pprint import PrettyPrinter

class Printer(PrettyPrinter):
    def __call__(self, *args, **kwargs):
        super().pprint(*args, **kwargs)

    def __truediv__(self, other):
        super().pprint(other)

    def __rtruediv__(self, other):
        super().pprint(other)

    def __repr__(self):
        return repr(pprint)


pp = Printer()
pp.__doc__ = pprint.__doc__&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  With the &lt;code class=&quot;notion-text-code&quot;&gt;__truediv__&lt;/code&gt; , you can do
  &lt;code class=&quot;notion-text-code&quot;&gt;pp/obj&lt;/code&gt; where
  &lt;code class=&quot;notion-text-code&quot;&gt;obj&lt;/code&gt; is anything and it will pretty print
  it.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://mastodon.social/@hugovk/117128111876948561&quot;&gt;Hugo pointed out&lt;/a&gt;
  that &lt;a href=&quot;https://docs.python.org/3.15/whatsnew/3.15.html&quot;&gt;in 3.15&lt;/a&gt;,
  &lt;code&gt;PrettyPrinter&lt;/code&gt; gets &lt;code&gt;expand&lt;/code&gt; keyword argument:
&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    Add an expand keyword argument for pprint.pprint(), pprint.pformat(),
    pprint.pp(). If true, the output will be formatted similar to pretty-printed
    json.dumps() when indent is supplied.
  &lt;/p&gt;
&lt;/blockquote&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>Q is for Q - Python A to Z</title>
    <link href="https://hamatti.org/posts/q-is-for-q-python-a-to-z/" />
    <updated>2026-08-19T00:00:00Z</updated>
    <id>https://hamatti.org/posts/q-is-for-q-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Second Django ORM post of the week! First, an important distinction before we
  start: this blog post is about
  &lt;a href=&quot;https://docs.djangoproject.com/en/6.1/topics/db/queries/#complex-lookups-with-q-objects&quot; class=&quot;notion-text-href&quot;&gt;Django’s Q objects&lt;/a&gt;
  and not about the task queue project
  &lt;a href=&quot;https://github.com/Koed00/django-q&quot; class=&quot;notion-text-href&quot;&gt;Django Q&lt;/a&gt;
  (or its fork
  &lt;a href=&quot;https://github.com/django-q2/django-q2&quot; class=&quot;notion-text-href&quot;&gt;Django Q2&lt;/a&gt;).
  &lt;i class=&quot;notion-text-italic&quot;&gt;Maybe I’ll write about them the next time I need to come up with something
    for letter Q because that’s not a large pool of ideas.&lt;/i&gt;
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Basics&lt;/h2&gt;

&lt;p&gt;
  Q objects are a way to write Django queries in a more composable way. These
  two are equivalent in outcome:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from models import Card

Card.objects.filter(name=&#39;Bulbasaur&#39;)

# and 

from django.db.models import Q

bulbasaur_query = Q(name=&#39;Bulbasaur&#39;)
Card.objects.filter(bulbasaur_query)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  For a simple example like this, there’s not a lot to gain. They are more
  useful for complex queries. If you want to use multiple fields for lookup,
  Django combines them with &lt;code class=&quot;notion-text-code&quot;&gt;AND&lt;/code&gt; operator:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# name = Bulbasaur AND set.name = Mega Evolution
Card.objects.filter(name=&#39;Bulbasaur&#39;, set__name=&#39;Mega Evolution&#39;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With Q objects, we get more logical operators:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;name = Q(name=&#39;Bulbasaur&#39;)
set_name = Q(set__name=&#39;Mega Evolution&#39;)

name &amp;#38; set_name # AND
name | set_name # OR
name ^ set_name # XOR
~name # NOT&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Now, we can combine different queries in many ways that makes it possible to
  do complex queries while keeping the code more readable and maintainable.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Documentation by giving expressions names
&lt;/h2&gt;

&lt;p&gt;Which brings me to another favourite of mine: documentation.&lt;/p&gt;

&lt;p&gt;
  I really like to give names to implementation details. Rather than reading
  through code and coming to a complex implementation detail of a query, I can
  read what it &lt;b class=&quot;notion-text-bold&quot;&gt;means&lt;/b&gt; in the context of the
  program.
&lt;/p&gt;

&lt;p&gt;If you run into the following code:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;Card.objects.filter(regulation_mark__in=[&#39;H&#39;, &#39;I&#39;, &#39;J&#39;])&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  you’d need to know quite a lot about the details to understand
  &lt;b class=&quot;notion-text-bold&quot;&gt;what&lt;/b&gt; it does. Sure, you can quickly read that
  it filters the cards based on their field “regulation mark” being one of “H”,
  “I” or “J” but what does that mean?
&lt;/p&gt;

&lt;p&gt;
  Instead, we can extract the query to a Q object, give it a name and make it
  reusable across the application (and making it less error prone!):
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;is_standard_legal = Q(regulation_mark__in=[&#39;H&#39;, &#39;I&#39;, &#39;J&#39;])
Card.objects.filter(is_standard_legal)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Now it’s a little bit clearer that this query finds Cards that are legal in
  the standard format.
&lt;/p&gt;

&lt;p&gt;
  This becomes even more valuable when there’s a lot of them. One use case I
  have for them in my Pokémon TCG toolkit is to combine different pre-determined
  configurations like format legality as shown above
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;is_standard_legal = Q(regulation_mark__in=[&#39;H&#39;, &#39;I&#39;, &#39;J&#39;])

ace_spec = Subtype.objects.get(type=&#39;ACE SPEC&#39;)
is_restricted_to_one = Q(subtypes=ace_spec)
is_trainer = Q(supertype=&#39;Trainer&#39;)

standard_legal_single_trainers = Card.objects.filter(
  is_standard_legal &amp;#38; is_restricted_to_one &amp;#38; is_trainer
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  If you’d only run into the last line, you would have a pretty good idea of
  what kind of cards we are filtering for. Especially when compared to mental
  work you need to do to gain the same understanding from:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;standard_legal_single_trainers = Card.objects.filter(
  Q(regulation_mark__in=[&#39;H&#39;, &#39;I&#39;, &#39;J&#39;]) &amp;#38; 
  Q(subtypes=Subtype.objects.get(type=&#39;ACE SPEC&#39;)) &amp;#38;
  Q(supertype=&#39;Trainer&#39;)
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Variables are very good, not only when you need to use them multiple times or
  in multiple places. I always tell developers, especially the ones newer to the
  craft, to liberally use intermediate variables to break down their code rather
  than trying to do too much at once.
&lt;/p&gt;

&lt;p&gt;
  It also makes it way easier to debug because you can use those intermediate
  variables and their values to find where things went wrong.
&lt;/p&gt;

&lt;p&gt;
  Giving them good names also helps you understand what you’re trying to achieve
  and notice logical blunders when the name you give it doesn’t match the effect
  it has.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Dynamically crafting queries
&lt;/h2&gt;

&lt;p&gt;Like regular queries, Q objects can be crafted dynamically.&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# You can start with an empty query!
query = Q()

if set_name:
  query &amp;#38;= Q(set__name=set_name)
  
if supertype:
  query &amp;#38;= Q(supertype=supertype)
  
Card.objects.filter(query)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  My project isn’t public but if you want to get an idea for how many search
  options there are for Pokémon TCG cards, take a look at
  &lt;a href=&quot;https://pkmncards.com/advanced/&quot; class=&quot;notion-text-href&quot;&gt;pkmncards.com’s advanced search page&lt;/a&gt;. Q objects make creating these a joy and I can use them everywhere in the
  app that deals with querying so I can guarantee that my queries are consistent
  across the app (either all right or all wrong) and need to change them only at
  one place when I need to fix them or the circumstances change.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>P is for Python Enhancement Proposal (PEP) - Python A to Z</title>
    <link href="https://hamatti.org/posts/p-is-for-python-enhancement-proposal-pep-python-a-to-z/" />
    <updated>2026-08-18T00:00:00Z</updated>
    <id>https://hamatti.org/posts/p-is-for-python-enhancement-proposal-pep-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  PEP stands for Python Enhancement Proposal. A PEP is a design document
  providing information to the Python community, or describing a new feature for
  Python or its processes or environment. The PEP should provide a concise
  technical specification of the feature and a rationale for the feature. -
  &lt;a href=&quot;https://peps.python.org/pep-0001/&quot; class=&quot;notion-text-href&quot;&gt;PEP 1 – PEP Purpose and Guidelines&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  If you’re a developer, you should start reading PEPs (or equivalents proposals
  in your language if such exist). They are a wonderful lesson in writing
  proposals for changes in software, having discussions in good faith between
  people who have differing opinions and arguing for your viewpoint and
  recording those discussions into decisions. In your own work or hobby project,
  this might be done in a form of
  &lt;a href=&quot;https://github.com/architecture-decision-record/architecture-decision-record&quot; class=&quot;notion-text-href&quot;&gt;an architecture decision record (ADR)&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  My favourite PEPs are a triplet
  &lt;a href=&quot;https://peps.python.org/pep-0634/&quot; class=&quot;notion-text-href&quot;&gt;PEP 634 – Structural Pattern Matching: Specification&lt;/a&gt;,
  &lt;a href=&quot;https://peps.python.org/pep-0635/&quot; class=&quot;notion-text-href&quot;&gt;PEP 635 – Structural Pattern Matching: Motivation and Rationale&lt;/a&gt;
  and
  &lt;a href=&quot;https://peps.python.org/pep-0636/&quot; class=&quot;notion-text-href&quot;&gt;PEP 636 – Structural Pattern Matching: Tutorial&lt;/a&gt;
  that defined
  &lt;a href=&quot;https://hamatti.org/posts/branch-out-with-pattern-matching/&quot; class=&quot;notion-text-href&quot;&gt;the pattern matching mechanism that I like a lot&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  A PEP starts with a metadata that shows who has authored it, when, what is its
  status and where you can find the discussion.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;Author:
    Tobias Kohn &amp;#60;kohnt at tobiaskohn.ch&amp;#62;, Guido van Rossum &amp;#60;guido at python.org&amp;#62;
BDFL-Delegate:
Discussions-To:
    Python-Dev list
Status:
    Final
Type:
    Informational
Created:
    12-Sep-2020
Python-Version:
    3.10
Post-History:
    22-Oct-2020, 08-Feb-2021
Resolution:
    Python-Committers message &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Then, it goes through the motivation for this proposal, rationale, history and
  context, technical considerations like backwards compatibility and security
  concerns and a few additional things like references and copyright statement.
&lt;/p&gt;

&lt;p&gt;
  These days, the discussion about PEPs happen in
  &lt;a href=&quot;https://discuss.python.org/c/peps/19&quot; class=&quot;notion-text-href&quot;&gt;Python’s official discussion forum&lt;/a&gt;. It’s a joy to read through to see bright minds discuss both the technical
  and the human side of these proposed changes.
&lt;/p&gt;

&lt;p&gt;
  I don’t think I’ll ever be involved in any PEP writing, discussion or decision
  making. I still enjoy reading them and following their development because it
  makes me better at arguing for and supporting my ideas and writing better
  documentation for decisions that have been made.
&lt;/p&gt;

&lt;p&gt;I encourage you to do the same!&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>O is for ORM - Python A to Z</title>
    <link href="https://hamatti.org/posts/o-is-for-orm-python-a-to-z/" />
    <updated>2026-08-17T00:00:00Z</updated>
    <id>https://hamatti.org/posts/o-is-for-orm-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Last year when I gave a talk
  &lt;a href=&quot;https://notes.hamatti.org/my-projects/talks/love-letter-for-django-(talk)&quot; class=&quot;notion-text-href&quot;&gt;Love Letter to Django&lt;/a&gt;
  in a local meetup, my main thread was about its ORM (Object–relational
  mapping) and a few things it enables.
&lt;/p&gt;

&lt;p&gt;
  I’m a big fan of ORM as a model: abstracting the database layer behind models
  that model the business logic of your application rather than writing raw SQL
  at the end of every function call path makes writing program logic way more
  enjoyable.
&lt;/p&gt;

&lt;p&gt;Here’s a small excerpt from one of my apps.&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from django.db import models
from django.conf import settings

from api.models import Card


class Collection(models.Model):
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )

    name = models.CharField(blank=True, null=True)

    def __str__(self):
        if self.name:
            return f&quot;Collection(name={self.name})&quot;
        else:
            return f&quot;Collection(name={self.owner.username}&#39;s collection)&quot;


class CollectionItem(models.Model):
    collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
    variant = models.ForeignKey(CollectionVariant, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=0)

    def __str__(self):
        return f&quot;{self.variant.card}, {self.variant.variant}, x{self.quantity}&quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I can then query my collection items in different ways.&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;## In a view based on the logged in user

def add_to_collection(request, variant_id: str) -&amp;#62; HttpResponse:
  collection = Collection.objects.filter(owner=request.user).first()
  collection_item = CollectionItem.objects.get(
        collection=collection, variant__id=variant_id
  )
  collection_item.quantity += 1
  collection_item.save()
  
  # ... do the rest
  
## In a shell to explore

&amp;#62;&amp;#62;&amp;#62; col = Collection.objects.first()
&amp;#62;&amp;#62;&amp;#62; col.collectionitem_set.all()
&amp;#60;QuerySet [&amp;#60;CollectionItem: Weedle (1) &amp;#60;me4-1&amp;#62;, Normal, x0&amp;#62;, ...]&amp;#62;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  It especially helps make new queries easier and lets you focus on the thing
  you want to achieve rather than diving deep into the database tables and how
  to make sure everything gets connected correctly every time.
&lt;/p&gt;

&lt;p&gt;
  On top of that basic premise, Django’s ORM is so enjoyable and part of it
  comes down to the REPL shell (guess what I’ll be writing tomorrow for “r”…).
  The ability to jump on a Django shell and start writing
  &lt;code class=&quot;notion-text-code&quot;&gt;Card.objects.filter(...)&lt;/code&gt; to start
  querying your database and building your software on top of that is something
  I enjoy a ton.
&lt;/p&gt;

&lt;p&gt;
  Joseph Zammit’s talk
  &lt;b class=&quot;notion-text-bold&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4JkghjjulvE&quot; class=&quot;notion-text-href&quot;&gt;Thinking in SQL with Django
    &lt;/a&gt;&lt;/b&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4JkghjjulvE&quot; class=&quot;notion-text-href&quot;&gt;in DjangoDay Copenhagen 2022&lt;/a&gt;
  improved my understanding of the ORM-SQL relationship a lot and
  &lt;a href=&quot;https://django-debug-toolbar.readthedocs.io/en/latest/&quot; class=&quot;notion-text-href&quot;&gt;Django Toolbar&lt;/a&gt;
  and &lt;a href=&quot;https://kolo.app/&quot; class=&quot;notion-text-href&quot;&gt;Kolo&lt;/a&gt; are great
  tools to debug and understand the queries your application actually makes
  under the hood and help you identify problems and optimize the queries.
&lt;/p&gt;

&lt;p&gt;
  Django’s ORM can be used on a more standalone way as well to connect to query
  existing databases that are not managed by Django.
  &lt;a href=&quot;https://www.paulox.net/2026/02/20/django-orm-standalone-database-inspectdb-query/&quot; class=&quot;notion-text-href&quot;&gt;Paulo Melchiorre has a wonderful blog post about it&lt;/a&gt;. You can write a tiny &lt;code class=&quot;notion-text-code&quot;&gt;manage.py&lt;/code&gt; and
  run a couple of commands against existing database to create
  &lt;code class=&quot;notion-text-code&quot;&gt;models.py&lt;/code&gt; and then start up a Django
  shell and make queries to explore that database. Really handy when you’re
  joining a new non-Django project and want a familiar toolset to explore the
  database.
&lt;/p&gt;

&lt;p&gt;
  I know there are people who don’t like ORMs and if you’re one of those people,
  I invite you to write a rebuttal on your own blog and send me a
  webmention/email/toot. I genuinely would love to read your thoughts and update
  this post with links for opposing views.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>N is for namedtuple - Python A to Z</title>
    <link href="https://hamatti.org/posts/n-is-for-namedtuple-python-a-to-z/" />
    <updated>2026-08-16T00:00:00Z</updated>
    <id>https://hamatti.org/posts/n-is-for-namedtuple-python-a-to-z/</id>
    <content type="html">
      
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  If you write any Python code, you should be using more
  &lt;a href=&quot;https://docs.python.org/3/library/collections.html#collections.namedtuple&quot; class=&quot;notion-text-href&quot;&gt;namedtuples&lt;/a&gt;
  than you are using right now. They are my favourite data structure in Python:
  they are a subclass of tuples so they are
  &lt;a href=&quot;https://hamatti.org/posts/i-is-for-immutable-python-a-to-z/&quot; class=&quot;notion-text-href&quot;&gt;immutable&lt;/a&gt;
  but in addition, you give them and their fields names which increases the
  readability and debuggability of your code a lot.
&lt;/p&gt;

&lt;p&gt;
  I have raved about them before: in
  &lt;a href=&quot;https://hamatti.org/posts/improve-your-code-with-namedtuples/&quot; class=&quot;notion-text-href&quot;&gt;Batteries included in 2024&lt;/a&gt;
  as well as every year when
  &lt;a href=&quot;https://hamatti.org/adventofcode/&quot; class=&quot;notion-text-href&quot;&gt;writing about Advent of Code&lt;/a&gt;. You should read the earlier post for more reasons for why to use them.
&lt;/p&gt;

&lt;p&gt;
  For the second time this month, I’ll link to Raymond Hettinger’s great talks,
  this time to his
  &lt;a href=&quot;https://www.youtube.com/watch?v=wf-BqAjZb8M&quot; class=&quot;notion-text-href&quot;&gt;Beyond PEP 8 talk from PyCon 2015&lt;/a&gt;
  where in addition to a bunch of other good tips, briefly talks about why
  namedtuples are better than regular tuples.
&lt;/p&gt;

&lt;p&gt;
  They are also a drop-in replacement which makes it easy to start adopting them
  into an existing codebase without having to do huge rewrites or refactorings.
  Any function that accepts a tuple, also accepts a namedtuple so whenever you
  see a tuple being created, you can replace it with a namedtuple and whenever
  you touch a code that uses those tuples, you can improve the code by changing
  from index lookups to attribute lookups.
&lt;/p&gt;

&lt;p&gt;
  Earlier this week,
  &lt;a href=&quot;https://adamj.eu/tech/2026/08/13/python-fix-typeerror-namedtuple-got-an-unexpected-keyword-argument/&quot; class=&quot;notion-text-href&quot;&gt;Adam Johnson shared about a change that’s coming in Python 3.15&lt;/a&gt;
  where you can no longer use (accidentally supported) keyword argument style
  for
  &lt;a href=&quot;https://docs.python.org/3/library/typing.html#typing.NamedTuple&quot; class=&quot;notion-text-href&quot;&gt;NamedTuple&lt;/a&gt;
  typing.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from typing import NamedTuple

Color = NamedTuple(&#39;Color&#39;, hue=float, saturation=float, luminosity=float)
# TypeError: NamedTuple() got an unexpected keyword argument &#39;hue&#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  It’s been deprecated since 3.13 but if you’ve been using them and have missed
  the deprecation warnings, now is a really good time to fix it.
&lt;/p&gt;

&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;Last time I wrote about namedtuples, at least a couple of people told me
    afterwards that they had taken my praise for them to heart and made
    improvements to their code so I decided to write about it again this year
    with hopes that it reaches the eyes of someone who haven’t heard of them
    before.&lt;/i&gt;
&lt;/p&gt;


    </content>
  </entry>
   
  
  <entry>
    <title>Saoirse Ronan Appreciation Week, round 2</title>
    <link href="https://hamatti.org/posts/saoirse-ronan-appreciation-week-round-2/" />
    <updated>2026-08-15T00:00:00Z</updated>
    <id>https://hamatti.org/posts/saoirse-ronan-appreciation-week-round-2/</id>
    <content type="html">
      
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
  A short Saturday break from my Python series to talk about
  &lt;a href=&quot;https://www.imdb.com/name/nm1519680/&quot;&gt;Saoirse Ronan&lt;/a&gt;. She&#39;s
  quickly become one of my favourite actors and after my first
  &lt;a href=&quot;https://hamatti.org/posts/saoirse-ronan-appreciation-week&quot;&gt;Saoirse Ronan Appreciation Week&lt;/a&gt;
  in May, I decided to do another one this week.
&lt;/p&gt;

&lt;p&gt;
  Five more days, five more wonderful movies. From historical to artistic and
  from intense to joyful, there&#39;s a lot going on in these five movies.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Monday: How I Live Now&lt;/h2&gt;

&lt;div class=&quot;movie-card&quot;&gt;
  &lt;div class=&quot;poster&quot;&gt;
    &lt;img src=&quot;https://hamatti.org/assets/img/movie-cards/how-i-live-now-poster.webp&quot; alt=&quot;How I Live Now&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;info-card&quot;&gt;
    &lt;h3&gt;How I Live Now &lt;time datetime=&quot;2013&quot;&gt;(2013)&lt;/time&gt;&lt;/h3&gt;
    &lt;table&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.imdb.com/title/tt1894476/&quot;&gt;&lt;img class=&quot;imdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/imdb.svg&quot; alt=&quot;IMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;6.4/10&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://letterboxd.com/film/how-i-live-now/&quot;&gt;&lt;img class=&quot;letterboxd-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/letterboxd.svg&quot; alt=&quot;Letterboxd&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;3.0/5&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.themoviedb.org/movie/162215-how-i-live-now&quot;&gt;&lt;img class=&quot;tmdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/tmdb.png&quot; alt=&quot;TMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;66%&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
    &lt;p class=&quot;directed-by&quot;&gt;Directed by Kevin Macdonald&lt;/p&gt;
    &lt;p&gt;Starring Saoirse Ronan, George MacKay, Tom Holland&lt;/p&gt;
    &lt;p&gt;
      An American girl, sent to the English countryside to stay with relatives, finds love and purpose while fighting for her survival as war envelops the world around her.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  First thought after a few first scenes roll by: Saoirse Ronan and Tom Holland
  are so young in this. From Ronan, I had seen Hanna from her earlier years but
  my first time seeing Holland was in Marvel&#39;s Civil War. Somehow, it&#39;s only 3
  years later than this but Holland grew up from a boy to a young man in those
  years. Even at their young age, they both ooze charisma. No wonder they ended
  up becoming stars.
&lt;/p&gt;
&lt;p&gt;
  This movie is quite a rollercoaster ride of moods and tones. It starts with a
  happy go lucky summer of a group of kids who are cousins to each other getting
  to know each other and having a good time. Then the dread of the war starts to
  make waves — first far in the distance and then suddenly right at the backyard
  and the whole feel of the movie changes from joyful relationship drama to
  surviving in a war and kids taking care of other kids.
&lt;/p&gt;
&lt;p&gt;
  What I really like about Ronan&#39;s acting is that she&#39;s great at playing
  imperfect characters in broken worlds. This movie is one good example of that.
  The Outrun that I watched last time is another.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Tuesday: On Chesil Beach&lt;/h2&gt;

&lt;div class=&quot;movie-card&quot;&gt;
  &lt;div class=&quot;poster&quot;&gt;
    &lt;img src=&quot;https://hamatti.org/assets/img/movie-cards/on-chesil-beach-poster.webp&quot; alt=&quot;On Chesil Beach&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;info-card&quot;&gt;
    &lt;h3&gt;On Chesil Beach &lt;time datetime=&quot;2017&quot;&gt;(2017)&lt;/time&gt;&lt;/h3&gt;
    &lt;table&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.imdb.com/title/tt1667321/&quot;&gt;&lt;img class=&quot;imdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/imdb.svg&quot; alt=&quot;IMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;6.3/10&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://letterboxd.com/film/on-chesil-beach/&quot;&gt;&lt;img class=&quot;letterboxd-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/letterboxd.svg&quot; alt=&quot;Letterboxd&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;3.0/5&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.themoviedb.org/movie/391714-on-chesil-beach&quot;&gt;&lt;img class=&quot;tmdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/tmdb.png&quot; alt=&quot;TMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;61%&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
    &lt;p class=&quot;directed-by&quot;&gt;Directed by  Dominic Cooke&lt;/p&gt;
    &lt;p&gt;Starring Saoirse Ronan, Billy Howle, Anne-Marie Duff&lt;/p&gt;
    &lt;p&gt;
      In 1962 England, a young couple finds their idyllic romance colliding with issues of sexual freedom and societal pressure, leading to an awkward and fateful wedding night.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  On Chesil Beach was on my original list last time but due to an admin error on
  my behalf (I accidentally used all my library streaming loans before watching
  it) had to skip it. Now it was time to right that wrong and borrow the movie
  from the library for this week&#39;s viewing.
&lt;/p&gt;
&lt;p&gt;
  I had high expectations for this movie based on the positive reviews I had
  read. Unfortunately it didn&#39;t quite do it for me.
&lt;/p&gt;
&lt;p&gt;
  Don&#39;t get me wrong, it&#39;s not by any means a bad movie. It&#39;s a good
  relationship drama set in 1960s where Florence (Saoirse Ronan) and Edward
  (Billy Howle) explore their relationship and figuring out how to have sex.
&lt;/p&gt;
&lt;p&gt;
  It just never clicked with me and I&#39;m having difficulty pointing out exactly
  why. Having watched a bunch of Ronan&#39;s movies in a short time, I think she has
  many better performances playing somewhat similar characters.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Wednesday: Mary Queen of Scots
&lt;/h2&gt;

&lt;div class=&quot;movie-card&quot;&gt;
  &lt;div class=&quot;poster&quot;&gt;
    &lt;img src=&quot;https://hamatti.org/assets/img/movie-cards/mary-queen-of-scots-poster.webp&quot; alt=&quot;Hanna&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;info-card&quot;&gt;
    &lt;h3&gt;Mary Queen of Scots &lt;time datetime=&quot;2018&quot;&gt;(2018)&lt;/time&gt;&lt;/h3&gt;
    &lt;table&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.imdb.com/title/tt2328900/&quot;&gt;&lt;img class=&quot;imdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/imdb.svg&quot; alt=&quot;IMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;6.3/10&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://letterboxd.com/film/mary-queen-of-scots-2018/&quot;&gt;&lt;img class=&quot;letterboxd-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/letterboxd.svg&quot; alt=&quot;Letterboxd&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;3.1/5&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.themoviedb.org/movie/457136-mary-queen-of-scots&quot;&gt;&lt;img class=&quot;tmdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/tmdb.png&quot; alt=&quot;TMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;65%&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
    &lt;p class=&quot;directed-by&quot;&gt;Directed by Josie Rourke&lt;/p&gt;
    &lt;p&gt;Starring Saoirse Ronan, Margot Robbie, Jack Lowden&lt;/p&gt;
    &lt;p&gt;
      In 1561, Mary Stuart, widow of the King of France, returns to Scotland,
      reclaims her rightful throne and menaces the future of Queen Elizabeth I
      as ruler of England, because she has a legitimate claim to the English
      throne.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  Mary Queen of Scots is a magnificent movie of the 1500s monarchy power
  struggles in England and Scotland. It&#39;s one of the rare movies where the press
  tour — especially the visits of Saoirse Ronan and Margot Robbie at The Graham
  Norton Show — was the trigger for me to get interested in this movie.
&lt;/p&gt;
&lt;p&gt;
  I don&#39;t know or particularly care about how historically accurate it is. I
  know they made a bunch of artistic choices to make a more interesting story
  and I&#39;m very fine with that. My knowledge or interest of 1500s history is not
  very high so I watched it like I would watch any medieval fantasy fiction.
&lt;/p&gt;
&lt;p&gt;
  And for that, it&#39;s a great movie. Ronan and Robbie, playing Mary and Elizabeth
  respectively, are both great at portraying strong women who need to keep all
  sorts of power hungry underlings in leash as they are many.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Thursday: See How They Run
&lt;/h2&gt;
&lt;div class=&quot;movie-card&quot;&gt;
  &lt;div class=&quot;poster&quot;&gt;
    &lt;img src=&quot;https://hamatti.org/assets/img/movie-cards/see-how-they-run-poster.webp&quot; alt=&quot;See How They Run&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;info-card&quot;&gt;
    &lt;h3&gt;See How They Run &lt;time datetime=&quot;2022&quot;&gt;(2022)&lt;/time&gt;&lt;/h3&gt;
    &lt;table&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.imdb.com/title/tt13640696/&quot;&gt;&lt;img class=&quot;imdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/imdb.svg&quot; alt=&quot;IMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;6.5/10&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://letterboxd.com/film/see-how-they-run-2022/&quot;&gt;&lt;img class=&quot;letterboxd-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/letterboxd.svg&quot; alt=&quot;Letterboxd&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;3.2/5&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.themoviedb.org/movie/766475-see-how-they-run&quot;&gt;&lt;img class=&quot;tmdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/tmdb.png&quot; alt=&quot;TMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;63%&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
    &lt;p class=&quot;directed-by&quot;&gt;Directed by Tom George&lt;/p&gt;
    &lt;p&gt;Starring Saoirse Ronan, Sam Rockwell, Adrien Brody&lt;/p&gt;
    &lt;p&gt;
      In the West End of 1950s London, plans for a movie version of a smash-hit
      play come to an abrupt halt after a pivotal member of the crew is
      murdered.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  See How They Run was a bit of surprising movie for me. I&#39;m generally not a fan
  of traditional whodunnit movies but this one geared up better and better,
  especially through the relationship of Constable Stalker (Saoirse Ronan), an
  up and coming young police officer and her boss Inspector Stoppard (Sam
  Rockwell) who&#39;s not as enthusiastic and is reluctant to work with Stalker.
&lt;/p&gt;
&lt;p&gt;
  There&#39;s a fantastic short scene where
  &lt;a href=&quot;https://hamatti.org/posts/a-lovely-dialogue-about-taking-notes-from-see-how-they-run/&quot;&gt;Stalker is taking notes and Stoppard asks her how she knows if something is
    important&lt;/a&gt;. As a note geek, I loved it so much. (And yes, I may be stretching a bit on
  the concept of a &quot;week&quot; here but like Doctor Who said, &quot;time is wibbly
  wobbly&quot;.)
&lt;/p&gt;
&lt;p&gt;
  The movie has two parallel stories: the aforementioned relationship and the
  actual murder mystery. During a stage play of a whodunnit mystery, a crew
  member is murdered and the police need to figure out who&#39;s behind the crime
  and why.
&lt;/p&gt;
&lt;p&gt;
  There&#39;s a lot of clever word play that I loved. The dialogue is sharp and
  witty and the characters have strong personalities, portraied in overly manner
  as is customary to stage play.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Friday: The Grand Budapest Hotel
&lt;/h2&gt;
&lt;div class=&quot;movie-card&quot;&gt;
  &lt;div class=&quot;poster&quot;&gt;
    &lt;img src=&quot;https://hamatti.org/assets/img/movie-cards/the-grand-budapest-hotel.webp&quot; alt=&quot;The Grand Budapest Hotel&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;info-card&quot;&gt;
    &lt;h3&gt;The Grand Budapest Hotel &lt;time datetime=&quot;2014&quot;&gt;(2014)&lt;/time&gt;&lt;/h3&gt;
    &lt;table&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.imdb.com/title/tt2278388/&quot;&gt;&lt;img class=&quot;imdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/imdb.svg&quot; alt=&quot;IMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;8.1/10&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://letterboxd.com/film/the-grand-budapest-hotel/&quot;&gt;&lt;img class=&quot;letterboxd-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/letterboxd.svg&quot; alt=&quot;Letterboxd&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;4.2/5&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.themoviedb.org/movie/120467-the-grand-budapest-hotel&quot;&gt;&lt;img class=&quot;tmdb-logo&quot; src=&quot;https://hamatti.org/assets/img/movie-cards/tmdb.png&quot; alt=&quot;TMDB&quot; /&gt;&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;80%&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
    &lt;p class=&quot;directed-by&quot;&gt;Directed by Wes Anderson&lt;/p&gt;
    &lt;p&gt;Starring Ralph Fiennes, Tony Revolori (+ Saoirse Ronan)&lt;/p&gt;
    &lt;p&gt;
      The Grand Budapest Hotel tells of a legendary concierge at a famous
      European hotel between the wars and his friendship with a young employee
      who becomes his trusted protégé. The story involves the theft and recovery
      of a priceless Renaissance painting, the battle for an enormous family
      fortune and the slow and then sudden upheavals that transformed Europe
      during the first half of the 20th century.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  After I shared the first blog post, I had a couple of discussions about her
  movies with friends and Wes Anderson&#39;s fantastic The Grand Budapest Hotel came
  up a few times.
&lt;/p&gt;
&lt;p&gt;
  It&#39;s not exactly a &quot;Saoirse Ronan movie&quot; as she plays a rather small
  supporting role but it&#39;s Friday and she&#39;s in the movie and I love that movie
  so I&#39;m cashing in my &lt;strong&gt;Creative Freedom coupon&lt;/strong&gt; and count this
  as one of her movies.
&lt;/p&gt;
&lt;p&gt;
  I&#39;ve seen this movie so many times. When it came out, I was living in San
  Francisco and was friends with a guy who was a massive Anderson fan. I had
  never seen any of his movies or even heard of him before that but the
  recommendation from a friend led me to this movie. Oh boy I&#39;m happy it did.
&lt;/p&gt;
&lt;p&gt;
  Every shot and scene in this movie is beautifully crafted. The sets are
  breath-takingly stunning, the action and dialogue is framed perfectly and
  while it has a certain &lt;em&gt;wesandersonist absurdism&lt;/em&gt; to it, it flows so
  effortlessly and it&#39;s easy to enjoy.
&lt;/p&gt;
&lt;p&gt;
  Ronan&#39;s role as Agatha is not a big one but she does a great performance as
  the love interest of the young hotel lobby boy Zero. She&#39;s brave and cunning
  and pours her love and effort into pastry baking.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>M is for meetups - Python A to Z</title>
    <link href="https://hamatti.org/posts/m-is-for-meetups-python-a-to-z/" />
    <updated>2026-08-14T00:00:00Z</updated>
    <id>https://hamatti.org/posts/m-is-for-meetups-python-a-to-z/</id>
    <content type="html">
      
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  You walk into a room that’s buzzing of nervous energy. Some people are
  chatting with each other, others are sitting alone in the corner looking at
  their phone or grabbing a drink or slice of pizza from the kitchen. After a
  moment, the MC welcomes everyone in and a speaker after another take the stage
  to talk about something they know.
&lt;/p&gt;

&lt;p&gt;
  You have likely been to a &lt;i class=&quot;notion-text-italic&quot;&gt;meetup.&lt;/i&gt; I love
  meetups. I’ve been participating in them for 13 years and have been running my
  own for over 10 years. A lot of my best friendships have started from those
  communities.
&lt;/p&gt;

&lt;p&gt;
  I have written about meetups and conferences from various perspectives in the
  past so consider this post as a
  &lt;i class=&quot;notion-text-italic&quot;&gt;literature review&lt;/i&gt; of sorts.
&lt;/p&gt;

&lt;p&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;&lt;a href=&quot;https://hamatti.org/posts/developers-guide-to-communities/&quot; class=&quot;notion-text-href&quot;&gt;Developer&#39;s Guide to Communities&lt;/a&gt;&lt;/b&gt;&lt;b class=&quot;notion-text-bold&quot;&gt; &lt;/b&gt;is a good starting point. It’s my magnum
  opus on why and how to join meetup communities as a software developer. It
  covers everything from why you as a developer should join as a participant or
  even start sharing as a speaker all the way to what meetups are and how they
  function.
&lt;/p&gt;

&lt;p&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;&lt;a href=&quot;https://archipylago.dev/blog/its-my-first-time-at-meetup/&quot; class=&quot;notion-text-href&quot;&gt;It&#39;s my first time at a meetup - how does it work?&lt;/a&gt;&lt;/b&gt;&lt;b class=&quot;notion-text-bold&quot;&gt; &lt;/b&gt;is a post I wrote for our local Python
  meetup group to give first-timers a feel for what they were joining if they
  decided to sign up for one of our events.
&lt;/p&gt;

&lt;p&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;&lt;a href=&quot;https://hamatti.org/1924c7c90c2d4823b004b29287d3f51f&quot; class=&quot;notion-text-href&quot;&gt;Shy introvert&#39;s (short) guide to speaking in conferences&lt;/a&gt;&lt;/b&gt;&lt;b class=&quot;notion-text-bold&quot;&gt; &lt;/b&gt;is for those who want to take the next step
  and start sharing their knowledge but are bit unsure. As I was traveling to
  Copenhagen to speak in
  &lt;a href=&quot;https://hamatti.org/posts/my-trip-to-django-day-copenhagen-2022-and-stockholm/&quot; class=&quot;notion-text-href&quot;&gt;Django Day 2022 conference&lt;/a&gt;, I wrote down all my best tips and tricks of how I — an introvert — navigate
  social conferences and get the best out of them.
&lt;/p&gt;

&lt;p&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;In &lt;/b&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;&lt;a href=&quot;https://marijkeluttekes.dev/blog/articles/2024/02/08/you-can-be-a-beginner-and-also-a-speaker-blogger-or-participant/&quot; class=&quot;notion-text-href&quot;&gt;You can be a beginner and also a speaker, blogger, or participant&lt;/a&gt;&lt;/b&gt;
  Marijke Luttekes writes encouraging words to everyone who considers them self
  too inexperienced to share: “Have you recently heard yourself or someone else
  say that you cannot share knowledge because you are a beginner or junior
  developer? I hope to convince you otherwise.”
&lt;/p&gt;

&lt;p&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;&lt;a href=&quot;https://hamatti.org/posts/chance-of-serendipity/&quot; class=&quot;notion-text-href&quot;&gt;Chance of serendipity&lt;/a&gt;&lt;/b&gt;&lt;b class=&quot;notion-text-bold&quot;&gt; &lt;/b&gt;is my argument for giving events like
  meetups and conferences a chance. You never know what’s going to happen: who
  you’ll meet, what will inspire you — even to the extent of having
  life-changing experiences.
&lt;/p&gt;

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




    </content>
  </entry>
   
  
  <entry>
    <title>L is for learning - Python A to Z</title>
    <link href="https://hamatti.org/posts/l-is-for-learning-python-a-to-z/" />
    <updated>2026-08-13T00:00:00Z</updated>
    <id>https://hamatti.org/posts/l-is-for-learning-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  A life of a software developer — no matter if you’re doing it professionally
  or as a hobby — is a life of continuous learning. The technologies evolve,
  problem solving skills are never complete and thanks to very generous sharing
  from the community, there’s always new things to learn.
&lt;/p&gt;

&lt;p&gt;
  For today’s entry to Python A-Z, I’ll share different resources that I’ve
  found good for learning more about Python.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Books&lt;/h2&gt;

&lt;p&gt;
  Luciano Ramalho’s Fluent Python is still my number one recommendation. I wrote
  a
  &lt;a href=&quot;https://hamatti.org/posts/book-recommendation-fluent-python/&quot; class=&quot;notion-text-href&quot;&gt;recommendation for it two years ago&lt;/a&gt;
  so I won’t repeat too much here. It’s a really good book for anyone who knows
  Python and wants to deepen their knowledge and get a better grasp of writing
  Python code that takes the best advantage of all of the language’s structures.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://automatetheboringstuff.com/&quot; class=&quot;notion-text-href&quot;&gt;Automate the boring stuff with Python&lt;/a&gt;
  by Al Sweigart is a good primer for Python (and available as free online
  book). It has a very positive, supportive and encouraging tone which is really
  nice because especially in the beginning, learning programming can be quite a
  challenging — but rewarding — endeavour.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://www.manning.com/books/python-workout&quot; class=&quot;notion-text-href&quot;&gt;Python Workout by Reuven M. Lerner&lt;/a&gt;
  is a good option if you have some experience writing Python under your belt
  but you want more exercise. It has 50 exercises to practice the basic
  fundamentals of Python. Each exercise is explained and teaches you different
  concepts in Python.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://www.manning.com/books/classic-computer-science-problems-in-python&quot; class=&quot;notion-text-href&quot;&gt;David Kopec’s Classic Computer Science Problems in Python&lt;/a&gt;
  is aimed for intermediate or advanced level developers and rather than
  teaching the basics, it focuses on interesting classic computer science
  problems (as one might guess from the name…) and how to solve those with
  Python. Each chapter focuses on a sub category of problems and at the end of
  the chapter ties it all together with how these skills are useful in
  real-world applications.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Podcasts&lt;/h2&gt;

&lt;p&gt;
  There are many interesting Python podcasts and for this, I also refer to my
  previous post in our Python meetup’s blog:
  &lt;a href=&quot;https://archipylago.dev/blog/start-your-year-with-a-new-podcast/&quot; class=&quot;notion-text-href&quot;&gt;Start your year with a new podcast&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  Whether you want to learn about the nitty gritty details of building Python
  itself or want to hear stories of the people in the Python community, there’s
  something in the Python podcast world for everyone.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Blogs (and other forms of web writing)
&lt;/h2&gt;

&lt;p&gt;
  Here are some of the blogs and people who do Python stuff from my RSS reader.
  I initially started writing about everyone and their blog (because
  &lt;a href=&quot;https://hamatti.org/posts/human-curation-over-algorithmic-recommendations/&quot; class=&quot;notion-text-href&quot;&gt;info dumps are not a great reader experience&lt;/a&gt;) but there are so many great people on this list that it got overwhelming.
&lt;/p&gt;

&lt;p&gt;
  Going through the list, I’m so happy that I’ve gotten to know many people on
  this list in-person as well and compiling the list brought back a lot of great
  memories.
&lt;/p&gt;
&lt;ul class=&quot;notion-bulleted_list&quot;&gt;
  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://adamj.eu/&quot; class=&quot;notion-text-href&quot;&gt;Adam Johnson&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.andreagrandi.it/&quot; class=&quot;notion-text-href&quot;&gt;Andrea Grandi&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://programmingmylife.com/&quot; class=&quot;notion-text-href&quot;&gt;Andrew Mshar&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://blog.pecar.me/&quot; class=&quot;notion-text-href&quot;&gt;Anže Pečar&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://noumenal.es/&quot; class=&quot;notion-text-href&quot;&gt;Carlton Gibson&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://cheuk.dev/&quot; class=&quot;notion-text-href&quot;&gt;Cheuk Ting Ho&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://coredispatch.xyz/&quot; class=&quot;notion-text-href&quot;&gt;Core Dispatch&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://daniel.feldroy.com/&quot; class=&quot;notion-text-href&quot;&gt;Daniel Roy Greenfeld&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://dawnwages.info/bajoran-engineer/&quot; class=&quot;notion-text-href&quot;&gt;Dawn Wages&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://dennyperez.dev/&quot; class=&quot;notion-text-href&quot;&gt;Denny Perez&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://georgiker.com/&quot; class=&quot;notion-text-href&quot;&gt;Georgi Ker&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://blog.ovalerio.net/&quot; class=&quot;notion-text-href&quot;&gt;Gonçalo Valério&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hugovk.dev/&quot; class=&quot;notion-text-href&quot;&gt;Hugo van Kemenade&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.b-list.org/&quot; class=&quot;notion-text-href&quot;&gt;James Bennett&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://jefftriplett.com/&quot; class=&quot;notion-text-href&quot;&gt;Jeff Triplett&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://johnnymetz.com/&quot; class=&quot;notion-text-href&quot;&gt;Johnny Metz&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://lukeplant.me.uk/&quot; class=&quot;notion-text-href&quot;&gt;Luke Plant&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://mariatta.ca/&quot; class=&quot;notion-text-href&quot;&gt;Mariatta&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://marijkeluttekes.dev/&quot; class=&quot;notion-text-href&quot;&gt;Marijke Luttekes&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://pythonbynight.com/&quot; class=&quot;notion-text-href&quot;&gt;Mario Munoz&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://martinheinz.dev/&quot; class=&quot;notion-text-href&quot;&gt;Martin Heinz&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://406.ch/&quot; class=&quot;notion-text-href&quot;&gt;Matthias Kestenholz&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://maurits.vanrees.org/&quot; class=&quot;notion-text-href&quot;&gt;Maurits van Rees&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://miabajic.dev/&quot; class=&quot;notion-text-href&quot;&gt;Mia Bajić&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.mostlypython.com/&quot; class=&quot;notion-text-href&quot;&gt;Mostly Python&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://kuttler.eu/&quot; class=&quot;notion-text-href&quot;&gt;Nicolas Kuttler&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://nkantar.com/&quot; class=&quot;notion-text-href&quot;&gt;Nik Kantar&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://pamelafox.org/&quot; class=&quot;notion-text-href&quot;&gt;Pamela Fox&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.paulox.net/&quot; class=&quot;notion-text-href&quot;&gt;Paolo Melchiorre&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.peterbe.com/&quot; class=&quot;notion-text-href&quot;&gt;Peter Bengtsson&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://savannah.dev/&quot; class=&quot;notion-text-href&quot;&gt;Savannah Ostrowski&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://sethmlarson.dev/&quot; class=&quot;notion-text-href&quot;&gt;Seth Larson&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.sheenaoc.com/&quot; class=&quot;notion-text-href&quot;&gt;Sheena O&#39;Connell&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://simonwillison.net/tags/python/&quot; class=&quot;notion-text-href&quot;&gt;Simon Willison&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://stfn.pl/&quot; class=&quot;notion-text-href&quot;&gt;Stefan Machura&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://b0uh.github.io/&quot; class=&quot;notion-text-href&quot;&gt;Thomas Loiret&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://timonweb.com/&quot; class=&quot;notion-text-href&quot;&gt;Tim Kamanin&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.screamingatmyscreen.com/&quot; class=&quot;notion-text-href&quot;&gt;Timo Zimmermann&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://www.pythonmorsels.com/&quot; class=&quot;notion-text-href&quot;&gt;Trey Hunner&lt;/a&gt;
    who also shares great
    &lt;a href=&quot;https://mastodon.social/@treyhunner&quot; class=&quot;notion-text-href&quot;&gt;daily tips in Mastodon&lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  There’s also probably many that I have missed. My categorisation efforts in my
  RSS reader are not at the level I wish they were so there are probably many
  more that I read but couldn’t find or remember right now.
&lt;/p&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>K is for keyword arguments - Python A to Z</title>
    <link href="https://hamatti.org/posts/k-is-for-keyword-arguments-python-a-to-z/" />
    <updated>2026-08-12T00:00:00Z</updated>
    <id>https://hamatti.org/posts/k-is-for-keyword-arguments-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  In
  &lt;a href=&quot;https://www.youtube.com/watch?v=OSGv2VnC0go&quot; class=&quot;notion-text-href&quot;&gt;PyCon US 2013, Raymond Hettinger talked about writing beautiful, idiomatic
    Python&lt;/a&gt;. It’s a great talk but for this post, I’ll focus on what he talks about
  around 30 minute mark.
&lt;/p&gt;

&lt;p&gt;
  He shares an example (which dates the talk and example nicely; those were the
  times)
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;twitter_search(&#39;@obama&#39;, False, 20, True)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  We spend way more time reading code than we do writing it. While modern code
  editors and IDEs give you a lot of help (as you can see in the following
  screenshot from VS Code), it’s still an extra step.
&lt;/p&gt;
&lt;img src=&quot;https://hamatti.org/assets/img/posts/k-is-for-keyword-arguments/1.png.webp&quot; alt=&quot;Screenshot from VS Code, showing a function call to twitter_search with an overlay documentation for parameter names, types and descriptions. &quot; /&gt;
&lt;p&gt;
  Keyword arguments are a great, small step to improving the readability of your
  code. Compare the two:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;twitter_search(&#39;@obama&#39;, False, 20, True)
# and 
twitter_search(&#39;@obama&#39;, retweets=False, numtweets=20, popularity=True)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The second one is immediately more readable and the better names for your
  keywords you write, the better its readability becomes.
&lt;/p&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>J is for JSON validation - Python A to Z</title>
    <link href="https://hamatti.org/posts/j-is-for-json-validation-python-a-to-z/" />
    <updated>2026-08-11T00:00:00Z</updated>
    <id>https://hamatti.org/posts/j-is-for-json-validation-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  When you need to load external data into your software, you want to make sure
  it behaves properly, ie. it’s in correct form. One tool to validate input data
  is
  &lt;a href=&quot;https://pydantic.dev/docs/validation/latest/get-started/&quot; class=&quot;notion-text-href&quot;&gt;Pydantic&lt;/a&gt;. This is a quick tutorial to show how it works.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Installing Pydantic&lt;/h2&gt;

&lt;p&gt;Set up your environment and install with&lt;/p&gt;

&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;pip install pydantic&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Alternatively, you can run these with
  &lt;a href=&quot;https://docs.astral.sh/uv/&quot; class=&quot;notion-text-href&quot;&gt;uv&lt;/a&gt;:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;uv run --with pydantic code.py&lt;/code&gt;&lt;/pre&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Simplified Pokédex data&lt;/h2&gt;

&lt;p&gt;
  For this demo, I’m using a simplified data set adapted from
  &lt;a href=&quot;https://github.com/Purukitto/pokemon-data.json&quot; class=&quot;notion-text-href&quot;&gt;Purukitto’s Pokemon.json&lt;/a&gt;. I’ve saved this to a file
  &lt;code class=&quot;notion-text-code&quot;&gt;pokedex.json&lt;/code&gt;.
&lt;/p&gt;

&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Bulbasaur&quot;,
    &quot;type&quot;: [&quot;Grass&quot;, &quot;Poison&quot;],
    &quot;stats&quot;: {
      &quot;HP&quot;: 45,
      &quot;Attack&quot;: 49,
      &quot;Defense&quot;: 49,
      &quot;Sp. Attack&quot;: 65,
      &quot;Sp. Defense&quot;: 65,
      &quot;Speed&quot;: 45
    },
    &quot;height&quot;: &quot;0.7 m&quot;,
    &quot;weight&quot;: &quot;6.9 kg&quot;
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Ivysaur&quot;,
    &quot;type&quot;: [&quot;Grass&quot;, &quot;Poison&quot;],
    &quot;stats&quot;: {
      &quot;HP&quot;: 60,
      &quot;Attack&quot;: 62,
      &quot;Defense&quot;: 63,
      &quot;Sp. Attack&quot;: 80,
      &quot;Sp. Defense&quot;: 80,
      &quot;Speed&quot;: 60
    },
    &quot;height&quot;: &quot;1 m&quot;,
    &quot;weight&quot;: &quot;13 kg&quot;
  }
]&lt;/code&gt;&lt;/pre&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Building a schema&lt;/h2&gt;
&lt;h3&gt;Step 1: Basic types&lt;/h3&gt;
&lt;p&gt;
  Let’s start with a first step. We’ll define a class that inherits from
  Pydantic’s &lt;code class=&quot;notion-text-code&quot;&gt;BaseModel&lt;/code&gt;. These classes will
  be the way we define the schema and what powers Pydantic’s validation engines.
&lt;/p&gt;

&lt;p&gt;For the first step, we’ll use basic definitions.&lt;/p&gt;

&lt;p&gt;
  A Pokémon has a couple of fields: an id, a name, a list of types, a dictionary
  of stats, height and weight. A Pokedex is a list of Pokémon.
&lt;/p&gt;

&lt;p&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;I will be using this as a base for all future snippets in this post but
    will only share what’s changed later on. You can find &lt;a href=&quot;https://gist.github.com/Hamatti/863a59df2e6122d823a8d99b39c9e9bf&quot;&gt;full code examples in GitHub&lt;/a&gt;.&lt;/b&gt;
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;import json

from pydantic import BaseModel
from typing import List, TypeAlias


class Pokemon(BaseModel):
    &quot;&quot;&quot;An entry in Pokedex&quot;&quot;&quot;

    id: int
    name: str
    type: List[str]
    stats: dict
    height: str
    weight: str


Pokedex: TypeAlias = List[Pokemon]


def read_pokedex(filename: str) -&amp;#62; Pokedex:
    pokedex = []
    with open(filename, &quot;r&quot;) as dex:
        data = json.load(dex)
        for entry in data:
            pokedex.append(Pokemon(**entry))
    return pokedex


if __name__ == &quot;__main__&quot;:
    pokedex = read_pokedex(&quot;pokedex.json&quot;)
    print(pokedex)&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Step 2: More refined types&lt;/h3&gt;
&lt;p&gt;
  Python itself offers enough for the basic types (like
  &lt;code class=&quot;notion-text-code&quot;&gt;int&lt;/code&gt;,
  &lt;code class=&quot;notion-text-code&quot;&gt;str&lt;/code&gt; ,
  &lt;code class=&quot;notion-text-code&quot;&gt;List&lt;/code&gt; and
  &lt;code class=&quot;notion-text-code&quot;&gt;dict&lt;/code&gt; in the example above) but Pydantic
  offers a lot more if we want to be more specific.
&lt;/p&gt;

&lt;p&gt;
  For example, our &lt;code class=&quot;notion-text-code&quot;&gt;id&lt;/code&gt; field is always a
  positive integer. We can use
  &lt;code class=&quot;notion-text-code&quot;&gt;PositiveInt&lt;/code&gt; from Pydantic to add an
  extra layer of validation.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from pydantic import BaseModel, PositiveInt

class Pokemon(BaseModel):
    &quot;&quot;&quot;An entry in Pokedex&quot;&quot;&quot;

    id: PositiveInt
    name: str
    type: List[str]
    stats: dict
    height: str
    weight: str&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Everything else stays the same but we import
  &lt;code class=&quot;notion-text-code&quot;&gt;PositiveInt&lt;/code&gt; and change
  &lt;code class=&quot;notion-text-code&quot;&gt;id&lt;/code&gt; to it.
&lt;/p&gt;

&lt;p&gt;
  Now, lets we add a third element to our JSON to see how this works. Here, I’ve
  introduced a mistake in data: &lt;code class=&quot;notion-text-code&quot;&gt;id&lt;/code&gt; is now
  -3 instead of 3.
&lt;/p&gt;

&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &quot;id&quot;: -3,
    &quot;name&quot;: &quot;Venusaur&quot;,
    &quot;type&quot;: [&quot;Grass&quot;, &quot;Poison&quot;],
    &quot;stats&quot;: {
      &quot;HP&quot;: 80,
      &quot;Attack&quot;: 82,
      &quot;Defense&quot;: 83,
      &quot;Sp. Attack&quot;: 100,
      &quot;Sp. Defense&quot;: 100,
      &quot;Speed&quot;: 80
    },
    &quot;height&quot;: &quot;2 m&quot;,
    &quot;weight&quot;: &quot;100 kg&quot;
  }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  If we run our code from step 1, all runs smoothly. If we run the code from
  step 2, we get a validation error:
&lt;/p&gt;

&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;pydantic_core._pydantic_core.ValidationError: 1 validation error for Pokemon
id
  Input should be greater than 0 [type=greater_than, input_value=-3, input_type=int]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;Remember to switch -3 to 3 before continuing.&lt;/b&gt;
&lt;/p&gt;
&lt;h3&gt;Step 3: Renaming fields&lt;/h3&gt;
&lt;p&gt;Our stats field looks like this:&lt;/p&gt;

&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;    &quot;stats&quot;: {
      &quot;HP&quot;: 80,
      &quot;Attack&quot;: 82,
      &quot;Defense&quot;: 83,
      &quot;Sp. Attack&quot;: 100,
      &quot;Sp. Defense&quot;: 100,
      &quot;Speed&quot;: 80
    }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  We can use Pydantic’s alias feature to rename them to a more pythonic versions
  and add an extra layer of validation.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from pydantic import BaseModel, PositiveInt, Field
from typing import List, TypeAlias


class Stats(BaseModel):
    hp: PositiveInt = Field(alias=&quot;HP&quot;)
    attack: PositiveInt = Field(alias=&quot;Attack&quot;)
    sp_attack: PositiveInt = Field(alias=&quot;Sp. Attack&quot;)
    sp_defense: PositiveInt = Field(alias=&quot;Sp. Defense&quot;)
    speed: PositiveInt = Field(alias=&quot;Speed&quot;)


class Pokemon(BaseModel):
    &quot;&quot;&quot;An entry in Pokedex&quot;&quot;&quot;

    id: PositiveInt
    name: str
    type: List[str]
    stats: Stats
    height: str
    weight: str&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Here, we add an import for &lt;code class=&quot;notion-text-code&quot;&gt;Field&lt;/code&gt; and
  create a new validation model &lt;code class=&quot;notion-text-code&quot;&gt;Stats&lt;/code&gt;. For
  each field, we define the key in JSON as
  &lt;code class=&quot;notion-text-code&quot;&gt;alias=&lt;/code&gt; argument and use the new Stats
  model in our Pokemon definition.
&lt;/p&gt;

&lt;p&gt;
  We now have added validation for all the stats and they are in a form that’s
  easier to use within Python code.
&lt;/p&gt;
&lt;h3&gt;Step 4: Parse data into usable format&lt;/h3&gt;
&lt;p&gt;Our height and weight data is in a string format:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;    &quot;height&quot;: &quot;2 m&quot;,
    &quot;weight&quot;: &quot;100 kg&quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  It would be easier to use them if they were stored numerically. Good news! We
  can run code before or after validation to modify the values.
&lt;/p&gt;

&lt;p&gt;Let’s start by writing functions to turn these data points into numbers:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;def parse_height(height: str) -&amp;#62; float:
    height = height.removesuffix(&quot; m&quot;)
    return float(height)


def parse_weight(weight: str) -&amp;#62; float:
    weight = weight.removesuffix(&quot; kg&quot;)
    return float(weight)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  We remove the unit suffixes and convert the rest to a
  &lt;code class=&quot;notion-text-code&quot;&gt;float&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;To run this, we need a few extra bits.&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from pydantic import BaseModel, BeforeValidator, Field, PositiveFloat, PositiveInt
from typing import Annotated, List, TypeAlias

class Pokemon(BaseModel):
    &quot;&quot;&quot;An entry in Pokedex&quot;&quot;&quot;

    id: PositiveInt
    name: str
    type: List[str]
    stats: Stats
    height: Annotated[PositiveFloat, BeforeValidator(parse_height)]
    weight: Annotated[PositiveFloat, BeforeValidator(parse_weight)]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  We import three new things:
  &lt;code class=&quot;notion-text-code&quot;&gt;pydantic.BeforeValidator&lt;/code&gt; ,
  &lt;code class=&quot;notion-text-code&quot;&gt;pydantic.PositiveFloat&lt;/code&gt; and
  &lt;code class=&quot;notion-text-code&quot;&gt;typing.Annotated&lt;/code&gt;. (If we want to run
  code after value is validated, we could use
  &lt;code class=&quot;notion-text-code&quot;&gt;pydantic.AfterValidator&lt;/code&gt;).
&lt;/p&gt;

&lt;p&gt;
  We then define height and weight as annotated floats and want to run their
  corresponding functions.
&lt;/p&gt;

&lt;p&gt;
  Now we can validate that the result of our parsing is a positive float for
  both of them and our code has access to properly typed values.
&lt;/p&gt;

&lt;p&gt;
  If our data used mixed units, this would also help us normalise the data.
  Here, all heights are in meters and weights are in kilograms so we didn’t have
  to do any conversions.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  &lt;b class=&quot;notion-text-bold&quot;&gt;‘Tis but a scratch!&lt;/b&gt;
&lt;/h2&gt;

&lt;p&gt;
  I’ve given you a glimpse into Pydantic’s validation tools and how to get
  started but there’s so much more you can do. They have
  &lt;a href=&quot;https://pydantic.dev/docs/validation/latest/get-started/&quot; class=&quot;notion-text-href&quot;&gt;pretty good documentation&lt;/a&gt;
  that will help you define and refine your validators even further.
&lt;/p&gt;

&lt;p&gt;
  Writing validators is not only good for catching up rogue, misformed data but
  it forces you to be explicit about understanding your data before you venture
  into building your program’s business logic.
&lt;/p&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>I is for immutable - Python A to Z</title>
    <link href="https://hamatti.org/posts/i-is-for-immutable-python-a-to-z/" />
    <updated>2026-08-10T00:00:00Z</updated>
    <id>https://hamatti.org/posts/i-is-for-immutable-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  When we start learning programming, one of the first things we’re taught is a
  concept of &lt;b class=&quot;notion-text-bold&quot;&gt;variable&lt;/b&gt;. Variables are very handy:
  they store some data in memory, have a name you can use to refer to them later
  in the code and they can change.
&lt;/p&gt;

&lt;p&gt;
  That’s what &lt;i class=&quot;notion-text-italic&quot;&gt;variable&lt;/i&gt; means.
  &lt;a href=&quot;https://www.merriam-webster.com/dictionary/variable&quot; class=&quot;notion-text-href&quot;&gt;Merriam-Webster defines the word as&lt;/a&gt;
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  able or apt to vary : subject to variation or changes
&lt;/blockquote&gt;

&lt;p&gt;A classic example is a counter that goes up.&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;counter = 0
print(counter) # 0
counter = counter + 1
print(counter) # 1
counter = counter + 1
print(counter) # 2
counter = counter + 1
print(counter) # 3&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Most of the early days, months or years of learning programming is about
  understanding this idea and a few related bits like different types (numbers,
  strings, lists, dictionaries, booleans etc), conditionals and loops. After you
  learn those, the rest of your career is just banging your head to the wall
  when the simple constructs you wrote don’t work (or
  &lt;a href=&quot;https://hamatti.org/posts/debugging-python/&quot; class=&quot;notion-text-href&quot;&gt;you can talk to a duck and save a headache&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;
  You build your internal schema of how to program based on the idea of a
  variable.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Functional programming&lt;/h2&gt;

&lt;p&gt;
  Then at some point — maybe at an advanced class at the university, at a meetup
  or in the always peaceful and polite discussion forums on the web — you’re
  introduced to the idea of immutability (&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;a href=&quot;https://www.merriam-webster.com/dictionary/immutable&quot; class=&quot;notion-text-href&quot;&gt;not capable of or susceptible to change&lt;/a&gt;&lt;/i&gt;).
&lt;/p&gt;

&lt;p&gt;
  Someone comes hype the idea of
  &lt;a href=&quot;https://en.wikipedia.org/wiki/Functional_programming&quot; class=&quot;notion-text-href&quot;&gt;functional programming&lt;/a&gt;
  that you’ve never heard of. They start talking about how changing variable
  state is so error-prone and
  &lt;a href=&quot;https://en.wikipedia.org/wiki/Pure_function&quot; class=&quot;notion-text-href&quot;&gt;pure functions&lt;/a&gt;
  is where the game should be played at.
&lt;/p&gt;

&lt;p&gt;
  A pure function is a function that given an input, always returns the same
  output. There are no &lt;i class=&quot;notion-text-italic&quot;&gt;side effects&lt;/i&gt;. It only
  relies on the inputs provided (and doesn’t read anything from disk, other
  parts of memory, make any web calls and so on) and is deterministic in
  crafting its output.
&lt;/p&gt;

&lt;p&gt;
  Pure functions are way easier to test, have less unexpected issues and can
  make your code easier to reason with. The hardcore functional programming
  people don’t even store values in temporary variables. They call functions,
  pass the results to other functions and beauty emerges and birds sing.
&lt;/p&gt;

&lt;p&gt;
  But if your entire schema of how programming works was laid on the foundations
  of variables, it can be quite a head scratcher to start adopting. I was on my
  third year at the university studying computer science when I took a
  functional programming course and oh boy it was a doozy.
&lt;/p&gt;

&lt;p&gt;
  I don’t know if that course was a blessing or a curse. There were maybe easier
  ways to get into understanding immutability and pure functions in programming
  but on the other hand, diving directly to the deep end and learning to swim
  might have been a good form of shock therapy.
&lt;/p&gt;

&lt;p&gt;
  I’m not a purist but I do prefer as much functional style as possible — mostly
  to help make code easier to test and thus reducing the stress of growing
  complexity.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Tuples are great, m’kay&lt;/h2&gt;

&lt;p&gt;
  Some data structures are immutable: once you make it, you gotta stick with it.
&lt;/p&gt;

&lt;p&gt;
  In Python, we have
  &lt;a href=&quot;https://docs.python.org/3/library/stdtypes.html#tuple&quot; class=&quot;notion-text-href&quot;&gt;tuples&lt;/a&gt;
  (and their more eloquent heir
  &lt;a href=&quot;https://hamatti.org/posts/improve-your-code-with-namedtuples/&quot; class=&quot;notion-text-href&quot;&gt;namedtuples&lt;/a&gt;). You cannot change the contents of a tuple, no matter how hard you try.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;date = (2026, 8, 10) # parentheses are optional but preferable

# Access them by index
print(date[0]) # 2026

# Can&#39;t change though
date[0] = 2025 
# TypeError: &#39;tuple&#39; object does not support item assignment&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I really like tuples. Especially namedtuples.&lt;/p&gt;

&lt;p&gt;
  They also make me appreciate Python’s beautiful design. All you really need is
  a comma in between two expressions and you’ve got yourself an immutable tuple.
&lt;/p&gt;

&lt;p&gt;If you want to return multiple values from a function, it’s tuple time!&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from typing import Tuple

def squares_and_cubes(number: int) -&amp;#62; Tuple[int, ...]:
  return number, number**2, number**3&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Back in the day, I started with PHP and a bit later with Java at high school.
  When I moved into Python and learned about how easy it is to return multiple
  values through a tuple without having to explicitly craft a tuple, I started
  appreciating the language even more.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>HTML Day and the value of practicing the fundamentals</title>
    <link href="https://hamatti.org/posts/html-day-and-the-value-of-practicing-the-fundamentals/" />
    <updated>2026-08-10T00:00:00Z</updated>
    <id>https://hamatti.org/posts/html-day-and-the-value-of-practicing-the-fundamentals/</id>
    <content type="html">
      &lt;p&gt;
  On August 8th, we celebrated this year’s
  &lt;a href=&quot;https://2026.html.energy/&quot; class=&quot;notion-text-href&quot;&gt;HTML Day&lt;/a&gt;.
  &lt;i class=&quot;notion-text-italic&quot;&gt;(Or more accurately, many people around the world did. I tried to put
    together &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;a href=&quot;https://turkufrontend.fi/html-day-2026/&quot; class=&quot;notion-text-href&quot;&gt;an event for our local community&lt;/a&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    but failed to garner enough interest from speakers and partners and had to
    cancel. My failure left a disappointing taste to my mouth and I failed to
    celebrate the event on my own.)&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Tom wrote a post
  &lt;a href=&quot;https://endgameviable.com/post/2026/08/old-dog-not-understanding-html-day/&quot; class=&quot;notion-text-href&quot;&gt;Old Dog Not Understanding HTML Day&lt;/a&gt;
  where he expressed how he doesn’t get the appeal of HTML Day and writing HTML
  by hand.
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
&lt;p&gt;
  As a professional software engineer and sometimes web developer, you’d think
  I’d be excited about something like HTML Day. But the fact is that I hate
  hand-writing HTML. I avoid it as much as possible.&lt;/p&gt;
  &lt;p&gt;Sorry.&lt;/p&gt;
  &lt;p&gt;Once again I’m in the uncomfortable position of looking at a fun thing people are having fun with and reacting with, “Huh? I don’t get it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  To a certain extent, I agree with him: I dread the idea of writing a full
  website without any HTML generating tooling — outside an event like this. I
  love my
  &lt;a href=&quot;https://docs.emmet.io/&quot; class=&quot;notion-text-href&quot;&gt;Emmet snippets&lt;/a&gt;
  and &lt;a href=&quot;https://jamstack.org/&quot;&gt;static site generators&lt;/a&gt; so I can define the basic structures and let the
  machine go brrrrr and build the rest.
&lt;/p&gt;

&lt;p&gt;
  I especially agree with Tom’s later remark about using Markdown when writing
  instead of mixing creative writing and HTML implementation:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  I find it vastly easier and more intuitive to creatively write in Markdown
  than in HTML, when it comes to simple formatting like italics and bold and
  headings and such.
&lt;/blockquote&gt;

&lt;p&gt;
  The bits that I do write, I want to write myself by hand because it gives me
  greater control to make sure the output is as good, semantic HTML as possible.
&lt;/p&gt;

&lt;h2&gt;Practicing the fundamentals&lt;/h2&gt;
&lt;p&gt;
  First, let’s talk about HTML Day (and by extension, Advent of Code he also
  mentioned).
&lt;/p&gt;

&lt;p&gt;
  I think there’s tremendous value in gathering together as a community of
  practice to learn more about a topic, sharing good ideas and practicing your
  craft.
&lt;/p&gt;

&lt;p&gt;
  Even if we strip out the community benefits and even if you’d mostly generate
  your HTML by some sort of a tool on your day-to-day basis, I think it’s really
  good to regularly get back to the basics and practice them. If you’re not a
  developer who does web frontend, it’s not as important but for anyone who
  does.
&lt;/p&gt;

&lt;p&gt;
  HTML changes over time and it’s good to check what’s up with the standards,
  new options and best practices. When I started getting into frontend stuff
  back in my teens, we used iframes and tables to create layouts — now we have
  &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Flexbox&quot; class=&quot;notion-text-href&quot;&gt;flexbox&lt;/a&gt;
  and
  &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Grid_layout&quot; class=&quot;notion-text-href&quot;&gt;CSS grid&lt;/a&gt;
  as an example. Smaller things happen all the time as well and taking a moment
  every now and then to check where you are is good.
&lt;/p&gt;

&lt;p&gt;
  Second, even if you generate your code, you need to know and have a feel for
  the fundamentals so you can judge if the code that was generated is any good.
&lt;/p&gt;

&lt;p&gt;Tom writes:&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  There’s that pesky elephant in the room in 2026 again. AI. It’s hard to escape
  the universal answer to most development questions: I can just get AI to write
  or check whatever HTML I need so I don’t have to get distracted by troublesome
  details while I stay focused on the big picture. Laziness, hurray!
&lt;/blockquote&gt;

&lt;p&gt;
  The more you use AI to generate your code — in any language — the more you
  should focus on making sure your fundamentals of the language are good so you
  can assess the code it gives you.
&lt;/p&gt;

&lt;p&gt;
  Our industry is famously quite bad at writing good HTML. Especially with the
  popularity of React, there are a lot of frontend developers who don’t know the
  fundamentals and through that lack of knowledge, end up writing inaccessible
  solutions or having to reinvent the wheel while they could rely on built-in
  tools in HTML and browsers to make websites and web apps accessible.
&lt;/p&gt;

&lt;p&gt;
  I don’t want to write all the HTML in my projects by hand every time. However,
  I do find a lot of value in regularly doing so to keep up with new
  advancements in the language as well as honing my fundamental skills.
&lt;/p&gt;

&lt;p&gt;
  I also love this quote from
  &lt;a href=&quot;https://frontendmasters.com/courses/complete-intro-containers-v2/&quot; class=&quot;notion-text-href&quot;&gt;Brian Holt&lt;/a&gt;:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
&lt;p&gt;
  When I don’t know how my tools work, I tend to resent them because they add
  complexity to my life.&lt;/p&gt;&lt;p&gt; When I understand what they are doing for me and what I
  no longer have to do because the tool is doing it for me, I tend to really
  like my tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  Sometimes doing things the hard way makes me appreciate the tooling I have so
  I don’t have to do it every time.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>H is for htmx - Python A to Z</title>
    <link href="https://hamatti.org/posts/h-is-for-htmx-python-a-to-z/" />
    <updated>2026-08-09T00:00:00Z</updated>
    <id>https://hamatti.org/posts/h-is-for-htmx-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  For the past couple of years, all my new Django projects have been built with
  &lt;a href=&quot;https://htmx.org/&quot; class=&quot;notion-text-href&quot;&gt;htmx&lt;/a&gt; and I’ve worked
  on couple of other, non-Django projects that uses it as well. It’s a blast!
&lt;/p&gt;

&lt;p&gt;
  It took me a while to get used to but especially
  &lt;a href=&quot;https://www.youtube.com/watch?v=10G874qqtrc&quot; class=&quot;notion-text-href&quot;&gt;this demo by Michael Kennedy at PyBay 2021&lt;/a&gt;
  convinced me of its utility and looking back now, I love how simple it makes
  building most interactive websites and web apps.
&lt;/p&gt;

&lt;p&gt;
  htmx is a small (16k) Javascript library that makes it possible to build
  modern UIs by transferring HTML over the wire instead of JSON. Combined with
  Django’s templating system, it makes build smooth, fluid frontends with
  server-side rendering.
&lt;/p&gt;

&lt;p&gt;
  Here’s an example from my Pokémon TCG inventory application. I have a
  component for showing a card and a (-) [count] (+) style UI for it.
&lt;/p&gt;

&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;#60;button
	name=&quot;decrease-qty&quot;
	id=&quot;{{card.id}}-{{variant.variant|slugify}}&quot;
	data-foil=&quot;{{variant.variant}}&quot;
	hx-put=&quot;/collection/decrease/{{variant.id}}/&quot;
	hx-target=&quot;input#{{card.id}}-{{variant.variant|slugify}}&quot;
	hx-swap=&quot;outerHTML&quot;
&amp;#62;
-
&amp;#60;/button&amp;#62;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Here’s a button that decreases the amount in my database. It uses three htmx
  attributes: &lt;code class=&quot;notion-text-code&quot;&gt;hx-put&lt;/code&gt;,
  &lt;code class=&quot;notion-text-code&quot;&gt;hx-target&lt;/code&gt; and
  &lt;code class=&quot;notion-text-code&quot;&gt;hx-swap&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  &lt;code class=&quot;notion-text-code&quot;&gt;hx-put&lt;/code&gt; tells the code to send a PUT
  request to the provided endpoint when this button is clicked.
&lt;/p&gt;

&lt;p&gt;
  &lt;code class=&quot;notion-text-code&quot;&gt;hx-target&lt;/code&gt; tells which element the
  returned HTML in the response should go into.
&lt;/p&gt;

&lt;p&gt;
  &lt;code class=&quot;notion-text-code&quot;&gt;hx-swap&lt;/code&gt; tells how to change it: in this
  case, replace the entire HTML element of the target.
&lt;/p&gt;

&lt;p&gt;
  I click the button in the UI, a PUT is sent to the server that updates the
  count in the database and sends back HTML that in this case is an
  &lt;code class=&quot;notion-text-code&quot;&gt;input&lt;/code&gt; with the value of the new
  count (and some extra htmx controls so I can change the value directly in the
  input as well).
&lt;/p&gt;

&lt;hr class=&quot;notion-divider&quot; /&gt;


&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  HTMX 4.0, the first JavaScript library to release exclusively on the Game
  Boy® platform!
&lt;/blockquote&gt;
&lt;p&gt;
  There’s also
  &lt;a href=&quot;https://swag.htmx.org/en-eur/products/htmx-4-the-game&quot; class=&quot;notion-text-href&quot;&gt;a Game Boy game by htmx&lt;/a&gt;
  and
  &lt;a href=&quot;https://sethmlarson.dev/htmx-4-the-game&quot; class=&quot;notion-text-href&quot;&gt;Seth Larson made a video and wrote a blog post about it&lt;/a&gt;.
&lt;/p&gt;



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

    </content>
  </entry>
   
  
  <entry>
    <title>G is for get_or_create - Python A to Z</title>
    <link href="https://hamatti.org/posts/g-is-for-get-or-create-python-a-to-z/" />
    <updated>2026-08-08T00:00:00Z</updated>
    <id>https://hamatti.org/posts/g-is-for-get-or-create-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Let’s take our first journey into the wonders of
  &lt;a href=&quot;https://www.djangoproject.com/&quot; class=&quot;notion-text-href&quot;&gt;Django&lt;/a&gt;,
  my favourite web backend. One of the reasons I love it is its ORM which we’ll
  talk a bit more when we reach the midway of the month and letter O.
&lt;/p&gt;

&lt;p&gt;
  Today, I want to show my appreciation for one of the methods in QuerySet:
  &lt;a href=&quot;https://docs.djangoproject.com/en/6.1/ref/models/querysets/#django.db.models.query.QuerySet.get_or_create&quot; class=&quot;notion-text-href&quot;&gt;get_or_create&lt;/a&gt;
  which allows you to create new items in the database but if one already
  exists, it returns it instead.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from .models import Bread, BreadType

# (here, Bread.bread_type is a ForeignKey to BreadType)

breads_input = [{ &#39;id&#39;: 1, &#39;breadType&#39;: &#39;bun&#39; } , ... ]

for bread_data in breads:
	bread_type, type_created = BreadType.objects.get_or_create(type=bread_data[&#39;breadType&#39;])
	bread, bread_created = Bread.objects.get_or_create(id=bread_data[&#39;id&#39;], bread_type=bread_type)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Here we process through some list of dictionaries (imagine it’s read from a
  JSON file or queried from a REST API). First, we try to create a new BreadType
  but if one already exists, we return a reference to it rather than creating a
  new one every time. Then we create a bread, using the previous reference. If
  we’d already have that specific bread inputted (maybe we ran the command
  twice), we want to get the existing one rather than creating new ones every
  time.
&lt;/p&gt;

&lt;p&gt;
  I especially love using it in my data entry scripts / custom Django commands.
  Many of my applications operate on a model where I periodically input new data
  through these commands: for example in Pokémon TCG app, when a new set is
  released.
&lt;/p&gt;

&lt;p&gt;
  Pokémon TCG is a great example. Right now, when my script reads in data for
  new cards, there are 13 such relationships between my Card model and other
  models. The codebase looks so much neater and easier to follow thanks to
  &lt;code class=&quot;notion-text-code&quot;&gt;get_or_create.&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit later on Aug 8th&lt;/strong&gt;: I rewrote the entire post to better explain what &lt;code&gt;get_or_create&lt;/code&gt; does.&lt;/p&gt;
    </content>
  </entry>
   
   
  
  <entry>
    <title>My custom keyword reference guide for Magic the Gathering Battle Box</title>
    <link href="https://hamatti.org/posts/my-custom-keyword-reference-guide-for-magic-the-gathering-battle-box/" />
    <updated>2026-08-07T00:00:00Z</updated>
    <id>https://hamatti.org/posts/my-custom-keyword-reference-guide-for-magic-the-gathering-battle-box/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;I’ll start by playing a &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;a href=&quot;https://scryfall.com/card/blb/279/forest&quot; class=&quot;notion-text-href&quot;&gt;Forest&lt;/a&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;.&lt;/i&gt;
&lt;/p&gt;
&lt;img src=&quot;https://hamatti.org/assets/img/posts/my-custom-keyword-reference-guide-for-magic-the-gathering-battle-box/1.png.webp&quot; alt=&quot;A full art Bloomburrow Magic the Gathering card Forest played on a playmat&quot; /&gt;
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;a href=&quot;https://youtu.be/OwUDLv52nrc?t=145&quot; class=&quot;notion-text-href&quot;&gt;Sure, it resolves.&lt;/a&gt;&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;I’ll tap the Forest to generate green mana and use it to cast &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Battle Box keyword reference guide&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;.&lt;/i&gt;
&lt;/p&gt;
&lt;img src=&quot;https://hamatti.org/assets/img/posts/my-custom-keyword-reference-guide-for-magic-the-gathering-battle-box/2.png.webp&quot; alt=&quot;The Forest is tapped sideways and a booklet titled Battle Box keyword reference guide is played above it on the playmat.&quot; /&gt;
&lt;p&gt;
  I love
  &lt;a href=&quot;https://notes.hamatti.org/gaming/board-games/magic-the-gathering/mtg-battle-box&quot; class=&quot;notion-text-href&quot;&gt;Magic the Gathering Battle Box&lt;/a&gt;. It’s a great casual pick-and-play format where I can bring it with me to a
  lunch or an afterwork, shuffle the deck and start playing.
&lt;/p&gt;

&lt;p&gt;
  No need for everyone to bring their own deck, no long-winded drafting
  (although drafting cubes is hella fun) and deck construction. No mana drought
  or mana flood but a perfect curve.
&lt;/p&gt;

&lt;p&gt;
  A single, shared deck full of creatures and other spells that mostly focuses
  on combat and combat tricks. Each player has 10 lands (1 of each basic and 5
  dual-lands) they can play from Command Zone (I’m considering calling it
  Landfill for the memes).
&lt;/p&gt;

&lt;p&gt;There’s just one catch.&lt;/p&gt;

&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;a href=&quot;https://notes.hamatti.org/gaming/board-games/magic-the-gathering/reading-the-card-explains-the-card&quot; class=&quot;notion-text-href&quot;&gt;“Reading the card explains the card”&lt;/a&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt; &lt;/i&gt;doesn’t always work. With casual players
  and a bunch of unexplained keywords (like Menace, Madness, Shadow, Surveil),
  it can be bit of a burden to figure out what the cards do.
&lt;/p&gt;

&lt;p&gt;
  That’s why I compiled my own Battle Box keyword reference guide (see the thing
  I did in my intro where I used a &lt;i class=&quot;notion-text-italic&quot;&gt;Forest&lt;/i&gt; to
  cast a &lt;i class=&quot;notion-text-italic&quot;&gt;booklet&lt;/i&gt;. smart, huh?): it lists all
  41 keywords that are present in my deck in alphabetical order and explains how
  they work. And the back cover has a quick guide for what “instant speed” and
  “sorcery speed” mean and when you can activate abilities and mana abilities.
&lt;/p&gt;

&lt;p&gt;
  Now, instead of players having to expose what they have in hand and ask me or
  awkwardly search the wiki on their phone, they can pick up the guide and
  check.
&lt;/p&gt;

&lt;p&gt;It was such a fun project and it looks gorgeous.&lt;/p&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>F is for f-strings - Python A to Z</title>
    <link href="https://hamatti.org/posts/f-is-for-f-strings-python-a-to-z/" />
    <updated>2026-08-07T00:00:00Z</updated>
    <id>https://hamatti.org/posts/f-is-for-f-strings-python-a-to-z/</id>
    <content type="html">
      
&lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Programming languages have many ways to combine literal strings and variables.
  For a decade now, we in Python land have been able to enjoy the beauty and
  elegance of
  &lt;a href=&quot;https://docs.python.org/3/reference/lexical_analysis.html#f-strings&quot; class=&quot;notion-text-href&quot;&gt;f-strings&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;f-string is a string that is prefixed with the letter f:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;string_type = &#39;f-string&#39;
print(f&#39;This is a {string_type}&#39;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  When you craft an f-string, you can evaluate Python code inside it by placing
  it inside curly braces `{ … }`.
&lt;/p&gt;

&lt;p&gt;
  In addition to simply putting in a variable, there are a bunch of format
  specifications that allow further modification of the output.
  &lt;a href=&quot;https://fstring.help/cheat/&quot; class=&quot;notion-text-href&quot;&gt;fstring.help&lt;/a&gt;
  has a really handy reference sheet for these but here are some that I use
  regularly.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Padding numbers&lt;/h2&gt;

&lt;p&gt;
  File browsers usually default to sorting alphabetically so if you have files
  &lt;code class=&quot;notion-text-code&quot;&gt;1.json&lt;/code&gt;,
  &lt;code class=&quot;notion-text-code&quot;&gt;2.json&lt;/code&gt; and
  &lt;code class=&quot;notion-text-code&quot;&gt;10.json&lt;/code&gt;, the 10 will sort before the 2.
  So to keep things neat and tidy, I often left-pad with zeroes to force proper
  sorting:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;for filename in range(1, 20):
  print(f&#39;{filename:02d}&#39;)
  
# Prints 01, 02, 03, ... 20&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Here &lt;code class=&quot;notion-text-code&quot;&gt;0&lt;/code&gt; is the value we use to fill and
  &lt;code class=&quot;notion-text-code&quot;&gt;2&lt;/code&gt; is the width to how many characters we
  want to pad up to.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Aligning text&lt;/h2&gt;

&lt;p&gt;
  When I build command line tools, I want a well-defined output structure and
  aligning items left or right with a given width is helpful.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;name, goals = &#39;Juhis&#39;, 20
print(f&#39;{name:&amp;#60;10}{goals:&amp;#62;5}&#39;)
# &#39;Juhis        20&#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  &lt;code class=&quot;notion-text-code&quot;&gt;&lt;&lt;/code&gt; is for left-align,
  &lt;code class=&quot;notion-text-code&quot;&gt;&gt;&lt;/code&gt; is for right-align,
  &lt;code class=&quot;notion-text-code&quot;&gt;^&lt;/code&gt; would be for center and the number is
  the width. In this example, it guarantees that the name field is at least 10
  characters and goals field is 5.
&lt;/p&gt;

&lt;p&gt;
  You can combine this with the previous example if you want to fill the empty
  space with something else, like underscore:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;item, value = &#39;Cucumber&#39;, 14
print(f&#39;{item:_&amp;#60;10}{value:_&amp;#62;5}&#39;)
# Cucumber_____14&lt;/code&gt;&lt;/pre&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Debugging&lt;/h2&gt;

&lt;p&gt;
  For debugging, I use two formats. (Btw, if you wanna get better at debugging,
  check out
  &lt;a href=&quot;https://hamatti.org/posts/debugging-python/&quot; class=&quot;notion-text-href&quot;&gt;Debugging Python&lt;/a&gt;
  and if you’re curious about why printing is a good debug tool, read
  &lt;a href=&quot;https://hamatti.org/posts/print-is-your-best-debugging-tool/&quot; class=&quot;notion-text-href&quot;&gt;print is your best debugging tool&lt;/a&gt;.)
&lt;/p&gt;

&lt;p&gt;
  The first one uses &lt;code class=&quot;notion-text-code&quot;&gt;=&lt;/code&gt; sign to print out
  the name of the variable and its value separated with an equal sign. It’s a
  short hand for &lt;code class=&quot;notion-text-code&quot;&gt;f&#39;value={value}&#39;&lt;/code&gt; .
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;unknown = 400
print(f&#39;{unknown=}, {unknown = }&#39;) # Whitespace is preserved
# unknown=400, unknown = 400&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The second one uses &lt;code class=&quot;notion-text-code&quot;&gt;!r&lt;/code&gt; to print out
  object’s &lt;code class=&quot;notion-text-code&quot;&gt;__repr__&lt;/code&gt; value.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;class Card:
	def __init__(self, name, number):
	  self.name = name
	  self.number = number
	  
	def __repr__(self):
	  return f&#39;Card(&quot;{self.name}&quot;, {self.number})&#39;

card = Card(&#39;Pikachu&#39;, 25)
print(f&#39;{card!r}&#39;)
# Card(&quot;Pikachu&quot;, 25)&lt;/code&gt;&lt;/pre&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Percentages&lt;/h2&gt;

&lt;p&gt;
  The last one is actually something I didn’t know about before I started
  writing this but can be handy. You can convert floats to percentages with
  &lt;code class=&quot;notion-text-code&quot;&gt;:.0%&lt;/code&gt;:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;percent = 0.3738
print(f&#39;{percent:.0%}&#39;)
# 37%

print(f&#39;{percent:.2%}&#39;)
# 37.38%&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It’s pretty handy!&lt;/p&gt;


    </content>
  </entry>
   
  
  <entry>
    <title>E is for environment variables - Python A to Z</title>
    <link href="https://hamatti.org/posts/e-is-for-environment-variables-python-a-to-z/" />
    <updated>2026-08-06T00:00:00Z</updated>
    <id>https://hamatti.org/posts/e-is-for-environment-variables-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  The code that a program runs is provided by the developer of the program. The
  inputs and interactions are initiated by the user of the program. The
  environment variables are data provided by the system that runs it. They are
  used to abstract configuration to the system level so that the same code can
  be ran in different environments and act differently (for example, development
  vs production environments).
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Accessing environment variables
&lt;/h2&gt;

&lt;p&gt;
  In Python, you can access environment variables through
  &lt;a href=&quot;https://docs.python.org/3/library/os.html#os.environ&quot; class=&quot;notion-text-href&quot;&gt;os.environ dictionary&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  If you run the following in a REPL, you’ll see all the environment variables
  Python has access to.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&amp;#62;&amp;#62;&amp;#62; for variable, value in os.environ.items():
...     print(f&#39;{variable}: {value}&#39;)
...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The &lt;code class=&quot;notion-text-code&quot;&gt;os.environ&lt;/code&gt; is created at the time
  Python started and won’t update to reflect changes in environment variables
  that happen while the program is running. You can use
  &lt;a href=&quot;https://docs.python.org/3/library/os.html#os.reload_environ&quot; class=&quot;notion-text-href&quot;&gt;os.reload_environ&lt;/a&gt;
  to update the values.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Automatically read variables from .env
&lt;/h2&gt;

&lt;p&gt;
  It’s common to store project-specific configuration values in a
  &lt;code class=&quot;notion-text-code&quot;&gt;.env&lt;/code&gt; file instead of managing the
  operating system level variables and having to remember to do that on every
  machine when they change.
&lt;/p&gt;

&lt;p&gt;
  To automatically read the values from
  &lt;code class=&quot;notion-text-code&quot;&gt;.env&lt;/code&gt;, you can use
  &lt;a href=&quot;https://pypi.org/project/python-dotenv/&quot; class=&quot;notion-text-href&quot;&gt;python-dotenv&lt;/a&gt;.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# Example from dotenv documentation
# https://pypi.org/project/python-dotenv/

from dotenv import load_dotenv

load_dotenv()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Environment variables are often used to store secrets and accidentally leaking
  such secrets can be catastrophic. There’s a command line tool
  &lt;a href=&quot;https://dotenvx.com/docs/secrets-in-python&quot; class=&quot;notion-text-href&quot;&gt;dotenvx&lt;/a&gt;
  that enables you to encrypt your secrets and then inject them to Python
  programs at runtime.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>D is for dictionaries - Python A to Z</title>
    <link href="https://hamatti.org/posts/d-is-for-dictionaries-python-a-to-z/" />
    <updated>2026-08-05T00:00:00Z</updated>
    <id>https://hamatti.org/posts/d-is-for-dictionaries-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://docs.python.org/3/library/stdtypes.html#dict&quot; class=&quot;notion-text-href&quot;&gt;Dictionary&lt;/a&gt;
  is a data structure in Python that in other contexts and languages is called
  &lt;a href=&quot;https://en.wikipedia.org/wiki/Associative_array&quot; class=&quot;notion-text-href&quot;&gt;an associative array&lt;/a&gt;, akey-value store or a map. It’s a handy and efficient data structure for
  when you need to store and retrieve a value based on a key.
&lt;/p&gt;

&lt;p&gt;This is a 101 level introduction to dictionaries.&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Dictionary&lt;/h2&gt;
&lt;h3&gt;Creating a dictionary&lt;/h3&gt;
&lt;p&gt;The basic form of dictionary can be created in couple of ways:&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# dict constructor with keyword arguments
scores = dict(Charlie=10, Patty=25, Snoopy=30)

# dict constructor with tuples
scores = dict((&#39;Charlie&#39;, 10), (&#39;Patty&#39;, 25), (&#39;Snoopy&#39;, 30))

# key: value pairs
scores = { &#39;Charlie&#39;: 10, &#39;Patty&#39;: 25, &#39;Snoopy&#39;: 30 }

# dict comprehension
data = [(&#39;Charlie&#39;, 10), (&#39;Patty&#39;, 25), (&#39;Snoopy&#39;, 30)]
scores = { key: value for key, value in data }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  In each of these cases, the names are &lt;b class=&quot;notion-text-bold&quot;&gt;keys&lt;/b&gt; and
  numbers are &lt;b class=&quot;notion-text-bold&quot;&gt;values.&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
  There’s one big restriction to the keys of dictionaries: they need to be
  &lt;a href=&quot;https://docs.python.org/3/glossary.html#term-hashable&quot; class=&quot;notion-text-href&quot;&gt;hashable&lt;/a&gt;:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  An object is hashable if it has a hash value which never changes during its
  lifetime (it needs a &lt;code class=&quot;notion-text-code&quot;&gt;__hash__()&lt;/code&gt; method),
  and can be compared to other objects (it needs an
  &lt;code class=&quot;notion-text-code&quot;&gt;__eq__()&lt;/code&gt; method). Hashable objects which
  compare equal must have the same hash value.
&lt;/blockquote&gt;

&lt;p&gt;
  So you can’t use a &lt;code class=&quot;notion-text-code&quot;&gt;list&lt;/code&gt; as a key for
  example because it’s a mutable one.
&lt;/p&gt;
&lt;h3&gt;Accessing values&lt;/h3&gt;
&lt;p&gt;
  Regardless of what was used to create it, we can access the values in a couple
  of ways:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;scores = { &#39;Charlie&#39;: 10, &#39;Patty&#39;: 25, &#39;Snoopy&#39;: 30 }

# With brackets
scores[&#39;Charlie&#39;] # == 10

# With .get()
scores.get(&#39;Charlie&#39;) # == 10&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The difference between these two is what happens when a key does not exist.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;scores[&#39;Linus&#39;]  # raises KeyError: &#39;Linus&#39;
scores.get(&#39;Linus&#39;)  # is None

# We can give .get() a default value
scores.get(&#39;Linus&#39;, 0)  # == 0&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Changing values&lt;/h3&gt;
&lt;p&gt;
  Dictionaries are &lt;i class=&quot;notion-text-italic&quot;&gt;mutable&lt;/i&gt; data structures
  which means you can change their data.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# Set new value
scores[&#39;Charlie&#39;] = 15

# Modify a value
scores[&#39;Charlie&#39;] += 5

# Delete a key
del scores[&#39;Charlie&#39;]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Since there is a direct “one key to one value” relationship, dictionaries are
  very handy in collecting data for or counting values belonging to this key.
&lt;/p&gt;

&lt;p&gt;
  Let’s say we have a dataset where each time someone scores a point, their name
  is listed. We then want to count how may points each has:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;marks = [
  &#39;Charlie&#39;, &#39;Charlie&#39;, &#39;Patty&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;, &#39;Charlie&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;
]

scores = {} # Creating an empty dict
for person in marks: # Go through every mark
  if person not in scores: # If it&#39;s not in the dict yet,
    scores[person] = 0 # create an entry with starting value 0
  
  scores[person] += 1 # Add 1 point to this person
  
print(scores)
# {&#39;Charlie&#39;: 3, &#39;Patty&#39;: 1, &#39;Snoopy&#39;: 4}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  We’ll look a bit later how we can improve this with some of the special
  dictionaries but this basic structure of turning data into a dictionary is a
  fundamental basic to learn in Python.
&lt;/p&gt;

&lt;p&gt;
  While the key needs to be hashable, the values can be anything. So instead of
  just increasing a number from zero upwards, we could store data as a list or
  another dictionary or whatever.
&lt;/p&gt;
&lt;h3&gt;Looping over&lt;/h3&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# By default, loops over keys in for-in
for person in scores:
  print(person, scores[person])
  
# Loop over values
for score in scores.values():
  print(score)
  
# Loop over both
for person, score in scores.items():
  print(person, score)&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Sorting a dictionary&lt;/h3&gt;
&lt;p&gt;
  On a concept level, basic mapping does not have an order. In Python,
  dictionaries maintain the order the keys are inserted in and there are ways to
  force an order (like
  &lt;a href=&quot;https://docs.python.org/3/library/collections.html#collections.OrderedDict&quot; class=&quot;notion-text-href&quot;&gt;OrderedDict&lt;/a&gt;) but it’s a good baseline to base your knowledge of dictionaries on.
&lt;/p&gt;

&lt;p&gt;
  However, when looping over a dictionary, you often want the data to be in some
  sort of order. In the earlier example when we counted marks to a dictionary,
  we might want to print it in an order of most points to least.
&lt;/p&gt;

&lt;p&gt;
  For this, we can use
  &lt;a href=&quot;https://docs.python.org/3/library/functions.html#sorted&quot; class=&quot;notion-text-href&quot;&gt;sorted&lt;/a&gt;
  function:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# Let&#39;s recreate our dictionary
scores = {&#39;Charlie&#39;: 3, &#39;Patty&#39;: 1, &#39;Snoopy&#39;: 4}

for person in sorted(scores, key=scores.get, reverse=True):
  print(f&#39;{person}: {scores[person]}&#39;)
  
# Snoopy: 4
# Charlie: 3
# Patty: 1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  The first argument to &lt;code class=&quot;notion-text-code&quot;&gt;sorted&lt;/code&gt; is in this
  case our dictionary. The second key,
  &lt;code class=&quot;notion-text-code&quot;&gt;key=&lt;/code&gt; is a single argument function that
  defines what is used for sorting (in this case,
  &lt;code class=&quot;notion-text-code&quot;&gt;scores.get&lt;/code&gt; tells the function we want
  the values corresponding to the keys) and
  &lt;code class=&quot;notion-text-code&quot;&gt;reverse=&lt;/code&gt; can be used to reverse the
  order from ascending to descending.
&lt;/p&gt;

&lt;p&gt;
  It’s important to note that nothing internally in the dictionary changes when
  using &lt;code class=&quot;notion-text-code&quot;&gt;sorted&lt;/code&gt;. It returns a new list with
  the keys sorted based on the sorting criteria.
&lt;/p&gt;
&lt;h3&gt;You’ll do just fine with dictionary for a long time&lt;/h3&gt;
&lt;p&gt;
  Next, I’ll introduce some specialised dictionaries that are included in the
  standard library because developers have found these cases very useful. If
  you’re a new developer, I do recommend focusing on using and understanding the
  basic dictionary.
&lt;/p&gt;

&lt;p&gt;
  There’s nothing in the following dictionaries that you can’t do in the basic
  one.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://frontendmasters.com/courses/complete-intro-containers-v2/&quot; class=&quot;notion-text-href&quot;&gt;Brian Holt once said this in one of his containers course&lt;/a&gt;:
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  &lt;p&gt;When I don’t know how my tools work, I tend to resent them because they add
  complexity to my life. &lt;/p&gt;&lt;p&gt; When I understand what they are doing for me
  and what I no longer have to do because the tool is doing it for me, I tend to
  really like my tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  Jumping into the more advanced use cases before understanding the basics
  really well can be detrimental. Finish this blog post to see what’s out there
  but don’t be afraid to manually write the code they would let you skip over
  and over again so that you’ll gain a deeper understanding.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;defaultdict&lt;/h2&gt;

&lt;p&gt;
  In the earlier example of creating and populating a dictionary, we had to
  check if a key existed before we could interact with it:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;marks = [
  &#39;Charlie&#39;, &#39;Charlie&#39;, &#39;Patty&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;, &#39;Charlie&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;
]

scores = {} # Creating an empty dict
for person in marks: # Go through every mark
  if person not in scores: # If it&#39;s not in the dict yet,
    scores[person] = 0 # create an entry with starting value 0
  
  scores[person] += 1 # Add 1 point to this person
  
print(scores)
# {&#39;Charlie&#39;: 3, &#39;Patty&#39;: 1, &#39;Snoopy&#39;: 4}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  There’s a way to shortcut this by using a
  &lt;a href=&quot;https://docs.python.org/3/library/collections.html#collections.defaultdict&quot; class=&quot;notion-text-href&quot;&gt;defaultdict&lt;/a&gt;:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from collections import defaultdict

marks = [
  &#39;Charlie&#39;, &#39;Charlie&#39;, &#39;Patty&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;, &#39;Charlie&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;
]

# Create a new dictionary with
# default value of 0 for each key
scores = defaultdict(int) 

for person in marks:
  scores[person] += 1
  
print(scores)
# defaultdict(&amp;#60;class &#39;int&#39;&amp;#62;, {&#39;Charlie&#39;: 3, &#39;Patty&#39;: 1, &#39;Snoopy&#39;: 4})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Passing &lt;code class=&quot;notion-text-code&quot;&gt;int&lt;/code&gt; to
  &lt;code class=&quot;notion-text-code&quot;&gt;defaultdict&lt;/code&gt; tells the dictionary to
  start from a default value of 0. You could also pass
  &lt;code class=&quot;notion-text-code&quot;&gt;list&lt;/code&gt; and the default would be an empty
  list. As you can see comparing the two examples above, the code becomes way
  easier to read and comprehend.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Counter&lt;/h2&gt;

&lt;p&gt;
  Our example of counting things is such a common operation that there’s a
  special
  &lt;a href=&quot;https://docs.python.org/3/library/collections.html#collections.Counter&quot; class=&quot;notion-text-href&quot;&gt;Counter&lt;/a&gt;
  dictionary for it.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;from collections import Counter

marks = [
  &#39;Charlie&#39;, &#39;Charlie&#39;, &#39;Patty&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;, &#39;Charlie&#39;, 
  &#39;Snoopy&#39;, &#39;Snoopy&#39;
]

scores = Counter(marks)

print(scores)
# Counter({&#39;Snoopy&#39;: 4, &#39;Charlie&#39;: 3, &#39;Patty&#39;: 1})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Not only does &lt;code class=&quot;notion-text-code&quot;&gt;Counter&lt;/code&gt; help us in
  creation of it but it has a couple of really handy methods.
&lt;/p&gt;

&lt;p&gt;
  I have
  &lt;a href=&quot;https://hamatti.org/posts/count-em/&quot; class=&quot;notion-text-href&quot;&gt;written about Counter before&lt;/a&gt;
  and rather than repeating myself here, I recommend reading through that post.
&lt;/p&gt;

    </content>
  </entry>
   
  
  <entry>
    <title>C is for command line interfaces - Python A to Z</title>
    <link href="https://hamatti.org/posts/c-is-for-command-line-interfaces-python-a-to-z/" />
    <updated>2026-08-04T00:00:00Z</updated>
    <id>https://hamatti.org/posts/c-is-for-command-line-interfaces-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Python is a great language for writing scripts that are executed from the
  command line. I’m
  &lt;a href=&quot;https://hamatti.org/posts/why-i-love-command-line/&quot; class=&quot;notion-text-href&quot;&gt;a big fan of command line interfaces&lt;/a&gt;
  and love building them for all sorts of needs both personally and when I’m
  part of a team.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Command Line Interface Guidelines
&lt;/h2&gt;

&lt;p&gt;
  An integral part of command line software is dealing with arguments and
  options. In this post, I’ll focus on the technical part within the Python code
  but I highly recommend reading
  &lt;a href=&quot;https://clig.dev/&quot; class=&quot;notion-text-href&quot;&gt;Command Line Interface Guidelines&lt;/a&gt;. It’s an open source guide to “help you write better command-line programs,
  taking traditional UNIX principles and updating them for the modern day”. I
  keep it at hand and always refer to it when I’m designing my interfaces.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Simple arguments through &lt;code class=&quot;notion-text-code&quot;&gt;sys.argv&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;
  For the simplest use cases, accessing the list of arguments through
  &lt;a href=&quot;https://docs.python.org/3/library/sys.html#sys.argv&quot; class=&quot;notion-text-href&quot;&gt;sys.argv&lt;/a&gt;
  is the best option. I usually use it at the beginning while I’m still forming
  the ideas of what the tool will look like or when the script only requires one
  or two positional arguments.
&lt;/p&gt;

&lt;p&gt;Let’s say we run a script with the following:&lt;/p&gt;

&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;python script.py 2 file.txt&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  To access those arguments, we write
  &lt;code class=&quot;notion-text-code&quot;&gt;&lt;a href=&quot;http://script.py/&quot; class=&quot;notion-text-href&quot;&gt;script.py&lt;/a&gt;&lt;/code&gt;
  to be:
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;import sys

script_name = sys.argv[0]  # script.py
copies = sys.argv[1]  # 2
filename = sys.argv[2]  # file.py&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  You can’t trust the user to always provide the right amount of arguments
  though so it’s a good idea to add some checks and instruct the user how to use
  it properly.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;import sys

if __name__ == &quot;__main__&quot;:
    arguments = sys.argv
    if len(arguments) &amp;#60; 3:
        print(&quot;&quot;&quot;Two arguments required for the amount of copies and the filename.
    
Usage; script.py &amp;#60;amount of copies&amp;#62; &amp;#60;filename&amp;#62;&quot;&quot;&quot;)
        sys.exit(1)

    copies = arguments[1]
    filename = arguments[2]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  This provides a top level check AND documentation at the same time and is a
  great habit to get into.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;
  Documentation-first approach with docopt
&lt;/h2&gt;

&lt;p&gt;
  Speaking of documentation,
  &lt;a href=&quot;http://docopt.org/&quot; class=&quot;notion-text-href&quot;&gt;docopt&lt;/a&gt; and its
  maintained Python implementation
  &lt;a href=&quot;https://github.com/jazzband/docopt-ng&quot; class=&quot;notion-text-href&quot;&gt;docopt-ng&lt;/a&gt;
  provide a documentation-driven way for declaring the command line interface.
&lt;/p&gt;

&lt;p&gt;
  Defining the interface first leads to better user experience. In my
  experience, starting with implementation details often leads to either
  suboptimal interfaces or spaghetti code to try to mend an implementation to
  interface later. If you start by thinking how the user should interact with
  the application, then the rest follows to fill the gaps in an elegant way.
&lt;/p&gt;

&lt;p&gt;
  Using docopt, you write the usage pattern in your docstring and let the tool
  figure out what arguments and options it needs to derive from it (example from
  docs):
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&quot;&quot;&quot;Naval Fate.

Usage:
  naval_fate.py ship new &amp;#60;name&amp;#62;...
  naval_fate.py ship &amp;#60;name&amp;#62; move &amp;#60;x&amp;#62; &amp;#60;y&amp;#62; [--speed=&amp;#60;kn&amp;#62;]
  naval_fate.py ship shoot &amp;#60;x&amp;#62; &amp;#60;y&amp;#62;
  naval_fate.py mine (set|remove) &amp;#60;x&amp;#62; &amp;#60;y&amp;#62; [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=&amp;#60;kn&amp;#62;  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

&quot;&quot;&quot;
from docopt import docopt


if __name__ == &#39;__main__&#39;:
    arguments = docopt(__doc__, version=&#39;Naval Fate 2.0&#39;)
    print(arguments)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  &lt;a href=&quot;https://hamatti.org/posts/documentation-driven-command-line-tools-in-python-with-docopt/&quot; class=&quot;notion-text-href&quot;&gt;I have written about docopt&lt;/a&gt;
  a few years ago and that post still holds water so I recommend you go read
  that for more information.
&lt;/p&gt;

&lt;h2 class=&quot;notion-heading_2 notion-color-default&quot;&gt;Click&lt;/h2&gt;

&lt;p&gt;
  The library I use most often these days — because it works so nicely with
  custom Django commands with
  &lt;a href=&quot;https://github.com/django-commons/django-click&quot; class=&quot;notion-text-href&quot;&gt;django-click&lt;/a&gt;
  — is
  &lt;a href=&quot;https://click.palletsprojects.com/en/stable/&quot; class=&quot;notion-text-href&quot;&gt;click&lt;/a&gt;. Click uses Python decorators to define commands, subcommands, arguments and
  options.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# Example from click documentation
# https://click.palletsprojects.com/en/stable/

import click

@click.command()
@click.option(&#39;--count&#39;, default=1, help=&#39;Number of greetings.&#39;)
@click.option(&#39;--name&#39;, prompt=&#39;Your name&#39;,
              help=&#39;The person to greet.&#39;)
def hello(count, name):
    &quot;&quot;&quot;Simple program that greets NAME for a total of COUNT times.&quot;&quot;&quot;
    for x in range(count):
        click.echo(f&quot;Hello {name}!&quot;)

if __name__ == &#39;__main__&#39;:
    hello()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  You can add
  &lt;a href=&quot;https://click.palletsprojects.com/en/stable/advanced/#callbacks-for-validation&quot; class=&quot;notion-text-href&quot;&gt;extra validation through callbacks&lt;/a&gt;
  directly at this level which makes the actual code of your application a bit
  cleaner when the validation and first level input processing is done before
  the arguments and options ever reach your application logic.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;# Example from click documentation
# https://click.palletsprojects.com/en/stable/advanced/#callbacks-for-validation

def validate_rolls(ctx, param, value):
    if isinstance(value, tuple):
        return value

    try:
        rolls, _, dice = value.partition(&quot;d&quot;)
        return int(dice), int(rolls)
    except ValueError:
        raise click.BadParameter(&quot;format must be &#39;NdM&#39;&quot;)

@click.command()
@click.option(
    &quot;--rolls&quot;, type=click.UNPROCESSED, callback=validate_rolls,
    default=&quot;1d6&quot;, prompt=True,
)
def roll(rolls):
    sides, times = rolls
    click.echo(f&quot;Rolling a {sides}-sided dice {times} time(s)&quot;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  With django-click, you can use the same interface to define an interface for
  your custom commands.
&lt;/p&gt;

&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;import djclick as click

@click.command()
@click.option(&quot;--file&quot;, help=&quot;Load set from a local JSON file&quot;)
def command(file):
	...&lt;/code&gt;&lt;/pre&gt;

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

    </content>
  </entry>
   
  
  <entry>
    <title>B is for batteries included - Python A to Z</title>
    <link href="https://hamatti.org/posts/b-is-for-batteries-included-python-a-to-z/" />
    <updated>2026-08-03T00:00:00Z</updated>
    <id>https://hamatti.org/posts/b-is-for-batteries-included-python-a-to-z/</id>
    <content type="html">
      &lt;p&gt;
  &lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Python A-Z&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is a blog series about Python. Each day, I share insights, ideas and
    examples for different parts of Python development that match with the
    letter of the day. &lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;&lt;b class=&quot;notion-text-bold&quot;&gt;Blaugust&lt;/b&gt;&lt;/i&gt;&lt;i class=&quot;notion-text-italic&quot;&gt;
    is an annual blogging festival in August where the goal is to write a blog
    post every day of the month.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  Python is often described as having
  &lt;b class=&quot;notion-text-bold&quot;&gt;batteries included&lt;/b&gt;
&lt;/p&gt;

&lt;blockquote class=&quot;notion-quote notion-color-default&quot;&gt;
  The Python source distribution has long maintained the philosophy of
  “batteries included” – having a rich and versatile standard library which is
  immediately available, without making the user download separate packages.
  This gives the Python language a head start in many projects. - from
  &lt;a href=&quot;https://peps.python.org/pep-0206/&quot; class=&quot;notion-text-href&quot;&gt;withdrawn PEP 206&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  I love Python’s batteries included philosophy and its standard library so much
  that I dedicated an entire
  &lt;a href=&quot;https://hamatti.org/blog/batteries-included/&quot; class=&quot;notion-text-href&quot;&gt;Blaugust in 2024&lt;/a&gt;
  writing about it:
&lt;/p&gt;
&lt;ul class=&quot;notion-bulleted_list&quot;&gt;
  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/parsing-nginx-server-logs-with-regular-expressions/&quot; class=&quot;notion-text-href&quot;&gt;Parsing nginx server logs with regular expressions&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/reading-and-writing-csv-with-python/&quot; class=&quot;notion-text-href&quot;&gt;Reading and writing CSV with Python&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/improve-your-code-with-namedtuples/&quot; class=&quot;notion-text-href&quot;&gt;Improve your code with namedtuples&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/count-em/&quot; class=&quot;notion-text-href&quot;&gt;Count ‘em&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/help-me-obi-wan-kenobi-youre-my-only-hope/&quot; class=&quot;notion-text-href&quot;&gt;help() me Obi-Wan Kenobi, you’re my only hope&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/improved-print-readability-with-pprint/&quot; class=&quot;notion-text-href&quot;&gt;Improved print readability with pprint&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/document-intended-usage-through-tests-with-doctest/&quot; class=&quot;notion-text-href&quot;&gt;Document intended usage through tests with doctest&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/debug-with-pdb-and-breakpoint/&quot; class=&quot;notion-text-href&quot;&gt;Debug with pdb and breakpoint&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/rotating-turn-order-with-deque/&quot; class=&quot;notion-text-href&quot;&gt;Rotating turn order with deque&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/data-classes-in-python-with-dataclasses/&quot; class=&quot;notion-text-href&quot;&gt;Data classes in Python with dataclasses&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/combinatoric-iterators-from-itertools/&quot; class=&quot;notion-text-href&quot;&gt;Combinatoric iterators from itertools&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/write-more-pythonic-code-with-context-managers/&quot; class=&quot;notion-text-href&quot;&gt;Write more pythonic code with context managers&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/random-number-chosen-by-fair-dice-roll/&quot; class=&quot;notion-text-href&quot;&gt;Random number chosen by fair dice roll&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/combine-iterables-with-zip/&quot; class=&quot;notion-text-href&quot;&gt;Combine iterables with zip&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/map-and-filter-with-list-comprehensions/&quot; class=&quot;notion-text-href&quot;&gt;map and filter with list comprehensions&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/memoize-with-functools-cache/&quot; class=&quot;notion-text-href&quot;&gt;Memoize with functools.cache&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/keep-track-of-happenings-with-logging/&quot; class=&quot;notion-text-href&quot;&gt;Keep track of happenings with logging&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/bring-your-custom-toolkit-to-repl-sessions/&quot; class=&quot;notion-text-href&quot;&gt;Bring your custom toolkit to REPL sessions&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/serve-local-html-and-css-files-with-http-server/&quot; class=&quot;notion-text-href&quot;&gt;Serve local HTML and CSS files with http.server&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/branch-out-with-pattern-matching/&quot; class=&quot;notion-text-href&quot;&gt;Branch out with pattern matching&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/test-your-codebase-with-unittest/&quot; class=&quot;notion-text-href&quot;&gt;Test your codebase with unittest&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/pretty-print-and-validate-json-on-command-line-with-json-tool/&quot; class=&quot;notion-text-href&quot;&gt;Pretty print and validate JSON on command line with json.tool&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/parse-command-line-arguments-with-argparse/&quot; class=&quot;notion-text-href&quot;&gt;Parse command line arguments with argparse&lt;/a&gt;
  &lt;/li&gt;

  &lt;li class=&quot;notion-bulleted_list_item notion-color-default&quot;&gt;
    &lt;a href=&quot;https://hamatti.org/posts/quick-prototyping-with-sqlite3/&quot; class=&quot;notion-text-href&quot;&gt;Quick prototyping with sqlite3&lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  At
  &lt;a href=&quot;https://hamatti.org/posts/pycon-finland-2025-recap/&quot; class=&quot;notion-text-href&quot;&gt;PyCon Finland 2025&lt;/a&gt;, we got this cool LEGO Python logo that had batteries included:
&lt;/p&gt;
&lt;img src=&quot;https://hamatti.org/assets/img/posts/b-is-for-batteries-included-python-a-to-z/1.png.webp&quot; alt=&quot;A LEGO Python logo with two pieces removed to the side, exposing two 1-by-1 circle pieces with battery stickers inside the logo. &quot; /&gt;
&lt;p&gt;&lt;/p&gt;

    </content>
  </entry>
  
</feed>
