WEBVTT
NOTE
Transcription provided by Podhome.fm
Created: 7/6/2024
4:40:44 PM
Duration: 2967.832
Channels: 1
1
00:00:13.985 -->
00:00:18.440 Hello, and welcome to podcast dot in it, the podcast about Python and the people who make it great.
2
00:00:19.000 -->
00:00:23.420I would like to thank everyone who supports us on Patreon. Your contributions help to make the show sustainable.
3
00:00:23.800 -->
00:00:28.140When you're ready to launch your next project, you'll need somewhere to deploy it, so you should check out linode at ww
4
00:00:30.215 -->
00:00:36.715w.podcastinnit.com/linode and get a $20 credit to try out their fast and reliable Linux virtual servers for running your app or experimenting with something that you hear about on the show.
5
00:00:37.415 -->
00:00:38.990You can visit the site at www.podcastinit.com
6
00:00:40.490 -->
00:01:01.500to subscribe to the show, sign up for the newsletter, read the show notes, and get in touch. To help other people find the show, please leave a review on Itunes or Google Play Music. Tell your friends and coworkers and share it on social media. Your host as usual is Tobias Macy. And today, I'm interviewing Glyph about automat, a library that provides self-service finite state machines for the programmer on the go. So, Glyph, could you please introduce yourself?
7
00:01:02.395 -->
00:01:03.594 Sure. I'm Glyph.
8
00:01:03.995 -->
00:01:09.170I've been doing Python for quite a long time. I am usually interviewed in my capacity
9
00:01:10.210 -->
00:01:18.204the Twisted Project. So it's refreshing to be here today to talk about another 1 of my projects because I do have a few. I'm a open source maintainer
10
00:01:18.585 -->
00:01:20.665and a professional programmer for the last,
11
00:01:21.784 -->
00:01:33.590closing in on 2 decades now, and you can see lots of my public speaking at PyCon and other conferences around the Internet. So I'm a what passes for a public intellectual, I guess, in our industry.
12
00:01:34.435 -->
00:01:39.975 Yeah. It's funny. This is actually the second time that you've been on the show and in neither case, have you been talking about Twisted?
13
00:01:40.755 -->
00:01:41.635 Yeah. No. It's,
14
00:01:42.115 -->
00:01:43.590you've got good taste, I guess.
15
00:01:44.710 -->
00:02:07.460 I figure it's just a subject that's probably been pretty well covered elsewhere, so we can discuss other things. So for anybody who missed it, you were on fairly early in this series. Don't remember the exact episode number, but I'll put it in the show notes, but you were talking about the idea of ethics and software development. So I'll refer people back to that episode if they wanna learn more about that topic. But in the meantime, how did you first get introduced to Python?
16
00:02:08.085 -->
00:02:10.485 Well, hopefully, this story changes every time. It's,
17
00:02:11.045 -->
00:02:15.945interesting for folks who who might have heard it before, as they've I'm hoping they're all listening
18
00:02:16.360 -->
00:02:20.620wrapped to each episode of your show. But I was, I was interested introduced to Python
19
00:02:20.920 -->
00:02:22.200because I was,
20
00:02:22.840 -->
00:02:24.620working on a video game
21
00:02:24.985 -->
00:02:37.090in Java. And as games tend to be, it was very script heavy, lots of things that needed to have dynamic behavior. And for a variety of reasons, I wanted to try and rewrite it in something new. And
22
00:02:37.469 -->
00:02:44.555because most of the logic in Java was hash table lookups of dynamic classes, after learning about Python's dynamic
23
00:02:44.855 -->
00:02:48.315method lookups and semantics and runtime metaprogramming,
24
00:02:48.840 -->
00:02:52.620Like, 90% of the code just dissolved. It did nothing because it's
25
00:02:52.920 -->
00:03:07.530trivial to write an app in Python that needs to, like, have dynamic behavior and get things wired together into hash tables of arbitrary methods because that's kinda what the runtime is already. So that was a great introduction in about, I wanna say, 2000 to Python,
26
00:03:07.830 -->
00:03:08.330152,
27
00:03:08.790 -->
00:03:16.655the still the best version of Python ever. I'm sure. And so I've been doing it ever since. As I mentioned in the introduction, we were gonna talk about your automat library.
28
00:03:17.275 -->
00:03:21.680 And to begin with, I'm wondering if you can just do a quick high level overview
29
00:03:22.060 -->
00:03:26.400of what a state machine is and some of the cases that you might want to use 1.
30
00:03:26.940 -->
00:03:29.120 So that's an interesting question because
31
00:03:29.515 -->
00:03:41.989all computers are state machines, and so you kinda might wanna use a state machine anytime you wanna use a computer. But, of course, that's way too broad of an answer, especially when it comes to dealing with a state machine library. So the real question is,
32
00:03:42.450 -->
00:03:50.575what's a finite state automaton, or what are certain formalisms around state machines that are interesting? And the the interesting thing about modeling
33
00:03:50.875 -->
00:03:55.510state as a finite state automaton or a deterministic finite state machine
34
00:03:56.209 -->
00:03:57.670is they're good for
35
00:03:58.129 -->
00:03:58.629modeling
36
00:03:59.010 -->
00:04:02.549objects that change over time that have different behavior
37
00:04:02.965 -->
00:04:04.985depending on what has happened to them previously.
38
00:04:05.364 -->
00:04:07.545So it's very easy to accidentally
39
00:04:07.924 -->
00:04:08.825implement yourself
40
00:04:09.364 -->
00:04:09.864something
41
00:04:10.325 -->
00:04:11.465that is effectively
42
00:04:11.924 -->
00:04:12.424a
43
00:04:12.730 -->
00:04:14.510deterministic finite state automaton
44
00:04:14.810 -->
00:04:26.185by setting a bunch of attributes and just checking them before certain methods do things. But, of course, that's very difficult to assert anything interesting about or to visualize or to understand.
45
00:04:26.725 -->
00:04:27.465And so,
46
00:04:28.245 -->
00:04:28.880really, it's
47
00:04:29.600 -->
00:04:39.875the idea behind a state machine library is that you might want to use a formal state machine to be able to get more insight into and and be able to more rigorously analyze
48
00:04:40.175 -->
00:04:52.660the code that you're already writing that already has this behavior that changes over time. So these different sort of types of state machines, deterministic finite state automata, are objects that have a state that have transitions
49
00:04:53.120 -->
00:04:53.620between
50
00:04:54.000 -->
00:04:54.900different states,
51
00:04:55.255 -->
00:04:59.035inputs that can cause them to potentially change states,
52
00:04:59.415 -->
00:05:04.715and outputs that are things that happen as a result of them changing states. So there's,
53
00:05:05.169 -->
00:05:12.265the usual place that state machines are applied is in parsers because there are an explosively huge number of states
54
00:05:12.665 -->
00:05:23.070that something that could, for example, parse c or Python. They might have millions of internal states, and so there are parser generators like yak and bison and,
55
00:05:23.550 -->
00:05:27.730flex that do a bunch of the machinery associated with manipulating state machines
56
00:05:28.125 -->
00:05:49.515so that you don't have to know that there's a state machine under the covers. The kind of state machine that AutoMat is is actually for kind of a higher level application. It's for something where you've got an object, you know that it's changing over time, you want to know that it's there, you kinda want to know what state it's in. So it's almost the opposite use case of what the usual kind of academic literature about state machines is about.
57
00:05:49.895 -->
00:05:54.315 And there are a number of other libraries on the Python package index
58
00:05:54.759 -->
00:05:59.659that show up if you do a search for state machines. So what is it about the
59
00:06:00.039 -->
00:06:09.355offerings that were available or the lack of offering that made you feel the need to build a new option, and what are some of the ways that it differs from what was already available?
60
00:06:09.975 -->
00:06:13.490 Well, no disrespect to any of the authors of any of those libraries,
61
00:06:13.949 -->
00:06:15.330but none of them were
62
00:06:15.630 -->
00:06:36.215terribly mature. Like, none of them struck me as particularly, like, the thing to compete with. If I was gonna write a web framework these days, I would have to have a big section on my web site about how does it compare to Django and how does it compare to Pyramid and how does it compare to Rails because they're kind of a couple of big names that people are gonna know about that that do this thing in a particular way. And when I looked around on PyPI,
63
00:06:36.595 -->
00:07:01.080because I did before I started this project, I did kind of a more thorough analysis of the field of competitors than I usually do before starting an open source project because it's kind of an academic computer sciency thing, and I'm not particularly academic computer scientist. So I wanted to see if somebody else had already done this. And what I found was that they were all sort of just enough to solve the problem that their author had had,
64
00:07:01.560 -->
00:07:08.165and they all, so that was the initial problem was that none of them were really kind of advanced enough
65
00:07:08.544 -->
00:07:15.264to be worth contributing to or extending as opposed to doing my own thing. And the big difference is that not only were they,
66
00:07:16.110 -->
00:07:23.970not super featureful, but they also all kind of fell into the same trap, which is that they provided convenience functionality.
67
00:07:24.495 -->
00:07:36.229And 1 of the ways that automatic is really different is that while I certainly hope it's convenient to use, it doesn't provide anything that goes outside the scope of the specific type of state machine that it is.
68
00:07:36.630 -->
00:07:45.835I believe the correct terminology for what it implements is it is a pure Mealy machine. This is as opposed to other types of state machines like war machines. And
69
00:07:46.135 -->
00:07:54.190the thing that I really want the reason that that was an important and interesting thing for it not to provide convenience functionality is much of the convenience functionality
70
00:07:54.970 -->
00:07:57.615in these other libraries are things like
71
00:07:57.935 -->
00:08:04.115a hook every time you enter a state or a hook every time you exit a state or interaction with
72
00:08:04.735 -->
00:08:05.920other pieces of
73
00:08:06.400 -->
00:08:13.780logic associated with the state machine that would help you sort of debug it or diagnose issues with it that were about stepping through the code
74
00:08:14.080 -->
00:08:25.930around the state machine and about allowing you access to its internal states. You could say, like, what's the current state, and what are the transitions away from this state? And the problem with that is that the goal of having a library to reflect
75
00:08:26.230 -->
00:08:27.690this type of state machine,
76
00:08:28.150 -->
00:08:29.930in my mind, is to provide consistency of behavior. Because if you have
77
00:08:37.725 -->
00:08:57.310color of that object should is the caller of that object should just say, like, I wanna send a message. And if you have a message sender that can be in 17 different states, the caller shouldn't have to care that it's in the connecting state or the connected state or the timing out state. It should always do the same thing as far as the caller is concerned when you say send a message. And the problem with
78
00:08:58.089 -->
00:08:58.589exposing
79
00:08:59.050 -->
00:09:18.810the current state of the state machine, for example, is it seems helpful. Like, you might wanna know that while you're debugging. You might wanna instrument it a little bit. You might have logic that was, like, a little different depending on what state it's in. But the problem is that that that pushes the behavior, which in order to get the benefit of a state machine, which is having this consistent interface for
80
00:09:19.155 -->
00:09:25.735outside callers to know that they can just send it the same message and it'll do the appropriate thing for its internal state. Instead,
81
00:09:26.275 -->
00:09:33.139now there's always this opportunity that the state machine can say, well, you really can only call this and these these 2 states, so
82
00:09:33.600 -->
00:09:57.774always check before you call. And all of the utility that you get from knowing that it's a state machine and having all of these pieces of information about it, like the transition table, goes right out the window because now the actual behavior is being decided by this combination of this innards of the state machine itself and whatever the caller is doing to check before it provides the inputs. So the idea behind
83
00:09:58.154 -->
00:09:59.855automatic was to make everything
84
00:10:00.475 -->
00:10:02.250very, very opinionated,
85
00:10:02.630 -->
00:10:05.850I guess, is the popular term for this these days, and and to
86
00:10:06.230 -->
00:10:13.175force all of the logic that should be in an output of the state machine to be in an output and to force all of the inputs
87
00:10:13.714 -->
00:10:18.615not advertise anything about the internal state of the state machine unless they explicitly want to.
88
00:10:18.970 -->
00:10:20.830 Yeah. I can definitely see where
89
00:10:21.290 -->
00:10:22.510being able to
90
00:10:23.050 -->
00:10:23.550expose
91
00:10:23.930 -->
00:10:28.215the internals of the state machine would very quickly lead to a leaky abstraction
92
00:10:28.675 -->
00:10:38.399and cause people to take shortcuts and then, like you said, thereby sort of short circuit the capabilities that you're asking for in the state machine in the first place.
93
00:10:38.700 -->
00:10:43.440And at that point, you might as well just go back to your nested conditionals and,
94
00:10:43.980 -->
00:10:53.485just not even bother with trying to incorporate the state machine library. Exactly. 1 of the other questions I have too is you're mentioning how you were evaluating
95
00:10:53.785 -->
00:10:55.885the other libraries that were available on PyPI.
96
00:10:56.190 -->
00:11:05.250But what is it that first drove you to even considering either contributing to or creating your own library for state machines? What was the specific need that put you in that situation?
97
00:11:05.875 -->
00:11:16.520 Well, as it turns out, we are here today to talk about Twisted because in fact, the the thing that led to the need for, state machine library was really the 100
98
00:11:17.460 -->
00:11:18.680of really unfortunate
99
00:11:19.300 -->
00:11:26.575informal state machines that live inside twisted. In particular, the thing that motivated me to really create this as a library,
100
00:11:27.035 -->
00:11:36.470was, Ashwini Uraganti was developing a new TLS library at 1 of the PyCon Sprints, I believe, 2 or 3 years ago, and she was
101
00:11:36.770 -->
00:11:43.254looking for a way to represent the TLS state machine and looking around for examples in twisted that were kind of good examples.
102
00:11:43.634 -->
00:11:45.574And unfortunately, there are very few,
103
00:11:45.954 -->
00:11:57.250and the few that there are mirror that exact same criticism that I have of state machine libraries that others have written, which is that extra abstractions and twisted, which attempt to model things in state machines expose
104
00:11:57.714 -->
00:12:26.820lots of internal implementation details. They have state variable. It's very easy to tell what state they're in. It's very easy to add little hooks to different elements of the state transition machinery. So I I I wanted to say, like, here's the way that Twisted does this well. And there are lots of things in Twisted that really kind of need state machines and don't have them yet. So 1 in particular, the first that I believe is already in a Twisted release, because Twisted does depend upon automatic now, is, client service. So in Twisted, you got this notion of,
105
00:12:27.280 -->
00:12:45.640an outbound connection needs to stay up because it's a pipe that you're streaming data to some other service, and it's a pipe that needs to be maintained consistently. And if the connection goes down, it needs to come back up. And our original implementation of this, reconnecting client factory is kind of a legacy API
106
00:12:46.260 -->
00:12:48.040for a whole bunch of different reasons.
107
00:12:48.740 -->
00:12:50.375It doesn't work super well
108
00:12:50.915 -->
00:13:00.830with SSL. It has, it uses older interfaces in the internals of the reactor. There's a lot of tight coupling. So we knew that we needed a a new thing that could
109
00:13:01.210 -->
00:13:07.725do this job in a more flexible way. And 1 of the interesting things about this is it turns out there's actually a bunch of states associated
110
00:13:08.425 -->
00:13:15.649with reconnecting because there's the initial state where you start off and nothing has started. Then the application can say, okay. Start up
111
00:13:16.269 -->
00:13:38.635and start maintaining the connection, and then it's in the connecting state. Then the connection actually gets established, and it's in the connected state. And later, the connection might be dropped, or you might shut it down, which are both different ways to reach other states like the disconnected state or the reconnecting state. And this seems very straightforward because if you think about it from the perspective of, like, how do I implement this in terms of
112
00:13:39.435 -->
00:13:53.400some logic inside the methods, you start thinking, oh, well, I'll have, like, a flag, and the flag will say, am I connected or not? And if I'm not connected, then maybe I should start connecting. And if somebody tries to send a message over this channel and it's not connected,
113
00:13:53.860 -->
00:14:26.090maybe I should buffer it or something. And so, like, you have you start adding all these little if checks. But the problem with all those little if checks is that with 1 flag, you have 2 cases to worry about in every method. So you take all your methods and you multiply the work by 2. And sometimes the work of that other branch of the false case is actually the same, and you can look at the method and say, oh, well, this method, it doesn't matter. It behaves the same way either way. That's why you got 1 Boolean. Then you get 2 Booleans. And now you have to add 4 cases to every method. Then you get 3 booleans, and it's 8.
114
00:14:26.550 -->
00:14:49.685And pretty soon, you're into a combinatorial explosion that you can't manage anymore. And the interesting thing about this is not only do you have this combinatorial explosion, but because it's a state space explosion and not an actual code complexity explosion, like, you got these flags, and until you remember to go in and add all the if checks that you need, it doesn't look any more complex. All of the bugs can't be caught by code coverage testing.
115
00:14:50.065 -->
00:15:09.764You there's no obvious unit test that you need to write because all of your code's covered, and you added every case you could think of. There's no tooling that can help you see the complexity that's arisen. So we rewrote client service to, use automatic, and we actually found a bunch of bugs, which we didn't even realize were there. So there's things like so there's things like that. There's the,
116
00:15:10.545 -->
00:15:16.250host name endpoint, which is the thing that just when you say, I wanna connect to google.com on port 443,
117
00:15:16.710 -->
00:15:23.290the amount of state transitions involved in that are kind of staggering. There's a branch in progress right now to rewrite
118
00:15:23.995 -->
00:15:28.395that particular twisted class in terms of an explicit state machine using automat.
119
00:15:28.795 -->
00:15:33.430And that effort has although it hasn't landed yet as a a patch in release,
120
00:15:33.810 -->
00:15:43.404it has found a couple of bugs in the way that we do host name resolution and a couple of edge cases that we haven't handled a 100 correctly. Because when you're making an outgoing connection, they're
121
00:15:43.865 -->
00:15:47.885you think of it as just like, I'm not connected. Now I'm connected. There's only 2 states.
122
00:15:48.250 -->
00:16:19.214What could be simpler? But in fact, you have to do DNS resolution. DNS resolution could give you back multiple results. So you start off in the, I haven't resolved the host name state, then you're in, I have resolved the host name state. Then you've got a bunch of host names, and you're connecting to the first 1, and you're timing out on the second 1. And so that's in the discrete state. And then you get all the way to the end of that list, and then you start transitioning into the, oh, now I'm draining the queue of outstanding connections. I don't have any more connections. You got time calls flying around all over the place here. So this type of logic is very common in an event driven context, not because
123
00:16:19.834 -->
00:16:21.214it's particularly more
124
00:16:21.595 -->
00:16:26.790necessary in an async program as an asynchronous 1, but it's more obvious because
125
00:16:27.250 -->
00:16:29.190you gen you tend to have more objects
126
00:16:29.570 -->
00:16:51.250that represent, like, connections and stuff as opposed to with threads. You know, you might have a lot of implicit connections or subprocesses, but you don't you don't have an object that has all the flags on it. You just have, like, a call stack where you're in the middle of something somewhere. But if you're doing anything concurrent, you very rapidly have start having all these flags and objects floating around that that might be in a bunch of different states. So that's kind of the the genesis of
127
00:16:52.584 -->
00:16:53.084why,
128
00:16:53.625 -->
00:16:55.245automat was necessary
129
00:16:55.625 -->
00:16:59.885for, like, for Twisted and where the idea came from originally.
130
00:17:00.519 -->
00:17:13.265 Yeah. And an additional place that I've seen state machines leveraged too is for capturing business logic, particularly in something something like ecommerce where there are certain states that an order can be in and different transitions.
131
00:17:13.725 -->
00:17:21.779So, again, it's something that at face value seems simple, but then you start getting into the special situations of, okay. Well, what happens if the payment gets declined
132
00:17:22.080 -->
00:17:23.700after the order has been placed?
133
00:17:24.000 -->
00:17:36.035Or what happens if there is a stock shortage after the order has been completed and now it's ready for shipment except for this 1 thing. So there are various different states that can happen there, and there are certain
134
00:17:36.630 -->
00:17:45.690business needs. But, again, as soon as you have multiple different representations of state, how do you ensure that you never get into an impossible state within your program?
135
00:17:46.275 -->
00:18:04.175Because then it's somebody has to go in and manually try to figure out what happened and then resolve it with it with the you know, within the situation of the data, which can get quite messy quite fast. And, you know, from what you were describing earlier of, you know, starting off with the very simple case and then adding a conditional and then just adding another conditional,
136
00:18:04.555 -->
00:18:23.185after a certain point, it becomes very tempting to just keep going down that chain because you've already gotten started. So what are some of the signs that people should be watching out for to signify that they should stop adding new conditional blocks and start looking into actually incorporating a state machine into their code? Yeah. So I think that there's there's nothing wrong with conditionals.
137
00:18:23.565 -->
00:18:25.025 If is a venerable
138
00:18:25.485 -->
00:18:34.200keyword in the structured programming pantheon. But the point where you you know you probably need a state machine is when you have a class that has a bunch of attributes.
139
00:18:34.500 -->
00:18:44.025You really know you've you're you you've fallen onto the spikes already when you when you start adding your 5th or 6th Boolean flag, like, is order declined,
140
00:18:44.645 -->
00:18:54.370is order delayed, is, you know, if you have if you have, like, 9 of those, you definitely wanna have a state machine because you got there as a workflow. And to your point, by the way, about, like, order systems
141
00:18:54.750 -->
00:18:55.250and
142
00:18:55.955 -->
00:19:02.935dealing with different states that they can that inventory and fulfillment and things like that can be in, that is absolutely
143
00:19:03.315 -->
00:19:03.815another
144
00:19:04.490 -->
00:19:05.870use case related to
145
00:19:06.170 -->
00:19:23.809asynchronous event driven programming. Because what you've got in those types of systems where you have orders coming in and fulfillment's happening and billing, those are all events that are happening to your business. And they might be happening as, like, a blocking WSGI thread and your technical implementation of them, but they are nevertheless
146
00:19:24.190 -->
00:19:41.030an event has occurred in the world that has now affected your business, and it is touching a piece of mutable state that has persisted over the life cycle of this process that's happening. So the thing the the number 1 sign that you need a state machine right now is you have 6 flags on your object and you're adding the 7th.
147
00:19:42.530 -->
00:19:46.150But the more subtle signs are just that you
148
00:19:46.505 -->
00:19:57.580have objects that are sometimes missing attributes that certain methods need. For example, if you ever see a trace back that's like an attribute error and you say, oh, yeah. That's I just need to check for that first because
149
00:19:58.120 -->
00:20:13.630it's fine that it doesn't have that attribute. Sometimes that's not set yet at this point in its life cycle, but I'll I'll just add another check. Even if you don't have a whole bunch of Boolean flags, a bunch of has adder checks are another indication that there's something going on there that really needs a state machine. And for somebody who
150
00:20:14.090 -->
00:20:22.065 is at the point where they decide that they're going to start incorporating a state machine into their code base, what does the process look like for adding automat,
151
00:20:22.445 -->
00:20:31.110and, what are some of the steps that she that they should be considering in the process of adding that to the as a as a dependency to their code base?
152
00:20:31.490 -->
00:20:37.125 Hopefully, the answer to that question is that it's it's very easy. AutoMat was designed
153
00:20:37.745 -->
00:20:42.404specifically to be something that's easy to integrate to an existing code base that's
154
00:20:42.950 -->
00:20:44.730really easy to adopt incrementally.
155
00:20:45.270 -->
00:20:49.3701 of the things about the other state machine libraries on PyPI that I didn't mention
156
00:20:50.105 -->
00:20:55.085is they all require that you call some method like input
157
00:20:55.465 -->
00:20:55.965or,
158
00:20:56.745 -->
00:20:57.245feed
159
00:20:57.800 -->
00:21:04.860or data or something, some some method to tell the state machine to turn a crank on itself. There's a state machine object with its own API.
160
00:21:05.160 -->
00:21:05.900With AutoMat,
161
00:21:06.335 -->
00:21:10.115the best way you can adopt it is to find some object where you're falling into that,
162
00:21:12.015 -->
00:21:18.5397 flags trap and decide what that object's public interface is. You've probably got some unit tests already.
163
00:21:20.440 -->
00:21:22.195Rip out the implementation of that,
164
00:21:22.595 -->
00:21:31.174replace it with an automatic based implementation, and nothing about it should change. The callers should all just invoke the the methods, which are in Automat's
165
00:21:31.730 -->
00:21:37.830terminology, the inputs, and it should sort of magically work because the whole goal of automat is to present this interface
166
00:21:38.290 -->
00:21:50.775that looks just like a regular Python object. The all of the state machine wizardry, all of the stuff that's happening with state transitions and state enforcements and whatever, that's all happening inside the object. The outer interface
167
00:21:51.210 -->
00:22:28.820is just, here are the methods, they have return values. And so and 1 of the things about that strategy of adopting it is that it kind of forces you to think about, like, what is your real public interface? Quite often when you're designing with AutoMat, you can remove a whole ton of private flags, you can remove a whole ton of other internal details, but you really need to think about, like, what are the things that are happening to this object over time? Like, how does its state change? The 1 gotcha that I would say right now about adopting AutoMatte is that there is 1 extremely subtle detail, which is a known bug and we are working on fixing it. There's been some implementation prototypes of how to fix this. But you can't
168
00:22:29.360 -->
00:22:33.285produce an input from an output. So if your state machine
169
00:22:33.585 -->
00:22:34.645receives an input
170
00:22:34.945 -->
00:22:35.605and then
171
00:22:36.065 -->
00:22:49.625runs an output method on itself as a result of generating that input, and then it that output wants to produce a new input. That recursion will sort of, like, look like it works, but it actually doesn't. You need to find some way to
172
00:22:50.025 -->
00:22:50.985pop out of that,
173
00:22:51.785 -->
00:23:08.515loop and provide the input from the outside. There's a new API coming, at some point called feedback, which will sort of automate the process of doing this. 1 of the reasons for this is that because an input can produce multiple outputs if the transition specifies it, you can't actually necessarily block
174
00:23:08.815 -->
00:23:12.915on your own input because automatic tries to provide certain guarantees.
175
00:23:13.215 -->
00:23:43.255And 1 of them is if you've made your way through the state machine to a particular state, all of the outputs that get you on your way to that state will have run. The idea there is that if some of the outputs set attributes on self, you should be able to rely on those attributes being there. You shouldn't need to have your has at or checks. You shouldn't need to have a do I have my connection yet? It's just in an output that's being generated from a certain state where you should have your connection or your, order number or whatever already. You should just be able to
176
00:23:43.555 -->
00:24:12.730rely on it, and you shouldn't have any conditionals at all in any of your outputs. So in order to preserve that property, reentrant inputs are not, are not supported and probably won't be supported, actually. Like, the thing that's gonna make it easier to deal with this problem is that there's gonna be better error reporting, and there's gonna be a facility for generating what's called feedback, which is, like, it'll push the new input into a queue, but you still won't be able to, like, return the result from that input. You you're still gonna need to kind of treat that as an asynchronous result that happens later.
177
00:24:13.030 -->
00:24:15.210 And when I was looking at the documentation,
178
00:24:16.245 -->
00:24:19.465I was seeing that as far as the actual implementation
179
00:24:19.845 -->
00:24:25.685detail, a large portion of the way that you actually get started is just by setting the,
180
00:24:26.150 -->
00:24:26.650machine
181
00:24:27.429 -->
00:24:37.305attribute on the class object or on the class that you're defining. And so I'm assuming that that's using some sort of meta class hook to be able to then
182
00:24:37.765 -->
00:24:38.265instantiate
183
00:24:38.805 -->
00:24:42.025the internal machinery for the state machine. Is that correct?
184
00:24:42.405 -->
00:24:43.305 Actually, no.
185
00:24:43.720 -->
00:24:45.020There's really
186
00:24:45.320 -->
00:24:50.140very little magical metaprogramming going on in the internals of automat at all. It will initialize,
187
00:24:51.080 -->
00:24:51.900the machine
188
00:24:52.280 -->
00:24:52.780object
189
00:24:53.125 -->
00:25:01.145as a descriptor. But almost all of the magic that happens inside of AutoMat is all of the decorators that are methods of that machine
190
00:25:01.445 -->
00:25:09.029object like input and state and output, those set things up so that when you call an input for the first time, it will
191
00:25:09.330 -->
00:25:11.554have all of the state that it needs. So,
192
00:25:12.035 -->
00:25:15.575it's actually for the amount of sort of convenience
193
00:25:15.955 -->
00:25:19.735of the way that you interact with the library and the way you interface with it.
194
00:25:20.070 -->
00:25:35.635There's actually very little magic going on. And once you understand how decorators work and you understand just, like, sort of creating a new callable from the existing callable, there's pretty much no other stuff. There's no metaclasses. There's no class hooks. There's no inheritance. It's just
195
00:25:36.169 -->
00:25:44.750straightforward. Like, the decorated method is an object in its own right. It has a documented interface, and that's it. And as far as, like, the internals of the library itself,
196
00:25:45.755 -->
00:25:46.975the the way that it's
197
00:25:47.275 -->
00:25:47.775structured
198
00:25:48.155 -->
00:25:53.615in terms of how those methods, like, initialize their state and interfaith interact with the user, those
199
00:25:54.510 -->
00:25:56.770the the magical internals are mostly
200
00:25:57.230 -->
00:26:06.435designed around preventing you from accessing any of the internal state of the library. So that machine object is there kind of in the class scope, but
201
00:26:06.815 -->
00:26:28.980once you've defined all of your inputs and outputs and states, it's not really there anymore. You can't really access any attributes on it. It's not an attribute of your instances as far as you can see. You just get attribute errors if you try to reach inside the machine to, like, grab the state or whatever. So that's that that's really the the prevailing design idiom of automat is everything's an implementation detail. Do not look inside the box.
202
00:26:30.100 -->
00:26:31.799 And for somebody who has
203
00:26:32.419 -->
00:26:33.480different pieces
204
00:26:33.780 -->
00:26:40.184or different workflows that they wanna be able to link together, is it possible to compose 2 different state machines
205
00:26:40.645 -->
00:26:46.345that are using automat to be able to create sort of a more hybridized or higher level workflow?
206
00:26:46.980 -->
00:26:47.480 Absolutely.
207
00:26:48.019 -->
00:26:57.435Composability of state machines was definitely an a goal of the library. And the way that it the composability works is that state machine a is an object and state machine b is an object,
208
00:26:57.815 -->
00:27:13.575and state machine a exposes some methods which are inputs and state machine b exposes some methods which are inputs. State machine a's inputs call output methods that are defined on state machine a, and those can just call methods on b. There's really nothing more to it than that. There is still the rule that,
209
00:27:13.975 -->
00:27:48.485a should not recursively generate a new input to a. However, that rule is the same whether b is another state machine or b is just a plain old Python object. So this is actually 1 of the things that makes adopting automat kind of so effortless is that even if every object in your entire code base will eventually turn into an automat object with a state machine inside it. You can migrate them 1 at a time, and no 2 of them will ever know if any of the objects they're talking to are using automatic. It's all just, like, an internal technical detail. The only thing that you really that sort of leaks out of that right now is
210
00:27:48.785 -->
00:28:04.115the errors that you get when you activate an invalid transition. But invalid transition errors are sort of like assertion errors. If you wanna deal with them, you don't actually want to add try, accept, catch the invalid transition. You just wanna add the a definition of that input
211
00:28:04.495 -->
00:28:12.940 from whatever state that input's not defined in right now. And 1 of the things that I found myself asking while I was reading through the docs is that because
212
00:28:13.480 -->
00:28:15.885of the fact that the implementation
213
00:28:16.665 -->
00:28:26.810of a state machine with automat is tied to a class, are there any cases where you would want to be able to have some sort of state transition capabilities
214
00:28:27.270 -->
00:28:33.185outside of a class where you're just using their Python functions maybe in sort of a, scripted environment?
215
00:28:33.805 -->
00:28:39.665 I think the main thing I would have to say about that question is don't be afraid of using little classes.
216
00:28:40.390 -->
00:28:43.770Even if you're in a script, writing a little class to represent your state
217
00:28:44.070 -->
00:28:54.695should be fine. That said, automat is very heavily, I guess, object oriented would probably be the the right term, in the sense that the reason you need the class is that you're providing
218
00:28:55.395 -->
00:29:05.340inputs to the state machine, and inputs are really method calls. Like, Python already has an a syntax for providing an input to a state machine, and it's dot word open parenthesis.
219
00:29:05.720 -->
00:29:12.925So I know a lot of, like, people in the scientific Python community or, folks who use things like Jupyter often
220
00:29:13.280 -->
00:29:31.290feel like they're not building libraries. They're not building big sophisticated systems, so they don't really use classes as much. But if you have a piece of state which changes over time and it needs to present a uniform interface, it's kinda hard to find a better tool in a class to model that. So,
221
00:29:31.670 -->
00:29:50.430automat could be thought of as, like, classes plus plus. And, you know, to those folks who are doing scientific Python who don't think of themselves as, like, writing technical assets, which like big fancy libraries that need lots of classes, something to remember there, I think, is, like, list is a class, tuple is a class. Even in Python, int is a class effectively.
222
00:29:50.810 -->
00:29:53.310So you're using lots of objects already,
223
00:29:53.770 -->
00:29:56.595and there's no reason that you shouldn't define your own to
224
00:29:56.975 -->
00:30:24.090as the task befits them. So hopefully in a scripting context, you're not doing a whole lot that involves, like, long term persistent state. But if you are dealing with a workflow that can kinda go 1 of 3 or 4 different ways, an automatic state machine in a little class that's just dedicated to that might be a good way to represent it. So in the process of building automat, what are some of the technical hurdles that you've been faced with and what are some of the ways that you have overcome them?
225
00:30:24.549 -->
00:30:44.350The interesting thing about Automat as a library is that it's there's very little in terms of technical meat. Like, the the algorithms are all extremely simple. It's, like, loop over this thing, call this thing, set this variable to a new value. So most of the hurdles are sort of more social. 1 of the tricky parts of it, though, technically, is,
226
00:30:44.889 -->
00:30:46.705is actually the visualization layer.
227
00:30:47.105 -->
00:30:53.205So 1 of the benefits that automatic can provide to you is if you declare your whole state transition graph
228
00:30:53.585 -->
00:30:54.085declaratively
229
00:30:54.385 -->
00:31:16.039as automatic you do, there's a data structure there that you can then inspect kind of at import time instead of at run time to say what are all of the states this might ever be in and then to visualize it. So there's actually a tool that comes with automatic, automatic dash visualize. It's like a shell command. You can run it over your Python code, and it'll spit out a diagram that shows you what your state machine looks like
230
00:31:16.500 -->
00:31:25.655and sort of it'll use graph is to draw out all of the states and the transitions, the inputs, the outputs. 1 of the most technically challenging things about writing this library was
231
00:31:26.115 -->
00:31:27.255developing a presentation
232
00:31:27.795 -->
00:31:35.120that made any sense at all that was readable at all because there's a lot of data you have to cram into a tiny little space. And it's by
233
00:31:35.500 -->
00:32:10.640by necessity, it's changing around a lot. Like, you add a transition and the entire machine is like a different shape now, and so labels overlap and they it's hard to choose a shape for the way they organize the inputs and outputs because if you just kind of let graphviz lay things out however it wants, the inputs will be, like, 7 inches away from the outputs that they're paired with because there's a lot of arrows in between them. So we tweaked that a fair amount, and I I think we have a reasonably good looking visualization, but that'll probably be where a lot of the kind of, like, long term means the project ends up going is just trying to make the visualizations
234
00:32:10.940 -->
00:32:19.985look good and trying to add features like, not just visualizing it statically where you show what any instance of the state machine might do over its entire,
235
00:32:20.550 -->
00:32:23.690lifetime, but to, like, add a debugger so that you can
236
00:32:23.990 -->
00:32:36.205visualize a particular instance, like what state is it in right now and what transitions did it take to get there, maybe doing a little animation. So most of the sort of real technical difficulty was either in that part or just in the kind of,
237
00:32:36.685 -->
00:32:37.185prereading
238
00:32:38.270 -->
00:32:39.490of all of the different
239
00:32:39.790 -->
00:32:43.090Wikipedia pages about state machines, because there's, like,
240
00:32:43.470 -->
00:32:59.45090 different kinds of state machine. The differences are very subtle. Luckily, there's only a few that are really of interest in computer science, but there are tons of things that start to touch on, like, set theory and mathematics and a bunch of other things, which, like I said, I'm not much of a theoretical computer scientist myself.
241
00:33:00.010 -->
00:33:07.504So I wasn't necessarily comfortable with all that stuff, and I I had to think really hard about it first because state machines are almost like cryptography
242
00:33:07.884 -->
00:33:21.465and that you kinda don't wanna have to roll your own theoretical primitives. You wanna be building on something that's a little more foundational. So I knew what general shape I wanted for this. And so, that term that I used earlier, a Mealy machine, was
243
00:33:22.485 -->
00:33:30.320the result of my efforts of scanning through all the different types of finite state machines and deterministic finite state automata and Mealy machines and more machines.
244
00:33:30.779 -->
00:33:40.325And there were at least 2 or 3 others that I saw and figuring out where in the hierarchy of different things, which all might be called the state machine, I actually wanted to implement.
245
00:33:40.705 -->
00:33:55.515 Yeah. That was 1 of the things that kind of stalled me on putting together this particular episode is that state machines as a concept is something that I had been wanting to cover for a little while. But every time I started to try and dip into that space, there are so many different implementations
246
00:33:56.055 -->
00:34:09.205in the Python package index. And then also, as you said, trying to read up on the sort of ideas behind state machines. That's 1 big rabbit hole to dive into. So when I, came across Automat and saw the fairly clean API
247
00:34:09.685 -->
00:34:39.660and, you know, low overhead of implementing it, I thought it was, you know, definitely a good case study in the ideas of state machines and a practical implementation of it that people could actually walk away with and benefit from at the end of the day. So I was pretty happy when I saw that this library crossed my path. Well, thank you very much. That is exactly the kind of feedback that I was really hoping for when I first started this library, especially because, you know, there's so many different things you can do with state machines and so many different problem domains that they apply to that I was really interested in this 1 specific
248
00:34:40.295 -->
00:34:47.070 domain, which is what I think most of the other libraries on PyPI really are also focused on, which is this domain of objects
249
00:34:47.550 -->
00:34:55.765that change state over time that have kind of like an application meaningful set of some small number, like a a couple of dozen at most states, where
250
00:34:56.164 -->
00:34:56.744the transitions
251
00:34:57.204 -->
00:35:06.460are, like, manually specified. It's not an optimizing compiler. It's not some kind of computer graphics thing. There's no, you know, there's no automatic generation of states
252
00:35:06.760 -->
00:35:20.185or lattice reduction steps or whatever to try to, like, turn something else into a state machine via automation, but to just build your own by hand with some with some automation. So I I I hope that by trying to kind of restrict this ambition to this very specific
253
00:35:20.645 -->
00:35:27.920narrow vertical niche of what the very, very broad category of state machines are for, it'll be a little more successful in some of the more general entries.
254
00:35:28.460 -->
00:35:34.955 And 1 of the things that you alluded to briefly is the idea that when you're actually going about testing
255
00:35:35.335 -->
00:35:42.400your code base when it has a state machine involved that actually simplifies a lot of the cases because you don't have to worry about the impossible
256
00:35:42.700 -->
00:35:44.240situations that can arise
257
00:35:44.615 -->
00:35:46.795from a business domain perspective.
258
00:35:47.175 -->
00:35:55.060And so I'm wondering if you can just dig a bit into what an actual test case would look like for a state machine versus just the combinatorial
259
00:35:55.520 -->
00:36:11.520 conditional explosion that somebody might find themselves in? So this is actually a very interesting question because I have yet to follow this to its logical conclusion. The first thing I should say is there's so much interesting stuff you can do in the area of testing and state machines
260
00:36:11.980 -->
00:36:46.875that it's very easy to get intimidated and to think that it's this crazy rocket sciency domain. So the first thing that I would tell anyone who's gonna try to adopt automata and what they're gonna do for testing is just write regular unit tests. If you've already got a thing you're trying to port to automatic and it's got unit tests, you shouldn't need to change anything. Just use that as your regression test suite because it's fine. If if you write a bunch of unit tests that check all of the cases that you think are interesting and make sure the behavior is correct, that works totally fine with. Automatic. You'll get reasonably good code coverage numbers. Like, everything will work more or less the same as you're used to, and you should definitely start with that. Because if you try to jump straight into the deep end
261
00:36:47.900 -->
00:36:56.800of model checking and generative testing, you will end up with, like, this explosively huge amount of stuff to learn, and it'll seem very very intimidating.
262
00:36:57.245 -->
00:37:20.945That said, the thing that you learn first about testing with AutoMat is that AutoMat will teach you that you're not testing quite as much as you thought. Like, all of these systems in twisted that I'm talking about replacing with explicit state machines that already had an implicit state machine. They already had a 100% test coverage. Like, we were very rigorous about that kind of stuff. And so there was no, like, big legacy untested
263
00:37:21.245 -->
00:38:03.905code in there. It was just, oh, now that we have a list of all of the state transitions that we can read, it's obvious that we're missing 2. Like, there really should be a transition from this state to this state, and there isn't 1, and we just never thought to write a test for it. So why don't we just go ahead and write a test for that that state transition? And that's the way that a lot of testing for projects that are initially adopt adopting AutoMat will go is you'll do all the same testing you would have done before, just plain vanilla unit test or py test or whatever your favorite tool is. You call the methods on the thing, and they call the outputs. And your logic is exercised, and you feel good. And then you run automatic visualize, and you look at state machine graph, and you're like, wait a second. Did we cover that transition? And then you go write some more tests, and you find out,
264
00:38:04.305 -->
00:38:12.565your state machine maybe had a couple of bugs in it that you didn't realize before. So it's the first thing that you get out of a state machine is definitely the additional visibility
265
00:38:12.865 -->
00:38:24.645into cases you might not have tested. And then you can test those the same boring old way you always have. Once you've you're comfortable there, then there are all there are a lot of other opportunities that having a state machine
266
00:38:24.945 -->
00:38:25.445presents.
267
00:38:25.985 -->
00:38:29.025And 1, this is what I said, I haven't actually done this myself,
268
00:38:29.505 -->
00:38:36.790but I it's been something that folks at PyCon and in the twisted community have been talking about with some excitement is using hypothesis,
269
00:38:37.250 -->
00:38:37.570which,
270
00:38:38.050 -->
00:38:41.714I don't know if all of your listeners would know about that, but it's a generative testing framework
271
00:38:42.095 -->
00:38:46.435for Python sort of modeled on Haskell's quick check. And it can
272
00:38:46.815 -->
00:38:47.315generate
273
00:38:47.615 -->
00:38:48.115inputs
274
00:38:48.450 -->
00:38:49.270for a program
275
00:38:49.570 -->
00:38:56.310somewhat randomly. And then it can discern based on the types of failures that it gets when it runs your tests with these different inputs,
276
00:38:56.905 -->
00:39:01.405which are the interesting values, which are the ones that need to be tested. And
277
00:39:02.665 -->
00:39:06.045what automatic gives you is because now you have
278
00:39:06.570 -->
00:39:16.105an explicit list of all of your state transitions and all of your inputs and all of your states, you can have use that as an input to hypothesis
279
00:39:16.885 -->
00:39:27.740and generate your testing data based on a kind of random selection of inputs to your state machine. And by combining the state machine and this generative testing
280
00:39:28.040 -->
00:39:48.474strategy, you will rapidly find these, like, edge cases that may have completely eluded you because you wouldn't have realized, like, oh, this is the these are my inputs and these are my outputs, and here's what I have to test. The few cases I've seen of people doing that, you wouldn't actually be able to tell that that's what they were doing because inputs in automat do actually potentially require parameters, for example.
281
00:39:48.775 -->
00:39:59.170And the types of those parameters still need to be in some defined range, and those defined ranges have to be specified to hypothesis using its usual test generation APIs.
282
00:39:59.605 -->
00:40:08.025So it actually just looks the same as if you had applied hypothesis to your existing application code with no use of a state machine at all. But the data
283
00:40:08.325 -->
00:40:12.590that was created as part of turning it into a state machine is a super useful
284
00:40:12.970 -->
00:40:27.540input and thinking tool when it comes to doing the input generation step. So there's also some potentially interesting stuff beyond that that gets into the realm of, model checking, but I am the wrong person to interview for that particular area.
285
00:40:28.080 -->
00:40:35.535So there are some things that are coming down the pipe in future releases of AutoMat, like a tracing API so that you can get coverage information
286
00:40:36.075 -->
00:40:57.375on your transition so that you can know not only where the lines of code covered, but all of the different orders that you can exercise them in were also covered. So that can provide both more information both on the sort of test design side, which is where your state transition table is useful, and then also on the analysis side to make sure your code coverage is, complete. There is no additional burden
287
00:40:57.994 -->
00:41:00.095that automat imposes on your code
288
00:41:00.750 -->
00:41:11.405to make it testable or to do testing with it. But there are a bunch of additional opportunities that it creates where you can do more testing and you can be more confident that your testing is capturing all of your interesting distinctions.
289
00:41:11.945 -->
00:41:14.285 And you mentioned the tracing API
290
00:41:14.905 -->
00:41:16.365and your feedback
291
00:41:16.825 -->
00:41:23.770API as well as planned features that you're gonna be adding in the future. So I'm wondering, are there any other capabilities
292
00:41:24.150 -->
00:41:27.575that you have on the roadmap that people should be keeping an eye out for?
293
00:41:27.974 -->
00:41:28.954 Nothing terribly
294
00:41:29.255 -->
00:41:38.450big. I think there's probably gonna be some refactoring in the area of what we we call collation, which is that because an automatic input can produce
295
00:41:38.830 -->
00:41:39.810multiple outputs,
296
00:41:40.110 -->
00:41:43.490there's a step where when the output start getting generated,
297
00:41:44.005 -->
00:41:51.530those they get converted into a return value for your input method. And I think some of the decisions that we made in the first release of automat
298
00:41:52.010 -->
00:42:18.485were kind of formally correct, but, like, not super helpful. Like, it used to return an iterator, which meant that your outputs wouldn't always be run immediately, which was not purely that helpful. And, even now, I believe it still returns a list. And quite often, what you want if you have an output an input that produces a single output via its 1 transition, you just wanna return that value. So there's probably gonna be some tweaks in the way that you define state transitions to make it easier to do certain idiomatic things. But beyond that,
299
00:42:19.125 -->
00:42:47.150I really think automat is it's 1 of those libraries that can kinda be done in that it performs a fairly simple function. 1 of the most important things about it is that it restricts the scope of that function, that any kind of scope creep into, like, oh, let's add an automatic, you know, hook that gets called every time you enter the state, or let's add a way to get the state out because that would be useful for debugging. Those kinds of things are explicit non features. So I think it'll remain a fairly small and tidy library that just does this 1 thing. The
300
00:42:48.165 -->
00:42:50.025so in addition to the small cleanups,
301
00:42:50.805 -->
00:42:55.845longer term, probably most of the energy will go into making the visualizations better, making it easier to,
302
00:42:56.520 -->
00:42:57.579extract information
303
00:42:58.200 -->
00:43:02.359from the running state machine, do logging or tracing or things like that.
304
00:43:03.000 -->
00:43:06.445We already have certain APIs that do things like allow you
305
00:43:06.825 -->
00:43:45.135to serialize the current state that were fairly carefully designed to well, I guess I should explain first. I've talked a lot about how you don't wanna expose the internal state of your state machine, except you totally need to because sometimes you have a state machine, like, in your order processing example. The actual state transitions in that order processing example probably happen 1 at a time where something happens, your state machine gets saved to a database, and then a couple hours later, another thing happens, it gets deserialized from that database, you run the input, and then you put it back in the database. And in order to put it into the database, you need to know its internal state. So we tried very hard to come up with an API where you can technically get the internal state out of the state machine
306
00:43:45.435 -->
00:43:50.080via a public API. That's totally doable. But the shape of that public API is
307
00:43:50.860 -->
00:43:59.415extremely focused on making it really easy if what you're doing is, like, doing the kinds of things 1 would need to do to serialize the state machine to a database
308
00:43:59.795 -->
00:44:00.775and incredibly
309
00:44:01.075 -->
00:44:08.680inconvenient and painful if what you want is, like, a dot state attribute that you can check before doing something. So in that same way,
310
00:44:09.060 -->
00:44:13.400we need to do some thinking about how to make sure the tracing API and the other
311
00:44:13.705 -->
00:44:20.745internal things that will make it easier to get bits of information out don't accidentally make it too easy to,
312
00:44:21.225 -->
00:44:22.285subvert the
313
00:44:22.630 -->
00:44:24.010intended design of AutoMat.
314
00:44:24.550 -->
00:44:31.610 So are there any interesting use cases that you've seen of people leveraging AutoMat outside of Twisted?
315
00:44:32.454 -->
00:44:47.020 It's pretty early for the library, I think. It didn't see a lot of promotion until, fairly recently. As I believe I mentioned at the beginning of the project's life, Ashwini Aruganti was asking about how she might use a state machine library to write a TLS implementation.
316
00:44:47.320 -->
00:44:54.925And at this last PyCon, she gave a talk about, among other things, automatic, generally using state machines to design secure software. So
317
00:44:55.305 -->
00:45:06.830that has sparked a lot of interest, and we're starting to see a lot of really interesting kind of proposed uses. But I haven't heard a lot from users who have done a lot kind of in anger and production yet outside of twisted and a couple of,
318
00:45:07.494 -->
00:45:08.315related applications.
319
00:45:08.775 -->
00:45:14.875There were some very cool things that I heard about at PyCon, which I'm never quite sure how kind of, like,
320
00:45:15.450 -->
00:45:22.510public these things are. So I probably shouldn't repeat any of them here, but I hope that the next time we we talk about this, there'll be some really cool stuff.
321
00:45:23.005 -->
00:45:30.705 So are there any other topics or questions that you think we should talk about before we start to close out the show? Well, I guess I I should just kind of
322
00:45:32.520 -->
00:45:37.980 give an impassion to plea to anyone who's got 1 of these libraries or or applications that are
323
00:45:38.599 -->
00:45:39.400stuck in this,
324
00:45:39.880 -->
00:45:47.795quagmire of way too many little state flags and way too many little values and counters that are proliferating thousands of little conditionals.
325
00:45:49.539 -->
00:45:53.559Give automat a try and definitely, like, report bugs and,
326
00:45:54.660 -->
00:45:56.279help out on the project because
327
00:45:56.695 -->
00:46:02.315it's kind of an easy project to work on. There's a tiny, tiny public API. There's not a whole lot to,
328
00:46:03.415 -->
00:46:06.075to mess up. There's a 100% test coverage on everything.
329
00:46:07.099 -->
00:46:26.280It's all in memory. It's just a data structure. So, you know, if you've been thinking, you know, oh, determine deterministic finite state automata, this sounds like too fancy. I'm not really a computer sciency person. Like, don't worry. Neither am I, and it can give you a lot of advantages to the, to reduce the complexity of your code
330
00:46:26.820 -->
00:46:46.660 and give you more confidence that you understand what it's doing. Alright. Well, I will have you add your preferred contact information to the show. So anybody who wants to follow-up and give feedback on their work with Automat can get in touch and see the, progress being made on that and on its usage within Twisted. And so with that, I'll move us to the picks.
331
00:46:46.960 -->
00:46:49.299And for my pick today, I'm going to choose
332
00:46:49.680 -->
00:46:54.085a, set of lights that I found a little while ago at Home Depot that's actually
333
00:46:54.565 -->
00:46:57.9453 little LED pucks with a remote, and it's actually
334
00:46:58.405 -->
00:46:59.385a multicolored
335
00:46:59.845 -->
00:47:01.865LED light. So you can actually change
336
00:47:02.165 -->
00:47:18.005the hue from white all the way through to red. And you can do it either automatically or, you know, jump straight to to the color, or you can actually manually increment through each of the different shades as you go, and you can also vary the light intensity. So it's pretty nice for being able to,
337
00:47:18.785 -->
00:47:22.200set add some interesting light effects to whatever room you're in,
338
00:47:22.520 -->
00:47:31.225and it's fairly affordable. So thought it was pretty interesting and worth checking out. So I'll add a link to that in the show notes. And with that, I will pass it to you. Do you have any picks for us today, Glyph? Sure.
339
00:47:31.945 -->
00:47:34.845 I guess my pick for today would be OmniFocus,
340
00:47:35.545 -->
00:47:38.045which is a task tracking application
341
00:47:38.540 -->
00:47:41.119for the Mac and iOS and watchOS.
342
00:47:41.580 -->
00:47:51.375And, it's difficult to overstate my reliance on OmniFocus. It kinda runs my entire life, and it's germane to today's show because,
343
00:47:52.015 -->
00:47:58.210when an when a state transitioning event occurs in my life, OmniFocus is the place that it goes to.
344
00:47:58.530 -->
00:48:06.585It that's so it it's a getting things done based system that allows you to input all the things that you wanna do. And then at at a particular time,
345
00:48:07.045 -->
00:48:07.705you can
346
00:48:08.245 -->
00:48:15.660view the tasks, which are sort of currently available, like things that you could do right now, things that aren't, like, dependent on anything else, things that,
347
00:48:15.980 -->
00:48:25.015are not deferred into the future, things that have an active context. So, like, you can have different context for home and work and different people that you might need,
348
00:48:25.315 -->
00:48:42.675help from in order to do a particular task, and you can activate, deactivate them, and then see if you have just the things that you kinda should actually be doing right now. Big fan. Always glad to promote the excellent work of the Omni Group, which is the software company that makes that app. Alright. Well, I appreciate you taking the time out of your day
349
00:48:43.055 -->
00:48:48.435 to talk about Automat and the work you've been doing with that. Definitely seems like a very interesting and useful library.
350
00:48:48.790 -->
00:48:59.455 So I appreciate your time, and I hope you enjoy the rest of your evening. Thank you very much, and thank you for the opportunity to spread the gospel of state machines and not having a million if checks.