Blogging with Jekyll

I am making a number of changes to my site, including changing my software, changing my hosting, and doing a bit of clean-slate work. I have always enjoyed running useless sites as a side hobby, but I hope that this invokes some next-level shit.

I've been a fan of static site generation for personal blogs for a while now, and even more a fan of using an automatic build process where I just edit some files in a git repo and they show up on the internet in nicely formatted fashion on a simple git push.

Jekyll

I'm now using Jekyll to generate the site, and I'm pushing it to a Github Pages repo to publish it in an internet accessible place. This feels like just about the ideal in a long evolution of changing how I manage a blog:

  • I dictate ownership of the content (it's not facebook.)
  • I have a really easy local draft copy of the site
  • The dependency on github will be easy to remove should I choose to migrate away from them - It's really just running the standard build script, and I chould easily run that to publish on my own server.
  • Jekyll's front-matter and site data variables is just really slick and obvious
  • Github Pages is fast enough and free an reliable

Duplicate Keywords When Using Keyword Argument Unpacking in Python

Python has a feature often called "Argument List Unpacking," which can be found in several other popular languages under the names "splat" (ruby), "Apply" (LISP, Scheme, general use):

>>> def all_my_args(arg1, *args):
...     print arg1  # first arg
...     print args  # list containing the rest

>>> all_my_args("first", "second", "third")
first
('second', 'third')

And this same method can be applied to dictionaries by using the ** operator:

>>> def all_my_args(arg1, **keyword_args):
...     print arg1  # first arg
...     print keyword_args  # dict containing the rest
...
>>> all_my_args("first", second=2, third=3)
first
{'second': 2, 'third': 3}

You can then "pack" dictionaries and handle them to functions as arguments:

>>> my_args = {"second": 2, "third": 3}
>>> all_my_args("first", **my_args)
first
{'second': 2, 'third': 3}

Now, remember that all arguments can be keyword arguments, as the order of argument parsing matters: regular arguments are first, then variable arguments (*args), then any keyword arguments (whether named explicitly via keywordname= or implied as a packed dictionary with **keywordargs). Let's expand the above function to emphasize the concern:

>>> def describe_blob(name, shape, **attributes):
...   print "Name:", name
...   print "Shape:", shape
...   for k,v in attributes.items():
...     print "{}: {}".format(k, v)
...
>>> describe_blob("Jack", "round", size="huge", weight="ugly")
Name: Jack
Shape: round
weight: ugly
size: huge

This seems really cool at first, because it lets the user of the function provide any sort of keyword they want to use to describe the blob. Unfortunately, it can cause some problems:

>>> describe_blob("Jack", "round", shape="lumpy", size="huge", weight="ugly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: describe_blob() got multiple values for keyword argument 'shape'

Multiple values were found for 'shape', but I only specified one: "lumpy". Well, it turns out that the second position argument was internally called "shape" as well, which broke the function call. You can learn this through introspection, but that's not something one wants to do before every function is called:

>>> import inspect
>>> inspect.getargspec(describe_blob)
ArgSpec(args=['name', 'shape'], varargs=None, keywords='attributes', defaults=None)

Even if the argument list that is acceptable is described in the function's docstring, the arguments are often named in a way that's descriptively useful to developers of the function, not consumers of the function.

Ultimately, it's safer to just pass this as a dictionary, even though it seems cool to use the unpacking:

>>> def describe_blob(name, shape, attributes):
...   print "Name:", name
...   print "Shape:", shape
...   for k,v in attributes.items():
...     print "{}: {}".format(k, v)
...
>>> my_blob_attributes = { "brain": "mushy", "height": 3, "shape": "duplicative" }
>>> describe_blob("Jack", "round", my_blob_attributes)
Name: Jack
Shape: round
brain: mushy
shape: duplicative
height: 3