What have I been up to?
Late last year, I launched
a Patreon. Although not quite a “soft” launch — I did
toot about it, after all — I didn’t promote it very much.
I started this way because I realized that if I didn’t just put something up
I’d be dithering forever. I’d previously been writing a sprawling monster of
an announcement post that went into way too much detail, and kept expanding
to encompass more and more ideas until I came to understand that salvaging it
was going to be an editing process just as brutal and interminable as the
writing itself.
However, that post also included a section where I just wrote about what I
was actually doing.
So, for lots of reasons, there are a diverse array of loosely related (or
unrelated) projects below which may not get finished any time soon. Or,
indeed, may go unfinished entirely. Some are “done enough” now, and just won’t
receive much in the way of future polish.
That is an intentional choice.
The rationale, as briefly as I can manage, is: I want to lean into the my
strength of creative, divergent thinking, and see how these ideas pan out
without committing to them particularly intensely. My habitual impulse, for
many years, has been to lean extremely hard on strategies that compensate for
my weaknesses in organization, planning, and continued focus, and attempt to
commit to finishing every project to prove that I’ll never flake on anything.
While the reward tiers for the Patreon remain deliberately ambiguous, I
think it would be fair to say that patrons will have some level of influence
in directing my focus by providing feedback on these projects, and requesting
that I work more on some and less on others.
So, with no further ado: what have I been working on, and what work would you
be supporting if you signed up? For each project, I’ll be answering 3
questions:
- What is it?
- What have I been doing with it recently?
- What are my plans for it?
What is it?
For starters, I write stuff here. I guess you’re reading this post for some
reason, so you might like the stuff I write? I feel like this doesn’t require
much explanation.
What have I done with it recently?
You might appreciate the explicitly patron-requested Potato
Programming post, a screed about
dataclass
, or a deep dive on the
difficulties of codesigning and notarization on
macOS along with an announcement of a tool to
remediate them.
What are my plans for it?
You can probably expect more of the same; just all the latest thoughts &
ideas from Glyph.
What is it?
If you know of me you probably know of me as “the Twisted guy” and yeah, I am
still that. If, somehow, you’ve ended up here and you don’t know what it is,
wow, that’s cool, thanks for coming, super interested to know what you do
know me for.
Twisted is an event-driven networking engine written in Python, the precursor
and inspiration for the asyncio
module, and a suite of event-driven
programming abstractions, network protocol implementations, and general utility
code.
What have I done with it recently?
I’ve gotten a few things merged, including type annotations for
getPrimes
and making the
bundled CLI OpenSSH server replacement work at all with public key
authentication again, as well
as some test cleanups that
reduce the overall surface area of old-style Deferred-returning tests that can
be flaky and slow.
I’ve also landed a posix_spawnp
-based spawnProcess
implementation which speed up
process spawning significantly; this can be as much as 3x faster if you do a
lot of spawning of short-running processes.
I have a bunch of PRs in flight, too, including better annotations for
FilePath
Deferred, and
IReactorProcess, as well as a
fix for the aforementioned
posix_spawnp
implementation.
What are my plans for it?
A lot of the projects below use Twisted in some way, and I continue to maintain
it for my own uses. My particular focus is in quality-of-life improvements;
issues that someone starting out with a Twisted project will bump into and find
confusing or difficult. I want it to be really easy to write applications
with Twisted and I want to use my own experiences with it.
I also do code reviews of other folks’ contributions; we do still have over 100
open PRs right now.
What is it?
DateType is a workaround for a very specific bug in the way that the datetime
standard library module deals with type composition: to wit, that datetime
is
a subclass of date
but is not
Liskov-substitutable
for it. There are even #type:ignore
comments in the standard library type
stubs
to work around this problem, because if you did this in your own code, it
simply wouldn’t type-check.
What have I done with it recently?
I updated it a few months ago to expose DateTime
and Time
directly (as
opposed to AwareDateTime
and NaiveDateTime
), so that users could specialize
their own functions that took either naive or aware times without ugly and
slightly-incorrect unions.
What are my plans for it?
This library is mostly done for the time being, but if I had to polish it a bit
I’d probably do two things:
- a readthedocs page for nice documentation
- write a PEP to get this integrated into the standard library
Although the compatibility problems are obviously very tricky and a PEP would
probably be controversial, this is ultimately a bug in the stdlib, and should
be fixed upstream there.
What is it?
It’s a library to make deterministic finite-state automata easier to create and
work with.
What have I done with it recently?
Back in the middle of last year, I opened a PR to create a new, completely
different front-end API for state machine definition. Instead of something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class MachineExample:
machine = MethodicalMachine()
@machine.state()
def a_state(self): ...
@machine.state()
def other_state(self): ...
@machine.input()
def flip(self): ...
@machine.output()
def _do_flip(self): return ...
on.upon(flip, enter=off, outputs=[_do_flip], collector=list)
off.upon(flip, enter=on, outputs=[_do_flip], collector=list)
|
this branch lets you instead do something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | class MachineProtocol(Protocol):
def flip(self) -> None: ...
class MachineCore: ...
def buildCore() -> MachineCore: ...
machine = TypicalBuilder(MachineProtocol, buildCore)
@machine.state()
class _OffState:
@machine.handle(MachineProtocol.flip, enter=lambda: _OnState)
def flip(self) -> None: ...
@machine.state()
class _OnState:
@machine.handle(MachineProtocol.flip, enter=lambda: _OffState)
def flip(self) -> None: ...
MachineImplementation = machine.buildClass()
|
In other words, it creates a state for every type, and type safety that much
more cleanly expresses what methods can be called and by whom; no need to make
everything private with tons of underscore-prefixed methods and attributes,
since all the caller can see is “an implementation of MachineProtocol
”; your
state classes can otherwise just be normal classes, which do not require
special logic to be instantiated if you want to use them directly.
Also, by making a state for every type, it’s a lot cleaner to express that
certain methods require certain attributes, by simply making them available as
attributes on that state and then requiring an argument of that state type; you
don’t need to plot your way through the outputs generated in your state graph.
What are my plans for it?
I want to finish up dealing with some issues with that branch - particularly
the ugly patterns for communicating portions of the state core to the
caller
and also the documentation; there are a lot of magic signatures which make
sense in heavy usage but are a bit mysterious to understand while you’re
getting started.
I’d also like the visualizer to work on it, which it doesn’t yet, because the
visualizer cribs a bunch of state from MethodicalMachine when it should be
working purely on core objects.
What is it?
This is an attempt at a holistic, end-to-end secret management wrapper around
Keyring. Whereas Keyring handles
password storage, this handles the whole lifecycle of looking up the secret to
see if it’s there, displaying UI to prompt the user (leveraging a pinentry
program from GPG if available)
What have I done with it recently?
It’s been a long time since I touched it.
What are my plans for it?
- Documentation. It’s totally undocumented.
- It could be written to be a bit more abstract. It dates from a time before
asyncio, so its current Twisted requirement for
Deferred
could be made into
a generic Awaitable
one.
- Better platform support for Linux & Windows when GPG’s
pinentry
is not
available.
- Support for multiple accounts so that when the user is prompted for the
relevant credential, they can store it.
- Integration with 1Password via some of their many potentially relevant
APIs.
What is it?
Fritter is a frame-rate independent timer tree.
In the course of developing Twisted, I learned a lot about time and timers.
LoopingCall
encodes some of this knowledge, but it’s very tightly coupled to the somewhat
limited IReactorTime API.
Also, LoopingCall
was originally designed with the needs of media playback
(particularly network streaming audio playback) in mind, but I have used it
more for background maintenance tasks and for animations. Both of these things
have requirements that LoopingCall
makes awkward but FRITTer is designed to
meet:
-
At higher loads, surprising interactions can occur with the underlying
priority queue implementation, and different algorithms may make a
significant difference to performance. Fritter has a pluggable
implementation of a priority queue and is carefully minimally coupled to it.
-
Driver selection is a first-class part of the API, with an included, public
“Memory” driver for testing, rather than LoopingCall
’s “testing is at
least possible” .reactor
attribute. This means that out of the box it
supports both Twisted and asyncio, and can easily have other things added.
-
The API is actually generic on what constitutes time itself, which means
that you can use it for both short-term (i.e.: monotonic clock values as
float-seconds) and long-term (civil times as timezone-aware datetime
objects) recurring tasks. Recurrence rules can also be arbitrary functions.
-
There is a recursive driver (this is the “tree” part) which both allows
for:
a. groups of timers which can be suspended and resumed together, and
b. scaling of time, so that you can e.g. speed up or slow down the ticks
for AIs, groups of animations, and so on, also in groups.
-
The API is also generic on what constitutes work. This means that, for
example, in a certain timer you can say “all work units scheduled on this
scheduler, in addition to being callable, must also have an asJSON
method”. And in fact that’s exactly what the longterm
module in Fritter
does.
I can neither confirm nor deny that this project was factored out of a game
engine for a secret game project which does not appear on this list.
What have I done with it recently?
Besides realizing, in the course of writing this blog post, that its CI was
failing its code quality static checks (oops), the last big change was the
preliminary support for recursive timers and serialization.
What are my plans for it?
What is it?
I have written about Encrust quite
recently so if you want to know about it,
you should probably read that post. In brief, it is a code-shipping tool for
py2app
. It takes care of architecture-independence, code-signing, and
notarization.
What have I done with it recently?
Wrote it. It’s brand new as of this month.
What are my plans for it?
I really want this project to go away as a tool with an independent existence.
Either I want its lessons to be fully absorbed into
Briefcase or perhaps
py2app
itself,
or for it to become a library that those things call into to do its thing.
Various Small Mac Utilities
What is it?
- QuickMacApp is a very small library
for creating status-item “menu bar apps” in Python which don’t have much of a
UI but want to run some Python code in the background and occasionally pop up
a notification or ask the user a question or something. The idea is that if
you have a utility that needs a minimal UI to just ask the user one or two
things, you should be able to give it a GUI immediately, without thinking
about it too much.
- QuickMacHotkey this is a very
minimal API to register hotkeys on macOS. this
example
is what comes up if you search the web for such a thing, but it hasn’t worked
on a current Python for about 11 years. This isn’t the “right” way to do
such a thing, since it provides no UI to set the shortcut, you’d have to
hard-code it. But
MASShortcut
is now archived and I haven’t had the opportunity to investigate
HotKey
, so for the time being, it’s a
handy thing, and totally adequate for the sort of quick-and-dirty
applications you might make with QuickMacApp
.
- VEnvDotApp is a way of giving a
virtualenv its own Info.plist and bundle ID, so that command-line python
tools that just need to pop up a little mac GUI, like an alert or a
notification, can do so with cross-platform tools without looking like it’s
an app called “Python”, or in some cases breaking entirely.
- MOPUp is a command-line updater for
upstream Python.org macOS Python. For distributing third-party apps,
Python.org’s version is really the one you want to use (it’s universal2, and
it’s generally built with compiler options that make it a distributable thing
itself) but updating it by downloading a
.pkg
file from a web browser is
kind of annoying.
What have I done with it recently?
I’ve been releasing all these tools as they emerge and are factored out of
other work, and they’re all fairly recent.
What are my plans for it?
I will continue to factor out any general-purpose tools from my
platform-specific Python explorations — hopefully more Linux and Windows too,
once I’ve got writing code for my own computer down, but most of the tools
above are kind of “done” on their own, at the moment.
The two things that come to mind though are that QuickMacApp should have a way
of owning the menubar sometimes (if you don’t have something like Bartender,
menu-bar-status-item-only apps can look like they don’t do anything when you
launch them), and that MOPUp should probably be upstreamed to python.org.
What is it?
Pomodouroboros is a
pomodoro timer with a
highly opinionated take. It’s based on my own experience of ADHD time
blindness,
and is more like a therapeutic intervention for that specific condition than a
typical “productivity” timer app.
In short, it has two important features that I have found lacking in other
tools:
- A gigantic, absolutely impossible to ignore visual timer that presents a HUD
overlay over your entire desktop. It remains low-opacity and static most of
the time but pulses every 30 seconds to remind you that time is passing.
- Rather than requiring you to remember to set a timer before anything
happens, it has an idea of “work hours” when you want to be time-sensitive
and presents constant prompting to get started.
What have I done with it recently?
I’ve been working on it fairly consistently lately. The big things I’ve been
doing have been:
- factoring things out of the Pomodouroboros-specific code and into
QuickMacApp
and Encrust
.
- Porting the UI to the redesigned core of the
application, which has
been implemented and tested in platform-agnostic Python but does not have
any UI yet.
- fully productionizing the build process and ensuring that Encrust is
producing binary app bundles that people can use.
What are my plans for it?
In brief, “finish the app”. I want this to have its own website and find a
life beyond the Python community, with people who just want a timer app and
don’t care how it’s written. The top priority is to replace the current data
model, which is to say the parts of the UI that set and evaluate timers and
edit the list of upcoming timers (the timer countdown HUD UI itself is fine).
I also want to port it to other platforms, particularly desktop Linux, where I
know there are many users interested in such a thing. I also want to do a CLI
version for folks who live on the command line.
Finally: Pomodouroboros serves as a test-bed for a larger goal, which is that I
want to make it easier for Python programmers, particularly beginners who are
just getting into coding at all, to write code that not only interacts with
their own computer, but that they can share with other users in a real way.
As you can see with Encrust and other projects above, as much as I can I want
my bumpy ride to production code to serve as trailblazing so that future
travelers of this path find it as easy as possible.
And Here Is Where The CTA Goes
If this stuff sounds compelling, you can obviously sign
up, that would be great. But also, if
you’re just curious, go ahead and give some of these projects some stars on
GitHub or just share this post. I’d also love to hear from
you about any of this!
If a lot of people find this compelling, then pursuing these ideas will
become a full-time job, but I’m pretty far from that threshold right now. In
the meanwhile, I will also be doing a bit of consulting work.
I believe much of my upcoming month will be spoken for with contracting,
although quite a bit of that work will also be open source maintenance, for
which I am very grateful to my generous clients. Please do get in
touch if you have something more specific you’d
like me to work on, and you’d like to become one of those clients as well.