Luminescent Dreams

Programming a Neopixel Pride Flag

January 01, 0001

Some weeks ago, I had an instance in which I was planning to do in person volunteer recruitment for Freedom for All Massachusetts. Now, this is very different from canvassing, as I found out about when I was at Pride. There is no “hide your politics for a bit because we need accurate information”, this is “assume everyone you talk to is on our side because we’re trying to recruit them to help us”.

_DSC1459.jpg

So, what better way for me to show my pride, and be a techie, than to have a batch of LEDs rotating between the pride flags that I care most about?

Enter neopixels and an ATTiny86, in the form of an Adafruit Trinket. This is my first significant hardware project that involves both wiring and programming.

I started out with a neopixel RGBW jewel, but I was never able to get the signalling to work. Lacking any better ideas, I dropped back to a bunch of 8mm through-hole RGB neopixels and programmed for that. More on the problem later, but I think the programming library I was using does not actually signal RGBW neopixels correctly.

While it took a great many hours, mostly in setting up my environment and then getting the hang of very low level C programming, I did eventually succeed. But on a breadboard with long jumper wires. Still, proof of concept, and I’ll get a neopixel RGB jewel later to finish out the project.

All of the code for this project is in the pride-trinket github project.

AVR Builds

_DSC1460.jpg

First, I need a build environment. I am not using Adafruit’s development environment because I expect to pretty quickly go beyond the idea of a single tiny microcontroller, a microcontroller that Adafruit packaged and shipped. I’m already planning to go beyond Adafruit’s offerings, though their products are very nice.

For every AVR project, you need three things:

  • AVR binutils
  • Libc built for AVR
  • A GCC that targets AVR

I discovered bugs in the libc and GCC builds in my Nix repository, so I patched against those bugs and then repackaged all three packages for safety.

avrbinutils has warnings that cause the build to error, so I replaced the build line.

  src = fetchurl {
    url = "mirror://gnu/binutils/binutils-${version}.tar.bz2";
    sha256 = "028cklfqaab24glva1ks2aqa1zxa6w6xmc8q34zs1sb7h22dxspg";
  };
  configureFlags = "--target=avr --enable-languages=c,c++ --disable-werror";
  meta = with stdenv.lib; {

GCC needed a change to be able to find avrbinutils.

I glue it all together with a rather pedantic shell.nix.

    avrbinutils = import nixpkgs/avr/binutils {
        stdenv = pkgs.stdenv;
        fetchurl = pkgs.fetchurl;
    };
    avrgcc = import nixpkgs/avr/gcc {
        stdenv = pkgs.stdenv;
        fetchurl = pkgs.fetchurl;
        gmp = pkgs.gmp;
        mpfr = pkgs.mpfr;
        libmpc = pkgs.libmpc;
        zlib = pkgs.zlib;
        avrbinutils = avrbinutils;
        texinfo = pkgs.texinfo;
    };
    libc = import nixpkgs/avr/libc {
        stdenv = pkgs.stdenv;
        fetchurl = pkgs.fetchurl;
        avrgcc = avrgcc;
        avrbinutils = avrbinutils;
        automake = pkgs.automake;
        autoconf = pkgs.autoconf;
    };

    ...

    buildInputs = [
        avrbinutils
        avrgcc
        libc
        pkgs.avrdude
        ws2812.ws2812_avr
    ];

To get this working, I spent many hours trying to figure out why the libc libraries weren’t showing up in my path. I no longer remember searches that I used to find what I needed, but I eventually added a setup-hook.sh to my libc build. That script adds include paths and library paths to NIX_CFLAGS_COMPILE and NIX_LDFLAGS for each library directory under <libc>/avr/lib/.

I do not have a GCC wrapper working in my environment, so I had to modify my project Makefile to explicitely include these environment variables.

ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(NIX_CFLAGS_COMPILE) $(NIX_LDFLAGS)

I copied my base Makefile from the neopixel_i2c project, which I have used in previous experiments that involved an embedded Linux system.

One final element, the crtattiny85.o file, is missing. That file is found in in avrlibc hierarchy at <libc>/avr/lib/avr25/crtattiny85.o. I have no idea how the compiler knows that it wants that file in paricular, so I have not determined what environment variables I could use to tell the compiler where to find it. Instead, and somewhat out of laziness, I just copied the file into my working directory.

This gives me a working compiler!

If you want to use the environment, try pulling the shell.nix file and the avr directory. If you are not doing a neopixel project, you can leave out the ws2812 derivation, as that is a packaging of the library that handles neopixel signalling.

WS2812

_DSC1461.jpg

WS2812 refers to the onboard controller that neopixels use. Neopixels may look like LEDs, but they are actually clusters of LEDs with their own processor and power distribution. The control protocol works by sending precisely timed signals down the wire, one trio of signals per pixel, followed by a long time at logical 0. Each pixel captures the first three signals it receives and sends all remaining signals down the wire until the long logical 0 indicates that the entire message is done.

The neopixel ws2812 library includes clever assembly code that implements the timing based on an 800kHz clock.

I first worked with the library as I was learning to work with the neopixel_i2c library. However, I did not want to use git submodules. Instead, I decided to package this as a Nix derivation, including any patches that I wanted to include.

In this case, I had to build the derivation from scratch as I had no template to start with. Fortunately, I have the hang of building libraries, but I still provide a setup-hook file to set a convenient WS2812_PATH environment variable and a patch to the Makefile to depend on NIX_CFLAGS_COMPILE and NIX_LDFLAGS.

My derivation file provides both light-ws2812-source and the ws2812_avr library as separate derivations. The library actually provides code for multiple environments, but ws2812_avr is the only one I currently build.

While this is a “library”, it is unusual in the same way as libc in that my only target is a .o file, not a .so or .a. Like with the crtattiny85.o file, I manually copy the light_ws2812.o file into my project.

Some Hardware

_DSC1455.jpg

This is where the fun happens. For this project, I have:

I have standardized my wire colors on this project as RED for 5V power, BLUE for ground, and YELLOW for data. Since Neopixels are 5V driven, I made sure to use a 5V Trinket. Watch out for that, as I don’t know what would happen if you tried to use a 3.3V trinket with the pixels.

The Trinket accepts main power on the USB wire. The underside of the Trinket has pads to which one could attach incoming power, also. Importantly, the 5V pin and the GND pin on he sides of Trinket are for the Trinket to provide power to any attached hardware, not for the Trinket to receive power. Fortunately, I did not destroy anything before I understood this.

_DSC1458.jpg

I use Pin #3 on the Trinket as the data line. This pin number is hard-coded in the ws2812 library. You can change it by patching ws2812_pin in ws2812_config.h. If you wanted to do this, I recommend a patch that you provide in the patches phase of your derivation.

On this project I have only the single yellow wire because I carefully ensured that the data pins chain to one another. These particular pixels made that convenient by putting the data in and data out pins on the edge of the pixels with the power pins in the center.

The wiring is important. When you power the Trinket on for the first time, keep a finger near it and pull power as fast as you can if you feel heat. I made a mistake in my wire colors at first and accidentally shorted the Trinket’s power to ground. Fortunately, I noticed and pulled power before anything exploded.

When you apply power, all of the pixels will go to a random state while the Trinket boots up. While I don’t know the details of how to fix that, I suspect a pull-down resistor on the data line would help.

After a few seconds, though, the Trinket will start running the program. In my case, the program just runs forever, transitioning from one pride flag to the next in a loop. I get visible flicker in the pixels, which I suspect I could alleviate with capacitors on the inputs. Some folks have recommended 1uF capacitors, but said that 10uF, while overkill, would do the job.

Programming

Brush up your C, as this is low-level programming. Nevertheless, it is an odd combination of fun and frustrating. Remember, you have no debugging tools on the Trinket.

ws2812 provides a cRGB convenience data structure and a function, ws2812_setleds function which accepts a struct cRGB *ledarray. I had not done any C programming in quite a long time, so I completely missed that struct cRGB *ledarray is a standard C way of describing struct cRGB ledarray[x], where x is unknown. This means a single contiguous block of data, one cRGB after another.

Until I figured that out, I wrote all kinds of wrong code that mostly amounted to creating arrays of pointers to cRGB structures. Now, I create my pride flags as normal cRGB arrays, and happily pass them around as struct cRGB *.

const struct cRGB genderqueer_flag[LED_COUNT * 3] = {
    { .r = 128, .g = 0,     .b = 128 },
    { .r = 128, .g = 0,     .b = 128 },
    { .r = 128, .g = 128,   .b = 128 },
    { .r = 0,   .g = 128,   .b = 0 },
    { .r = 0,   .g = 128,   .b = 0 }
};

I wrote so many bugs that trial and error was insufficient, so I wrote in some conditional compilation logic that lets me run my application directly on my host machine. For the neopixel project, I needed in particular to provide my own _delay_ms function, declarations for uint8_t and uint16_t, and wrapper for ws2812_setleds. At the top of my program I have a conditional compilation block for initial declarations:

#if __HOST__
/* headers for the host machine */

typedef unsigned char uint8_t;
struct cRGB { uint8_t g; uint8_t r; uint8_t b; };

void _delay_ms(double ms) {
    int ms_int = round(ms);
    struct timeval delay = { .tv_sec = ms_int / 1000, .tv_usec = (ms_int % 1000) * 1000000 };
    select(0, NULL, NULL, NULL, &delay);
}

#define uint8_t unsigned char
#define uint16_t unsigned short
#else
/* headers for the AVR */
#endif

In the body of my code, I provide write_leds as a wrapper around ws2812_setleds, though I realize now that I could have simply redeclared ws2812_setleds in the __HOST__ block at the start of the application.

void write_leds(const struct cRGB *leds, uint16_t count) {
#if __HOST__
    for (int i = 0; i < count; i++) {
        printf("\t{ %d, %d, %d }\n", leds[i].r, leds[i].g, leds[i].b);
    }
#else
    ws2812_setleds((struct cRGB *)leds, count);
#endif
}

The main body of my code is a simple state machine that I modeled as a machine that is either RESTING on a particular pattern or TRANSITIONING to another pattern. When transitioning I do a simple interpolation between my starting pattern and my ending pattern, so I am able to base the “current” pattern solely on the start, end, current frame number, and total number of frames.

struct state_s {
    uint8_t current;
    uint8_t frame_count;

    const struct cRGB *start;
    const struct cRGB *end;
};
uint8_t interpolate_color (uint8_t start, uint8_t end, uint8_t frame_count) {
    uint8_t res = start + ((end - start) / TRANSITION_FRAMES * (frame_count + 1));
    return res;
}

Finally, my state machine block is rather straightforward:

        switch (state.current) {
            case RESTING:
                _delay_ms(REST_TIME_MS);
                state.current = TRANSITIONING;
                break;
            case TRANSITIONING:
                if (state.frame_count < TRANSITION_FRAMES) {
                    /* code to calculate write the current pattern */
                    state.frame_count = state.frame_count + 1;
                    _delay_ms(TRANSITION_TIME_MS / TRANSITION_FRAMES);
                } else {
                    state.current = RESTING;
                }
                break;
        }

Glue it all together and I get a program that displays a pattern for five seconds, then fades to the next one over the course of five seconds.

Program Loading

The final step to actually try out all of the work is to flash the program onto the chip. My Makefile provides a main.hex file, which is the AVR image to be sent to the controller. I use avrdude to do the programming. I include in my repository a Trinket-friendly, macOS version of avrdude.conf.

Every time I try programming, I have to do it twice in a row. The first time always fails with a checksum error, and the second time succeeds. I have no idea why this happens, but I made a script that does it for me:

avrdude -c usbtiny -p attiny85 -U flash:w:main.hex
sleep 5
avrdude -c usbtiny -p attiny85 -D -U flash:w:main.hex

Note that the two avrdude commands differ. Just issuing the command with -D gives me one checksum error. Issuing the command with the -D gives a different checksum error. Issuing the two commands in this order… just works? Let me know if you know why and especially if you can tell me the correct way of running the command.

Making sure to use a data cable, plug the Trinket into a USB port. The Trinket seems to automatically go into programming mode if it detects a data cable, so just run the script above. When the programming is done, the Trinket should immediately start running the application!

More things!

I still have more that I want to do. I want to get an RGB pixel jewel and wrap it into a wearable form. I want to add a bisexual pride flag. And I want to show it off!

And then I want to start on my Halloween costume. I have a Lapis Lazuli costume that I have wanted to make for years, and now I can add lights into the design. When I do this costume, instead of using a Trinket I want to drop down to a bare AtTiny85 so I can make the electronics as compact as possible.

But more than this, I want to do more work based on a Beagle Bone or other embedded Linux platform so I can use the i2c bus to link together sensors and lights with some logic. I have already done experiments with this and I am looking forward to figuring out how to mount sensors and lights on a piece of clothing.

More references

Progress on Illuminance

January 01, 0001

I started the Illuminance project back in February of this year. Immediately after my last contract ended, I started working on the code in order to make this application real.

Three months in, I both exault in everything I have learned, and despair at how much remains to be done. This looked like such a simple problem, yet in its solution I have pursued so much new information that I constantly swim underneath a bare understanding of all that I have learned.

Where am I now?

User Interface

I have a very basic user interface that allows me to load images, see their previews, see error messages that occur during load, and run a “naive” HDR calculation. Error messages get displayed in the location where an image preview would normally be. While this is not necessarily elegant, it allow me to display an error that is specific to one image, since a lot of things happen in parallel and in other threads.

Generally, no expensive operation occurs in the main gui thread, so the gui does not ever actually hang. I have not been able to test this exhaustively and I am sure that I will find places where I am wrong, but this does certainly seem to be the case.

Images load in millisecond time, even very large images. They also load with reasonable space usage. I had some great success with creating my own image codec library and representation, but then I discovered that a combination of JuicyPixels and Repa solve the problem better by virtue of being more complete.

On the HDR Calculation

I have spent a vast amount of time learning math in the hopes that I could figure out how to set up the HDR calculation. Ultimately, I read some Matlab code that somebody else wrote to figure out how to set up all of the terms for the problem.

I put all of that together, set up the matrices to solve, called into the LaPack sgelsd function to actually run the calculation… and got the wrong answer. I am stumped, however there is a lot that I can play with still. I have before solved problems where the wrong answer cannot be reverse engineered and where fixing the problem can only be done by carefully examining the inputs. I imagine that is what will be required here.

I do have what I call a naive calculation in the application. This calculation scales every pixel by the associated exposure value, and then averages the result. The end result is weird in the visual artifacts and doesn’t actually give me a high dynamic range anything. While I could improve this, all I wanted to do was to get something on the screen. I have since focused all of my efforts on getting the real calculation to work.

What needs to be done

Oh so much.

  • Get the HDR calculation actually working
  • Be able to save and load HDR files
  • Tonemapping to LDR
  • Making a nice GUI

sigh. In other words, what is done so far only barely scratches the surface of a complete application.

Diversion

In any event, I am diverting to a different task. Problems on Linux have left all of the current photo managers unusable, and have left me unable to even submit a bug report on the one that I’ve been using for several years, so I am scrambling to hack one out on my own. I have no idea whether this is going to work, because while I can handle most everything, there are GUI elements that I don’t know how to handle yet. We shall see. This does, however, put Illuminance on hold, but all of my photography is really on hold due to these bugs.

  • Shotwell – Ug. Doesn’t handle XMP files. Won’t update tags on a RAW file of any kind. Instead, squirrels tags away in some custom database in a hidden folder so that no other program can see them. This problem burned me very badly once, so I refuse to use the program again unles full XMP support gets added.
  • Digikam – An 8 million pound gorilla (requiring a full installation of KDE) that I’ve been using with good success for a couple of years. Writes all tags for RAW files into an XMP sidecar, so I haven’t lost a tag in years. Unfortunately, it is also crashing randomly with either the Abort signal (and no reportable stack trace) or a Segmentation Fault (which might be reportable as soon as I get symbols installed). I think the problem lies somewhere in the KDE libs, not in Digikam itself. Either way, the application is unusable and I have no way forward to fixing it.

So, maybe I can produce something a little lightweight. I’d been thinking of trying, anyway, but I had not intended to do so until Illuminance was done. Hopefully I will get results faster since this is just a GUI and data management problem.

Python Type Annotations

January 01, 0001

I have to admit, 40% of the reason I use Haskell lies in static type checking. Another 40% lies in strong, ironclad immutability. Only about 20% of my love of Haskell has anything to do with language expressiveness. Make no mistake, the language has expressiveness out the wazoo, but I truly love static typing and immutability.

One day recently turned into a disaster. One problem lead to another in a task that should have been trivial and instead involved four hours of beating my head against the desk. Part of the problem was that my system under test had only acceptance tests, executed only with all of standard out and standard error shunted away to /dev/null. Either way, after I got my changes made, I decided to step out of the office to think.

Python has expressiveness. It has neither static type checking or immutable values, and the language developers get really holy about this. I have no interest in arguing with them, as I believe they have decided to abandon all safety and flood the world with code that might blow up in production because maybe in some corner case that a STATIC TYPE CHECKER could have detected at compile time, they inadverdently passed an Integer to a function that expected a String. So, I will not change Python the language, but I do want to make things nicer. Even though my solution will not get checked at compile time, it can certainly make debugging easier when a function crashes immediately due to a blown data type assertion, rather than propogating that incorrect data type potentially quite some distance.

I have put the code in a Repository. You can get this particular file at type_checking.py

hg clone https://gitlab.com/savannidgerinel/python-tools

The gruesome way

You could do it like this. I have done this.

def authenticate (username, password):
    assert isinstance(username, types.StringType)
    assert isinstance(password, types.StringType)

    ... do a bunch of authentication stuff and talking to databases and many things that belong in the IO monad ...

    assert isinstance(ret, UserClass)
    return ret

Ew. It will work in a pinch… but ew. This gets especially bad if I have several places from which I can return from the function. Yes, it improves the readability of the input parameters, but it does little for the return parameter beyond making postconditions.

Slightly better

Assertions are things that “should never happen in code”. So, technically, an AssertionError is actually not a good thing to throw in the case of a type error. Python actually provides TypeError to indicate that a data type error has occurred. That is convenient. So, instead of calling assert, let’s create a function that will do the job and raise a better exception. And, let’s build in making the parameter optional.

def typecheck (param, type_, optional=False):
    if not optional and param is None:
        raise TypeError('None is not permitted for this value')
    if param is not None and not isinstance(param, type_):
        raise TypeError('Expected type %r, got %r' % (type_, type(param)))

With this, your above code would look like this:

def authenticate (username, password):
    typecheck(username, types.StringType)
    typecheck(password, types.StringType)

    ... do a bunch of authentication stuff and talking to databases and many things that belong in the IO monad ...

    typecheck(ret, UserClass)
    return ret

This doesn’t improve the code much, but it does make for more descriptive error messages. I’m rather liking this improvement. But I can do better.

Decorative rescue

I once read this round of pejoratives about static type users, and I wondered for a while what that meant. I looked things up, found a few references to using decorators to “decorate” type checks on functions, but I did not like the solutions. Maybe they were good solutions, but I wanted to solve it myself. Also, the typecheck module for Python appears to be almost seven years dead.

So I introduce a some code that I wrote in an hour yesterday.

First, I played a bit with the syntax, and then I put the syntax into a unit test. You do test your code, don’t you?

class TestTypeChecker(unittest.TestCase):
    @unittest.skip('disabled')
    def testNoParamsReturnString(self):
        @accepts()
        @returns(types.StringType)
        def f():
            return 'abcd'

        f()
        self.assertRaises(AssertionError, lambda: f('a'))

    @unittest.skip('disabled')
    def testParams(self):
        @accepts(types.StringType, types.IntType)
        @returns(types.NoneType)
        def f(var1, var2):
            return None

        f('abcd', 15)
        self.assertRaises(AssertionError, lambda: f('abcd', 'efgh'))
        self.assertRaises(AssertionError, lambda: f(15, 'efgh'))
        self.assertRaises(AssertionError, lambda: f())

In here you can see the syntax. Before each declaration of f(), I put an @accepts block and a returns block. The desired data types get passed into @accepts and @returns as though these two calls are function calls. As it happens, they are.

Additionally, I wanted to flag a parameter as optional. Not optional in that it can be omitted, but optional in that I could pass None instead of the declared type.

    def testMaybeParams(self):
        @accepts(types.StringType, Maybe(types.IntType))
        @returns(types.NoneType)
        def f(var1, var2):
            return None

        self.assertRaises(AssertionError, lambda: f('abcd', 'efgh'))
        f('abcd', None)
        f('abcd', 15)

def testOptions(self):
    @accepts(types.StringType, Options(types.NoneType, types.StringType, types.IntType))
    @returns(Options(types.NoneType, types.IntType))
    def f(var1, var2):
        if var1 == 'None':
            return None
        else:
            return 15

    f('abcd', 15)
    f('abcd', '15')
    f('abcd', None)
    self.assertRaises(TypeError, lambda: f('abcd', 5.5))
    self.assertRaises(TypeError, lambda: f(None, 'abcd'))

Note Maybe. Declarations will come soon, but I created Maybe as a class that accepts a data type as a single parameter. If either of the decorators see that the parameter type is Maybe, then it will allow None or the type passed in to Maybe in the corresponding parameter. And then, some time later, I created Options as a way to specify that a parameter can be any number of data types, including None.

So, finally, it is time to present the code itself. First, my two support classes. They are delightfully short.

class Maybe(object):
    def __init__(self, var_type):
        self.var_type = var_type

    def __repr__(self):
        return 'Maybe(%s)' % self.var_type

    def check(self, param):
        if param is None:
            return True
        if isinstance(param, self.var_type):
            return True
        return False

class Options(object):
    def __init__(self, *args):
        self.var_options = args

    def __repr__(self):
        return 'Options(%s)' % ','.join(map(repr, self.var_options))

    def check(self, param):
        for type_ in self.var_options:
            if isinstance(param, type_):
                return True
        return False

Both of these exist to give expressiveness to the type system, as above. In both cases, it became simplest to create a check operation that would actually run the check against a parameter and return whether the parameter passes.

The actual guts of the type checking happens in a series of standalone functions.

def format_param_mismatch(idx, arg_type, expected_type):
    return 'Incorrect type in parameter %d: got %s, expected %s' % (idx, arg_type, expected_type)

First, I have a function, format_param_mismatch to provide a good error message in the case of a parameter type mismatch. Note that the requirements are the index of the parameter, the argument type, and the expected argument type. I included the index because I found it necessary to say “Hey, a parameter doesn’t match and it is this parameter!”

def check_param(param, expected):
    if getattr(expected, 'check', None):
        return expected.check(param)
    return isinstance(param, expected)

This function is pretty simple. It only returns True or False. If the “expected” type has a check method, i.e., it is Maybe, Options, or some other supporting class that I have not created yet, get the result by calling the check method. Otherwise, just run isinstance.

def accepts(*var_types):
    def checked_function(f):
        def checker(*args, **kwargs):
            # if len(var_types) != len(args):
            mismatches = [
                (idx, type(arg), var_type)
                for (idx, var_type, arg) in zip(itertools.count(), var_types, args)
                if not check_param(arg, var_type)]
            if len(mismatches) != 0:
                raise TypeError('\n'.join(map(lambda x: format_param_mismatch(*x), mismatches)))
            return f(*args, **kwargs)
        return checker
    return checked_function

Decorators are complicated to code.

First, the decorator itself takes parameters. That is *var_types, and that is what allows the syntax above. That returns checked_function as a parameter.

Second, checked_function then gets applied to your original function, and the magic plumbing of the decorator replaces your binding with this new function that wraps your original function.

Third, the decorator needs to return a function, and that function will take your original function as a parameter. So, at compile time the original function and the types will all get linked together and your function binding will be replaced with the function that runs this check.

def returns(return_type):
    def checked_function(f):
        def checker(*args, **kwargs):
            val = f(*args, **kwargs)
            # assert isinstance(val, return_type), 'Incorrect return type: returned %s, expected %s' % (type(val), return_type)
            if not check_param(val, return_type):
                raise TypeError('Incorrect return type: return %s, expected %r' % (type(val), return_type))
            return val
        return checker
    return checked_function

returns works in exactly the same way as accepts, but applies the data type to the return value. With this, no matter how many return statements you have in your code, the actual returned value gets checked. Admittedly, it is getting checked after whatever side effects your function had, so if you return invalid data from your database update, your database has already been updated and potentially corrupted.

Limits

You have some limits still.

First, a part that I think is critical, is that you still will not know that you have a data type error until you actually exercise a code path that exhibits the data type error. On the other hand, at least you find out very quickly when you do so that your error is not a different kind of logic error.

Also, not quite obviously, I do not have a way for you to check arbitrary argument lists. Any part of *args that does not get captured by a named parameter will not be checked, and none of **kwargs will be checked. The decorator syntax is simply too limited to be able to describe such a check without the entire declaration becoming very cumbersome.

Generally, I would suggest avoiding arbitrary keyword arguments. It is not always a problem, but it does tend to lead to necessary but undocumented parameters. If you must use them, use them for cases where the arbitrary keyword arguments are just used to name optional arbitrary data fields, but that all actually necessary parameters are given an explicit name in your function declaration.

Overall, however, using these decorators liberally will help significantly with the task of tracking down problems that are ultimately data type errors. Additionally, the presence of the decorator helps document the API for the next person to come along, making explicit things that otherwise a programmer would have to dig into the code to find out.

If you are like me, then data type errors are the most common error you make, these decorators are going to be a big help.

http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png

Python Type Annotations by Savanni D'Gerinel is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. You can link to it, copy it, redistribute it, and modify it, but don't sell it or the modifications and don't take my name from it.

Remembering Amelia

January 01, 0001
IMG_1567.jpg

Tonight, I want to tell you about a dear friend. Someone very important to myself and to my partner, Daria.

Hir name was Amelia. Ze took hir own life late in the night last Tuesday. I found out as soon as I woke up Wednesday morning. A week ago today.

Amelia was gorgeous. Ze was brilliant. Ze sparkled, flirted, brightened spaces, tied innuendo into unexpected sentences, and could talk about advanced mathematics that I cannot begin to understand.

But, honestly, we never talked about advanced math or technology. Amelia was passionate about social justice. Ze came into our community during a chance run-in at a march in solidarity for immigrant’s rights. Late in hir life, ze started campaigning for patient’s rights in mental health. This hyper focus came about from continuous failures in her stays in various psych wards here in Boston. Some of these failures caused significant harm.

Make no mistake. While Amelia may have died at hir own hand, ze was driven to it. Multiple assaults from random men on the street who continue to feel liberty in how they treat women, followed by hospitalizations that dehumanized hir, ignored hir, and in some cases, did significantly more harm.

I miss my friend.

I miss a friendship lost.

I regret the tensions between us, the things we did not have time to heal.


IMG_2089.jpg

The day we all found out, the morning after Amelia died, seventeen people gathered for an impromptu wake.

Let that sink in. Seventeen people dropped everything to gather, weep, and reminisce, with no advance notice. Queer community is not large, but it is tightly connected and our members are loved. To quote my friend Aria:

Queer culture is grieving as a family no outsider would recognize, a haphazard landslide of lovers & friends. The ones who bring the tequila & gin, the ones who bring the casseroles, the ones who bring the cookies and apologize about the butter, the ones who make a big thai curry

While we can never change what outsiders would see, I want very much to change the definition of “outsider”. While I always live visibly, I have not in the past put much effort into broadcasting that image, and it thus became possible for people ostensibly in my life to completely ignore that which they did not see. But that means that, should I die, there are many people who would have to learn about the full me only through pictures and stories. If they learn at all. Perhaps they would actively ignore.

For now, however, I want to actively try to show what a happy, healthy life outside of the norm of cisgender, heterosexual, monogamy looks like.

Resistance

January 01, 0001

For the next ten days, my resistance against fascism is to sit with an enby who has just undergone surgery. The kind of person and surgery that self-proclaimed “decent folk” believe is an abomination worthy of death.

If you are one of them, fuck off.

A Quiet Walk, Interrupted

Ice Flow, 2017-01.web

Yesterday, my friend and I were out walking along the Delaware Canal Towpath in Morissville, Pennsylvania, having a lovely day. Part of the canal is still frozen, and we made a hobby of seeing if we could break the ice using whatever rocks we had to hand. The sound of an ice sheet fracturing is really unique. Not precisely like glass shattering, because the fracture and stresses race down the ice sheet, even if the break only happens in a small area. I lost count of the number of significant rocks that I hurled into the ice that simply got embedded.

We are pretty much minding our own business when an old man walking the other direction starts demanding to know where we’re going “dressed like that” and telling us “you’re not real girls”. He proceeded to hurl invectives after us for long after we could hear him. He even proclaimed just how great it must be to have wealthy parents who will support my “lifestyle”.

When I was twenty, I clawed my way nearly to financial independence to be free of my parents rules. I’ve been on my own ever since. I wanted so much to walk up and tell him precisely how much I get paid for my skills at a job that keeps me in air conditioning. The idea of shaming him into silence was almost overpowering. I am a professional in my field, not even 40, nearly at the top, and I make more money than he likely ever has.

How pathetic. Long after we could no longer hear him, we could see him still yelling at us. He had nothing better to do with his sorry excuse for a life.

It is very difficult to walk away silently. I cannot help but feel that I accomplished nothing. That there is no victory to the high road. But that perhaps there is nothing to be accomplished in my reply.

I so rarely face actual transphobia on the streets. So rarely that I vacillate between being shocked when it happens, and shocked that nobody even looks sidelong at me, even when I’m in a small town. For the rest of our walk, we kept our eyes out. We have no idea what the old man may have done. Perhaps he called the cops on us, as has happened so often to people like us. If he did, they ignored him and we continued the rest of our day unmolested.

Decent Folk

So often, the narrative is that we are a threat to “Decent Folk”. Somehow, trans queer reality is so powerful that all wholesome goodness breaks down around us.

Decent Folk assault people on the street.

Decent Folk poke their noses into other people’s private lives.

Decent Folk remain silent when death camps rise.

Decent Folk vote for a fascist, rapist, traitor because he pedals lies of prosperity.

Decent Folk vote for one who promises more power to those with power.

Decent Folk are so easily duped with fascist lies.

Defiance

What is the queer agenda? STOP HURTING OUR FRIENDS! STOP HURTING OUR CHILDREN! LET US LIVE HEALTHY PRODUCTIVE LIVES IN PEACE!

We are the ones who take in those not our kin. Teenagers thrown out of their homes for being gay, lesbian, transgender, bisexual, asexual, intersex, polyamorous. Strangers moving into a city where they can be safer.

If we could, we would walk away from your fucking “decent” culture. We would separate ourselves and build a civilization of our own. We would interact with you only to rescue the queers who emerge amongst you.

But you won’t permit that. You “Decent Folk” have all the power. So fuck you.