summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmacsConf <emacsconf-org@gnu.org>2025-12-06 10:45:37 -0500
committerEmacsConf <emacsconf-org@gnu.org>2025-12-06 10:45:37 -0500
commit8b4b8108e1ae24c3b2a976ddfa4a6779c10f0f34 (patch)
treea3ccc508c0cc55b029c9ee8e5012b703ab637cb2
parentb987d21df6fb0ce2ccf24a501824e900e320a350 (diff)
downloademacsconf-wiki-8b4b8108e1ae24c3b2a976ddfa4a6779c10f0f34.tar.xz
emacsconf-wiki-8b4b8108e1ae24c3b2a976ddfa4a6779c10f0f34.zip
Automated commit
-rw-r--r--2025/captions/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.vtt1108
-rw-r--r--2025/info/swanky-after.md380
-rw-r--r--2025/info/swanky-before.md4
3 files changed, 1490 insertions, 2 deletions
diff --git a/2025/captions/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.vtt b/2025/captions/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.vtt
new file mode 100644
index 00000000..35eb7ce7
--- /dev/null
+++ b/2025/captions/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.vtt
@@ -0,0 +1,1108 @@
+WEBVTT captioned by sachac
+
+00:00:00.880 --> 00:00:02.439
+Hello everyone, I'm Scott
+
+00:00:02.440 --> 00:00:04.239
+and I'll be talking about Swanky Python,
+
+00:00:04.240 --> 00:00:06.199
+which is a development environment for Python
+
+00:00:06.200 --> 00:00:08.319
+based on Emacs' Slime package.
+
+00:00:08.320 --> 00:00:11.679
+So what is that and why might you find it interesting?
+
+00:00:11.680 --> 00:00:15.279
+SLIME is the Superior Lisp Interaction Mode for Emacs.
+
+00:00:15.280 --> 00:00:17.999
+It's an Emacs package for developing Common Lisp,
+
+00:00:18.000 --> 00:00:20.679
+and it's a bit different from the way we develop most languages
+
+00:00:20.680 --> 00:00:22.599
+in that you're always connected
+
+00:00:22.600 --> 00:00:25.399
+to a running instance of your application,
+
+00:00:25.400 --> 00:00:27.959
+and you kind of build up your application, piece by piece,
+
+00:00:27.960 --> 00:00:30.399
+modifying one expression at a time
+
+00:00:30.400 --> 00:00:34.559
+without ever having to restart your application.
+
+00:00:34.560 --> 00:00:36.679
+So why might you want to develop this way?
+
+00:00:36.680 --> 00:00:40.039
+One advantage is that you can get a faster feedback loop.
+
+00:00:40.040 --> 00:00:42.599
+For some kinds of software, it doesn't make a big difference.
+
+00:00:42.600 --> 00:00:43.919
+Like, if you're developing a web backend
+
+00:00:43.920 --> 00:00:48.039
+where all state is stored externally in a database,
+
+00:00:48.040 --> 00:00:50.279
+then you can have a file watcher
+
+00:00:50.280 --> 00:00:52.799
+that just restarts the whole Python process
+
+00:00:52.800 --> 00:00:54.639
+whenever you make any edit,
+
+00:00:54.640 --> 00:00:56.159
+and you're not really losing anything,
+
+00:00:56.160 --> 00:00:59.679
+because all the state is stored outside the Python process
+
+00:00:59.680 --> 00:01:01.719
+in a database. So it works great.
+
+00:01:01.720 --> 00:01:03.559
+But for other kinds of software, like
+
+00:01:03.560 --> 00:01:05.559
+let's say you're developing an Emacs package
+
+00:01:05.560 --> 00:01:07.279
+or a video game,
+
+00:01:07.280 --> 00:01:10.319
+then it can be a real pain to restart the application
+
+00:01:10.320 --> 00:01:12.679
+and recreate the state it was in before
+
+00:01:12.680 --> 00:01:17.279
+just to test the effect of each edit you want to make.
+
+00:01:17.280 --> 00:01:21.359
+Another advantage is the runtime introspection you have available.
+
+00:01:21.360 --> 00:01:22.679
+So since you're always connected
+
+00:01:22.680 --> 00:01:24.999
+to a running instance of your application,
+
+00:01:25.000 --> 00:01:27.799
+you can inspect the values of variables,
+
+00:01:27.800 --> 00:01:30.959
+you can trace functions, and all sorts of other information
+
+00:01:30.960 --> 00:01:36.279
+to help you understand your application better.
+
+00:01:36.280 --> 00:01:39.919
+And lastly, it's just a lot of fun to develop this way,
+
+00:01:39.920 --> 00:01:43.519
+or at least I find it fun developing with SLIME,
+
+00:01:43.520 --> 00:01:45.759
+so I wrote a SLIME backend for Python
+
+00:01:45.760 --> 00:01:48.799
+so I could have more fun when I'm coding in Python.
+
+00:01:48.800 --> 00:01:52.599
+As for the name swanky-python, within SLIME,
+
+00:01:52.600 --> 00:01:56.279
+swank is the name of the Common Lisp backend
+
+00:01:56.280 --> 00:01:59.199
+that runs within your Common Lisp application
+
+00:01:59.200 --> 00:02:02.919
+and connects to Emacs. So I'm not too creative.
+
+00:02:02.920 --> 00:02:07.999
+swanky-python is just a swank implementation in Python.
+
+NOTE Demo
+
+00:02:08.000 --> 00:02:15.279
+So let's see it in action. So we started up with M-x slime.
+
+00:02:15.280 --> 00:02:19.639
+And what that does is it starts a Python process,
+
+00:02:19.640 --> 00:02:25.039
+starts swanky-python within it, and connects to it from Emacs.
+
+00:02:25.040 --> 00:02:29.039
+And you can configure how exactly it runs Python.
+
+00:02:29.040 --> 00:02:32.479
+Or you can start swanky python manually
+
+00:02:32.480 --> 00:02:35.119
+within a Python application running on a remote server
+
+00:02:35.120 --> 00:02:36.313
+and forward the port locally
+
+00:02:36.614 --> 00:02:40.919
+and connect to it in Emacs, from Emacs remotely.
+
+00:02:40.920 --> 00:02:43.239
+Within the README, there's more documentation
+
+00:02:43.240 --> 00:02:45.519
+on other ways to start it.
+
+00:02:45.520 --> 00:02:52.159
+But just M-x slime is the basic way that works most of the time.
+
+00:02:52.160 --> 00:02:55.759
+So within the REPL, the first thing you'll notice is that
+
+00:02:55.760 --> 00:02:58.839
+REPL outputs are clickable buttons,
+
+00:02:58.840 --> 00:03:02.119
+what SLIME calls presentations.
+
+00:03:02.120 --> 00:03:04.759
+So you can do things like inspect them.
+
+00:03:04.760 --> 00:03:09.759
+And for each presentation, in the Python backend,
+
+00:03:09.760 --> 00:03:12.479
+it holds on to the reference to the object.
+
+00:03:12.480 --> 00:03:14.559
+So for an int, it's not too interesting,
+
+00:03:14.560 --> 00:03:20.239
+but let's do a more complex object like a file.
+
+00:03:20.240 --> 00:03:22.519
+Then we can inspect the file.
+
+00:03:22.520 --> 00:03:26.599
+We can describe it, which will bring up documentation
+
+00:03:26.600 --> 00:03:33.759
+on that class. We can use it in further expressions
+
+00:03:33.760 --> 00:03:39.431
+like if we copy it, it will use the actual Python object
+
+00:03:39.432 --> 00:03:43.399
+in this expression.
+
+00:03:43.400 --> 00:03:48.319
+We can assign it to a variable.
+
+00:03:48.320 --> 00:03:50.999
+SLIME uses presentations everywhere
+
+00:03:51.000 --> 00:03:53.239
+that a Python object would be displayed.
+
+00:03:53.240 --> 00:03:56.559
+So instead of just their string representation,
+
+00:03:56.560 --> 00:04:00.239
+when you have a backtrace on an exception,
+
+00:04:00.240 --> 00:04:03.965
+or you... within the inspector or anywhere else really,
+
+00:04:03.966 --> 00:04:06.019
+anywhere that the string representation
+
+00:04:06.020 --> 00:04:07.940
+of an object would be displayed,
+
+00:04:07.941 --> 00:04:10.740
+it displays a presentation that you can go on to
+
+00:04:10.741 --> 00:04:14.960
+inspect, reuse, or send to the REPL and so on.
+
+00:04:14.961 --> 00:04:23.039
+One useful utility function is pp for print presentation.
+
+00:04:23.040 --> 00:04:25.119
+We haven't imported it yet.
+
+00:04:25.120 --> 00:04:29.159
+So when we get a name error exception
+
+00:04:29.160 --> 00:04:33.879
+and SLIME sees that that name is available for import somewhere,
+
+00:04:33.880 --> 00:04:38.279
+it'll give us the option of importing it.
+
+00:04:38.280 --> 00:04:40.599
+Since it's available for import from multiple modules,
+
+00:04:40.600 --> 00:04:43.919
+it'll prompt us for which one we want to import it from.
+
+00:04:43.920 --> 00:04:45.519
+We want to import it from swanky-python,
+
+00:04:45.520 --> 00:04:48.479
+not from the standard library.
+
+00:04:48.480 --> 00:04:52.599
+Then it will print a presentation of that object.
+
+00:04:52.600 --> 00:04:55.559
+Within the REPL, this is not really useful
+
+00:04:55.560 --> 00:04:58.919
+because all REPL outputs are already presentations.
+
+00:04:58.920 --> 00:05:02.799
+But I use this now whenever I would use print debugging,
+
+00:05:02.800 --> 00:05:05.639
+just whenever I would use insert print statements in my program
+
+00:05:05.640 --> 00:05:08.399
+to see what's going on, I have it print a presentation
+
+00:05:08.400 --> 00:05:11.199
+because that way I can go back and inspect it later,
+
+00:05:11.200 --> 00:05:16.599
+copy it to the REPL and further manipulate it and so on.
+
+NOTE Inspector
+
+00:05:16.600 --> 00:05:20.119
+Next up, let's look at the inspector more.
+
+00:05:20.120 --> 00:05:25.579
+If we go back and inspect the file object,
+
+00:05:25.580 --> 00:05:27.239
+you can write custom inspector views
+
+00:05:27.240 --> 00:05:28.839
+for different kinds of objects.
+
+00:05:28.840 --> 00:05:32.519
+So far, I just have a couple. One for sequences,
+
+00:05:32.520 --> 00:05:36.919
+one for mappings, and one for every other kind of object.
+
+00:05:36.920 --> 00:05:45.979
+Like if we inspect a mapping, there's a shortcut
+
+00:05:45.980 --> 00:05:48.639
+inspect last result, which is what I normally use
+
+00:05:48.640 --> 00:05:52.379
+to open the inspector. Then we see the values,
+
+00:05:52.380 --> 00:05:56.319
+and each value in the inspector is a presentation
+
+00:05:56.320 --> 00:05:58.419
+that we can go on to inspect, and so on.
+
+00:05:58.420 --> 00:06:03.979
+Let's go back to inspecting the file object.
+
+00:06:03.980 --> 00:06:06.039
+Again, we can inspect each of the values,
+
+00:06:06.040 --> 00:06:10.239
+we can copy them back to the REPL and so on.
+
+00:06:10.240 --> 00:06:13.839
+It just displays all the attributes for the class
+
+00:06:13.840 --> 00:06:15.399
+and their values.
+
+00:06:15.400 --> 00:06:18.119
+We can configure what attributes we want to show.
+
+00:06:18.120 --> 00:06:21.119
+There's a transient menu where we can toggle
+
+00:06:21.120 --> 00:06:23.359
+if we want to show private attributes, dunder attributes,
+
+00:06:23.360 --> 00:06:26.439
+doc strings, so on, or everything,
+
+00:06:26.440 --> 00:06:28.519
+which is a bit much to show by default.
+
+00:06:28.520 --> 00:06:33.719
+So we'll reset it to the default.
+
+00:06:33.720 --> 00:06:37.839
+In the future, I want to add graphical inspector views
+
+00:06:37.840 --> 00:06:40.679
+for different kinds of objects, and also support
+
+00:06:40.680 --> 00:06:42.999
+showing plots in both the inspector and the REPL,
+
+00:06:43.000 --> 00:06:47.719
+but that's future work I haven't started on yet.
+
+NOTE Evaluating Python
+
+00:06:47.720 --> 00:06:51.999
+Let's look at the different options for evaluating Python.
+
+00:06:52.000 --> 00:06:59.099
+So we can evaluate a whole file.
+
+00:06:59.100 --> 00:07:00.639
+We can evaluate just a class.
+
+00:07:00.640 --> 00:07:03.479
+We can evaluate just the method we're working on.
+
+00:07:03.480 --> 00:07:06.359
+We can evaluate a Python statement,
+
+00:07:06.360 --> 00:07:11.839
+and it will show the result in an overlay next to the cursor.
+
+00:07:11.840 --> 00:07:17.919
+We can select some code and just evaluate the highlighted region.
+
+00:07:17.920 --> 00:07:24.799
+We can sync the REPL to the active file.
+
+00:07:24.800 --> 00:07:27.319
+So now everything we evaluate in the REPL will be in the
+
+00:07:27.320 --> 00:07:29.639
+context of the eval_demo module.
+
+00:07:29.640 --> 00:07:35.399
+We can also set the module that the REPL is in.
+
+00:07:35.400 --> 00:07:38.279
+We can go back to main.
+
+00:07:38.280 --> 00:07:43.679
+But let's go back to the eval_demo module for now.
+
+NOTE Updating
+
+00:07:43.680 --> 00:07:49.799
+One useful thing is when you update a class or a function,
+
+00:07:49.800 --> 00:07:54.539
+it updates old instances of that class or function.
+
+00:07:54.540 --> 00:07:58.479
+So right now, f.bar is foobar.
+
+00:07:58.480 --> 00:08:03.719
+But if we edit that class, it will actually edit the code
+
+00:08:03.720 --> 00:08:05.239
+for the old instance of that class.
+
+00:08:05.240 --> 00:08:07.599
+And that's provided by code I copied
+
+00:08:07.600 --> 00:08:12.079
+from IPython's autoreload extension.
+
+00:08:12.080 --> 00:08:14.639
+It helps when you're trying to develop in Python
+
+00:08:14.640 --> 00:08:16.498
+without having to restart the Python process
+
+00:08:16.499 --> 00:08:20.039
+whenever you make a change.
+
+00:08:20.040 --> 00:08:22.599
+Auto reload in Python is a big topic
+
+00:08:22.600 --> 00:08:26.519
+that I don't really have time to go into here,
+
+00:08:26.520 --> 00:08:29.479
+but right now it is more limited
+
+00:08:29.480 --> 00:08:32.559
+than what is done in Common Lisp.
+
+00:08:32.560 --> 00:08:35.759
+Like for example, if you have a data class in Python
+
+00:08:35.760 --> 00:08:37.619
+and you add a new field to the data class,
+
+00:08:37.620 --> 00:08:41.039
+it won't automatically update old instances
+
+00:08:41.040 --> 00:08:43.399
+of the data class with a new field.
+
+00:08:43.400 --> 00:08:46.599
+So there's more that needs to be done with that,
+
+00:08:46.600 --> 00:08:50.359
+but I am perhaps naively optimistic
+
+00:08:50.360 --> 00:08:54.279
+that Python's runtime is quite dynamic and flexible,
+
+00:08:54.280 --> 00:08:59.799
+and that I can fully implement autoreload in Python,
+
+00:08:59.800 --> 00:09:02.119
+but there's still work to be done,
+
+00:09:02.120 --> 00:09:05.419
+and it's a big topic to go into.
+
+00:09:05.420 --> 00:09:08.959
+Next up, let's look at the backtrace buffer.
+
+00:09:08.960 --> 00:09:12.839
+But as it is right now, autoreload is actually useful.
+
+00:09:12.840 --> 00:09:16.959
+I mostly develop in Python without having to restart the process
+
+00:09:16.960 --> 00:09:19.599
+and without running into issues from old state
+
+00:09:19.600 --> 00:09:22.899
+that hasn't been updated properly.
+
+NOTE Backtraces
+
+00:09:22.900 --> 00:09:25.999
+So if we go on to look at the backtrace buffer,
+
+00:09:26.000 --> 00:09:32.819
+whenever we get an exception in Python...
+
+00:09:32.820 --> 00:09:37.079
+Let's go back to it.
+
+00:09:37.080 --> 00:09:41.419
+Whenever we get an exception, it will...
+
+00:09:41.420 --> 00:09:43.698
+let's change the code so that it actually
+
+00:09:43.699 --> 00:09:49.965
+gets an exception...
+
+00:09:49.966 --> 00:09:52.519
+we will get an interactive backtrace buffer
+
+00:09:52.520 --> 00:09:57.599
+where we can browse the source code for the different stack frames
+
+00:09:57.600 --> 00:10:00.199
+and the local variables within the stack frames,
+
+00:10:00.200 --> 00:10:03.439
+which are all presentations that we can inspect and so on.
+
+00:10:04.340 --> 00:10:10.619
+We can also open a REPL in the context of any stack frame.
+
+00:10:10.620 --> 00:10:16.439
+Or we can, when we go to the source for a given stack frame,
+
+00:10:16.440 --> 00:10:20.359
+we can select some Python code and evaluate it
+
+00:10:20.360 --> 00:10:25.959
+within the context of that stack frame.
+
+00:10:25.960 --> 00:10:30.699
+One major limitation compared to SLIME for Common Lisp
+
+00:10:30.700 --> 00:10:33.759
+is that in Common Lisp, you have the option to
+
+00:10:33.760 --> 00:10:38.159
+restart or resume execution from a given stack frame
+
+00:10:38.160 --> 00:10:42.439
+after an exception happens, where in Python,
+
+00:10:42.440 --> 00:10:45.799
+what we have right now is pretty much equivalent to
+
+00:10:45.800 --> 00:10:47.159
+the postmortem debugger.
+
+00:10:47.160 --> 00:10:50.839
+You can view the state that the call stack was in
+
+00:10:50.840 --> 00:10:51.959
+at the time of the exception,
+
+00:10:51.960 --> 00:10:55.659
+but you can't actually resume execution,
+
+00:10:55.660 --> 00:10:57.559
+which you often might want to do,
+
+00:10:57.560 --> 00:10:59.919
+because when you're coding in a dynamic language,
+
+00:10:59.920 --> 00:11:01.479
+you're going to get runtime errors.
+
+00:11:01.480 --> 00:11:04.119
+So if you're writing a script that does like some sort of
+
+00:11:04.120 --> 00:11:07.999
+long-running computation or processes a ton of files
+
+00:11:08.000 --> 00:11:11.939
+and gets an exception parsing one file halfway through,
+
+00:11:11.940 --> 00:11:16.919
+normally you'd have to fix the script, and then rerun it
+
+00:11:16.920 --> 00:11:19.759
+and have it process all the same files all over again,
+
+00:11:19.760 --> 00:11:23.839
+and lose a bunch of time for every bug you run into
+
+00:11:23.840 --> 00:11:24.879
+and fix you have to make.
+
+00:11:24.880 --> 00:11:28.679
+So right now we've got a kind of mediocre workaround
+
+00:11:28.680 --> 00:11:34.019
+which is you can add the restart decorator to a function
+
+00:11:34.020 --> 00:11:37.239
+and then... where in the case of a script
+
+00:11:37.240 --> 00:11:38.879
+processing a bunch of files,
+
+00:11:38.880 --> 00:11:41.799
+you would add the restart decorator to the function
+
+00:11:41.800 --> 00:11:43.599
+that processes a single file.
+
+00:11:43.600 --> 00:11:45.439
+You'd add it to the function
+
+00:11:45.440 --> 00:11:47.879
+that represents kind of the smallest unit of work
+
+00:11:47.880 --> 00:11:50.219
+that might fail with an exception,
+
+00:11:50.220 --> 00:11:54.359
+Then, when you get an exception,
+
+00:11:54.360 --> 00:11:57.479
+you can actually edit the function.
+
+00:11:57.480 --> 00:12:01.019
+Like, if we edit it so it doesn't throw an error,
+
+00:12:01.020 --> 00:12:07.199
+and then we can resume execution,
+
+00:12:07.200 --> 00:12:12.799
+then it will return from foo using the
+
+00:12:12.800 --> 00:12:15.040
+the new version of baz,
+
+00:12:15.041 --> 00:12:18.559
+without having to run the script from the beginning again.
+
+00:12:18.560 --> 00:12:22.379
+So in the example of a script that processes a bunch of files,
+
+00:12:22.380 --> 00:12:24.299
+that would let you,
+
+00:12:24.300 --> 00:12:27.619
+as you run into files that cause an exception,
+
+00:12:27.620 --> 00:12:29.079
+fix your code to deal with it
+
+00:12:29.080 --> 00:12:31.880
+and resume execution without having to restart the script
+
+00:12:31.881 --> 00:12:33.080
+from the beginning.
+
+00:12:33.081 --> 00:12:36.120
+But this is obviously a pretty terrible hack,
+
+00:12:36.121 --> 00:12:38.840
+having to add the restart decorator to the function.
+
+00:12:38.841 --> 00:12:46.739
+I would like it to be able to restart from any function.
+
+00:12:46.740 --> 00:12:49.631
+without needing the decorator, as you can in Common Lisp,
+
+00:12:49.632 --> 00:12:54.031
+but I think that will require patching CPython
+
+00:12:54.032 --> 00:12:56.579
+and I really have no idea how to do that.
+
+00:12:56.580 --> 00:13:00.531
+So if you do know anything about CPython internals
+
+00:13:00.532 --> 00:13:03.720
+and are interested in helping, please reach out.
+
+NOTE pydumpling
+
+00:13:03.721 --> 00:13:07.119
+Another feature we have with the backtrace buffer is
+
+00:13:07.120 --> 00:13:09.079
+there's this library called PyDumpling
+
+00:13:09.080 --> 00:13:14.659
+which can serialize a traceback and store it to a file.
+
+00:13:14.660 --> 00:13:17.859
+So you can use PyDumpling with your applications running in
+
+00:13:17.860 --> 00:13:21.239
+production to serialize a traceback
+
+00:13:21.240 --> 00:13:24.899
+whenever they have an exception and save it to a file.
+
+00:13:24.900 --> 00:13:28.599
+Then you can transfer the file locally
+
+00:13:28.600 --> 00:13:38.859
+and load it into your local Emacs with slime-py-load-pydumpling.
+
+00:13:38.860 --> 00:13:41.839
+This will load the same backtrace buffer,
+
+00:13:41.840 --> 00:13:44.559
+and you see all the same local variables
+
+00:13:44.560 --> 00:13:45.759
+at the time of the exception.
+
+00:13:45.760 --> 00:13:48.199
+You can inspect them and get a REPL
+
+00:13:48.200 --> 00:13:50.999
+in the context of the stack frame.
+
+00:13:51.000 --> 00:13:54.199
+Well, this will only work for variables
+
+00:13:54.200 --> 00:13:57.619
+that can be serialized with pickle.
+
+00:13:57.620 --> 00:13:59.519
+Or actually, the library uses dill,
+
+00:13:59.520 --> 00:14:03.039
+which can serialize a bit more than pickle can.
+
+00:14:03.040 --> 00:14:10.200
+But yeah so this can help you inspect and debug errors
+
+00:14:10.201 --> 00:14:12.880
+for applications running in production remotely
+
+00:14:12.881 --> 00:14:20.059
+that you don't want to have SLIME connected to 24-7.
+
+NOTE Documentation browser
+
+00:14:20.060 --> 00:14:24.859
+Next up, let's look at the documentation browser.
+
+00:14:24.860 --> 00:14:29.919
+We can bring up documentation for any module,
+
+00:14:29.920 --> 00:14:33.079
+and all this information is generated
+
+00:14:33.080 --> 00:14:34.999
+from runtime introspection,
+
+00:14:35.000 --> 00:14:37.079
+from the doc strings for the module
+
+00:14:37.080 --> 00:14:39.159
+and the classes and so on.
+
+00:14:39.160 --> 00:14:41.879
+So you won't see documentation for libraries
+
+00:14:41.880 --> 00:14:43.159
+that you don't have actually loaded
+
+00:14:43.160 --> 00:14:45.939
+into your running Python process.
+
+00:14:45.940 --> 00:14:50.119
+Then you can go browse to classes.
+
+00:14:50.120 --> 00:14:54.719
+It'll show all the attributes, their methods, and so on.
+
+00:14:54.720 --> 00:14:57.239
+By each method to the right, it will show
+
+00:14:57.240 --> 00:15:02.599
+the base class where the method was originally inherited from.
+
+00:15:02.600 --> 00:15:09.079
+You can also bring up a screen with all the Python packages
+
+00:15:09.080 --> 00:15:14.439
+that are installed, and browse that with imenu,
+
+00:15:14.440 --> 00:15:20.359
+and bring up information on any package and so on.
+
+NOTE Thread view
+
+00:15:20.360 --> 00:15:28.499
+Next up, let's take a look at the thread view.
+
+00:15:28.500 --> 00:15:31.839
+So let's run this and then bring up the thread view
+
+00:15:31.840 --> 00:15:35.559
+and this will show information on all running threads.
+
+00:15:35.560 --> 00:15:38.799
+You can configure it to refresh after a given interval,
+
+00:15:38.800 --> 00:15:41.959
+like every second, but I don't have that set up right now,
+
+00:15:41.960 --> 00:15:45.659
+so I have to manually refresh it.
+
+00:15:45.660 --> 00:15:47.639
+Probably the most useful thing is that
+
+00:15:47.640 --> 00:15:49.739
+you can bring up a backtrace for any thread
+
+00:15:49.740 --> 00:15:51.759
+which won't pause the thread or anything,
+
+00:15:51.760 --> 00:15:53.879
+but will just give you the call stack
+
+00:15:53.880 --> 00:15:55.879
+at the time you requested the backtrace.
+
+00:15:55.880 --> 00:15:59.199
+You can again view the stack frames, local variables,
+
+00:15:59.200 --> 00:16:04.139
+open a REPL in the context of the thread, and so on.
+
+00:16:04.140 --> 00:16:07.839
+There's also a viewer for async tasks,
+
+00:16:07.840 --> 00:16:09.999
+but I'm not going to demo that right now,
+
+00:16:10.000 --> 00:16:14.159
+because for that to work, you have to start swanky-python
+
+00:16:14.160 --> 00:16:16.599
+after the async event loop has started,
+
+00:16:16.600 --> 00:16:18.519
+from within the same thread.
+
+00:16:18.520 --> 00:16:20.279
+If you go to the project readme,
+
+00:16:20.280 --> 00:16:23.919
+there's a demo of how to use the async task viewer
+
+00:16:23.920 --> 00:16:27.439
+with a fastapi project.
+
+NOTE Tracing functions
+
+00:16:27.440 --> 00:16:33.879
+Next up, let's look at tracing functions.
+
+00:16:33.880 --> 00:16:36.279
+So here we got some random error,
+
+00:16:36.280 --> 00:16:39.879
+because this is still very much a work in progress.
+
+00:16:39.880 --> 00:16:42.359
+But it looks like it executed
+
+00:16:42.360 --> 00:16:43.199
+correctly this time.
+
+00:16:43.200 --> 00:16:47.565
+So now let's mark the fibonacci function
+
+00:16:47.566 --> 00:16:50.239
+for tracing and execute it.
+
+00:16:50.240 --> 00:16:56.079
+We can see, every time the function is called,
+
+00:16:56.080 --> 00:16:58.239
+all its arguments and return values.
+
+00:16:58.240 --> 00:17:02.899
+Again, there are presentations that we can inspect and so on.
+
+00:17:02.900 --> 00:17:06.079
+But let's inspect a more complex object, like a file object.
+
+00:17:06.080 --> 00:17:11.339
+If we trace the count_lines function and run that code,
+
+00:17:11.340 --> 00:17:15.319
+then we can inspect the file it was passed, or the file object.
+
+00:17:15.320 --> 00:17:21.039
+One pitfall is that in Python, objects are mutable.
+
+00:17:21.040 --> 00:17:25.559
+So in the trace buffer, the string representation
+
+00:17:25.560 --> 00:17:27.879
+that's printed is the string representation
+
+00:17:27.880 --> 00:17:31.219
+at the time it was passed to the function.
+
+00:17:31.220 --> 00:17:32.639
+But when we go to inspect it,
+
+00:17:32.640 --> 00:17:34.919
+we're inspecting the object as it is right now,
+
+00:17:34.920 --> 00:17:37.639
+which can be different than it was at the time
+
+00:17:37.640 --> 00:17:41.559
+the function saw it. So for this file object, for example,
+
+00:17:41.560 --> 00:17:44.279
+it's closed now, when it was open at the time
+
+00:17:44.280 --> 00:17:47.799
+the function used it.
+
+NOTE AI integrations
+
+00:17:47.800 --> 00:17:50.479
+Next up, let's look at AI integrations.
+
+00:17:50.480 --> 00:17:54.519
+So if you're used to SLIME with Common Lisp,
+
+00:17:54.520 --> 00:18:09.479
+Emacs actually has a built-in AI that can help with the transition.
+
+00:18:09.480 --> 00:18:14.559
+So it's just a joke, I actually really like Python.
+
+00:18:14.560 --> 00:18:18.119
+And for more serious AI integrations,
+
+00:18:18.120 --> 00:18:19.959
+I have some ideas for the future
+
+00:18:19.960 --> 00:18:21.919
+but I haven't implemented anything yet.
+
+00:18:21.920 --> 00:18:27.319
+I think right now, people are mostly passing source code to LLMs
+
+00:18:27.320 --> 00:18:32.679
+but since we're embedded in the Python process at runtime,
+
+00:18:32.680 --> 00:18:35.639
+we have a lot of more information available,
+
+00:18:35.640 --> 00:18:39.439
+like maybe we can trace all calls to functions,
+
+00:18:39.440 --> 00:18:41.799
+and when we have a bug,
+
+00:18:41.800 --> 00:18:46.479
+we can feed the trace to the LLM,
+
+00:18:46.480 --> 00:18:48.719
+and the LLM can point out maybe
+
+00:18:48.720 --> 00:18:51.959
+when this function was called with these arguments,
+
+00:18:51.960 --> 00:18:53.879
+its return value doesn't make sense,
+
+00:18:53.880 --> 00:18:55.679
+so maybe that's the root cause of your bug.
+
+00:18:55.680 --> 00:19:02.359
+If you have any ideas of potential LLM or AI integrations,
+
+00:19:02.360 --> 00:19:05.999
+let me know. I'm happy to discuss.
+
+NOTE LSP-type features
+
+00:19:06.000 --> 00:19:09.919
+Next up, let's look at standard LSP-type features.
+
+00:19:09.920 --> 00:19:14.439
+So we've got completions. It's fuzzy completions right now,
+
+00:19:14.440 --> 00:19:16.319
+so it's showing everything with a PR in the name.
+
+00:19:16.320 --> 00:19:21.779
+We can bring up documentation for each one.
+
+00:19:21.780 --> 00:19:26.759
+When we start calling a method in the minibuffer at the bottom
+
+00:19:26.760 --> 00:19:28.859
+it'll show the signature.
+
+00:19:28.860 --> 00:19:33.719
+There's some refactoring available.
+
+00:19:33.720 --> 00:19:37.399
+We can extract a function or variable,
+
+00:19:37.400 --> 00:19:39.499
+or rename something,
+
+00:19:39.500 --> 00:19:42.919
+like, let's rename fib to fib2,
+
+00:19:42.920 --> 00:19:47.479
+and it will rename all the uses of it.
+
+00:19:47.480 --> 00:19:49.759
+All these features are based on Jedi,
+
+00:19:49.760 --> 00:19:55.399
+which is the Python library used by IPython.
+
+00:19:55.400 --> 00:19:56.999
+But as it is right now,
+
+00:19:57.000 --> 00:20:02.039
+if you want the most complete Python development experience
+
+00:20:02.040 --> 00:20:05.579
+in Emacs, I'd probably recommend using LSP
+
+00:20:05.580 --> 00:20:10.439
+for everything LSP can do, and then just using swanky-python
+
+00:20:10.440 --> 00:20:13.679
+for the object inspector and backtrace buffer,
+
+00:20:13.680 --> 00:20:15.359
+and the interactive features it has
+
+00:20:15.360 --> 00:20:18.031
+that an LSP can't provide.
+
+NOTE Wrapping up
+
+00:20:18.032 --> 00:20:23.339
+And that's it really.
+
+00:20:23.340 --> 00:20:25.865
+Shortly we'll have questions and answers
+
+00:20:25.866 --> 00:20:28.799
+as part of EmacsConf, and later on,
+
+00:20:28.800 --> 00:20:31.199
+if you have any questions, ideas, or issues
+
+00:20:31.200 --> 00:20:34.639
+feel free to reach out over email
+
+00:20:34.640 --> 00:20:37.999
+or create an issue on the repository.
+
+00:20:38.000 --> 00:20:39.331
+I should probably warn you,
+
+00:20:39.332 --> 00:20:41.119
+if you want to try out the project:
+
+00:20:41.120 --> 00:20:45.279
+so far I'm probably the only user of it
+
+00:20:45.280 --> 00:20:48.279
+and I've only tested it on my own Emacs setup,
+
+00:20:48.280 --> 00:20:50.839
+so it's quite likely you'll run into issues
+
+00:20:50.840 --> 00:20:53.479
+trying to get it installed and working.
+
+00:20:53.480 --> 00:20:56.119
+But if you do run into problems, please reach out,
+
+00:20:56.120 --> 00:20:59.279
+let me know. I'm happy to help and try and fix them.
+
+00:20:59.280 --> 00:21:03.640
+So that's it. Thanks for listening.
diff --git a/2025/info/swanky-after.md b/2025/info/swanky-after.md
index 9386bdb6..53c84315 100644
--- a/2025/info/swanky-after.md
+++ b/2025/info/swanky-after.md
@@ -1,6 +1,386 @@
<!-- Automatically generated by emacsconf-publish-after-page -->
+<div class="transcript transcript-mainVideo"><a name="swanky-mainVideo-transcript"></a><h1>Transcript</h1>
+
+[[!template text="""Hello everyone, I'm Scott""" start="00:00:00.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and I'll be talking about Swanky Python,""" start="00:00:02.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which is a development environment for Python""" start="00:00:04.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""based on Emacs' Slime package.""" start="00:00:06.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So what is that and why might you find it interesting?""" start="00:00:08.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""SLIME is the Superior Lisp Interaction Mode for Emacs.""" start="00:00:11.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""It's an Emacs package for developing Common Lisp,""" start="00:00:15.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and it's a bit different from the way we develop most languages""" start="00:00:18.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""in that you're always connected""" start="00:00:20.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""to a running instance of your application,""" start="00:00:22.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and you kind of build up your application, piece by piece,""" start="00:00:25.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""modifying one expression at a time""" start="00:00:27.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""without ever having to restart your application.""" start="00:00:30.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So why might you want to develop this way?""" start="00:00:34.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""One advantage is that you can get a faster feedback loop.""" start="00:00:36.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""For some kinds of software, it doesn't make a big difference.""" start="00:00:40.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Like, if you're developing a web backend""" start="00:00:42.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""where all state is stored externally in a database,""" start="00:00:43.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""then you can have a file watcher""" start="00:00:48.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that just restarts the whole Python process""" start="00:00:50.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""whenever you make any edit,""" start="00:00:52.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and you're not really losing anything,""" start="00:00:54.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""because all the state is stored outside the Python process""" start="00:00:56.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""in a database. So it works great.""" start="00:00:59.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But for other kinds of software, like""" start="00:01:01.720" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""let's say you're developing an Emacs package""" start="00:01:03.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""or a video game,""" start="00:01:05.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""then it can be a real pain to restart the application""" start="00:01:07.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and recreate the state it was in before""" start="00:01:10.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""just to test the effect of each edit you want to make.""" start="00:01:12.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Another advantage is the runtime introspection you have available.""" start="00:01:17.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So since you're always connected""" start="00:01:21.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""to a running instance of your application,""" start="00:01:22.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""you can inspect the values of variables,""" start="00:01:25.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""you can trace functions, and all sorts of other information""" start="00:01:27.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""to help you understand your application better.""" start="00:01:30.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""And lastly, it's just a lot of fun to develop this way,""" start="00:01:36.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""or at least I find it fun developing with SLIME,""" start="00:01:39.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""so I wrote a SLIME backend for Python""" start="00:01:43.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""so I could have more fun when I'm coding in Python.""" start="00:01:45.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""As for the name swanky-python, within SLIME,""" start="00:01:48.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""swank is the name of the Common Lisp backend""" start="00:01:52.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that runs within your Common Lisp application""" start="00:01:56.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and connects to Emacs. So I'm not too creative.""" start="00:01:59.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""swanky-python is just a swank implementation in Python.""" start="00:02:02.920" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Demo""" start="00:02:08.000" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""So let's see it in action. So we started up with M-x slime.""" start="00:02:08.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""And what that does is it starts a Python process,""" start="00:02:15.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""starts swanky-python within it, and connects to it from Emacs.""" start="00:02:19.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""And you can configure how exactly it runs Python.""" start="00:02:25.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Or you can start swanky python manually""" start="00:02:29.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""within a Python application running on a remote server""" start="00:02:32.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and forward the port locally""" start="00:02:35.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and connect to it in Emacs, from Emacs remotely.""" start="00:02:36.614" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Within the README, there's more documentation""" start="00:02:40.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""on other ways to start it.""" start="00:02:43.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But just M-x slime is the basic way that works most of the time.""" start="00:02:45.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So within the REPL, the first thing you'll notice is that""" start="00:02:52.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""REPL outputs are clickable buttons,""" start="00:02:55.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""what SLIME calls presentations.""" start="00:02:58.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So you can do things like inspect them.""" start="00:03:02.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""And for each presentation, in the Python backend,""" start="00:03:04.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it holds on to the reference to the object.""" start="00:03:09.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So for an int, it's not too interesting,""" start="00:03:12.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but let's do a more complex object like a file.""" start="00:03:14.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Then we can inspect the file.""" start="00:03:20.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can describe it, which will bring up documentation""" start="00:03:22.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""on that class. We can use it in further expressions""" start="00:03:26.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""like if we copy it, it will use the actual Python object""" start="00:03:33.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""in this expression.""" start="00:03:39.432" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can assign it to a variable.""" start="00:03:43.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""SLIME uses presentations everywhere""" start="00:03:48.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that a Python object would be displayed.""" start="00:03:51.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So instead of just their string representation,""" start="00:03:53.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""when you have a backtrace on an exception,""" start="00:03:56.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""or you... within the inspector or anywhere else really,""" start="00:04:00.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""anywhere that the string representation""" start="00:04:03.966" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""of an object would be displayed,""" start="00:04:06.020" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it displays a presentation that you can go on to""" start="00:04:07.941" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""inspect, reuse, or send to the REPL and so on.""" start="00:04:10.741" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""One useful utility function is pp for print presentation.""" start="00:04:14.961" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We haven't imported it yet.""" start="00:04:23.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So when we get a name error exception""" start="00:04:25.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and SLIME sees that that name is available for import somewhere,""" start="00:04:29.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it'll give us the option of importing it.""" start="00:04:33.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Since it's available for import from multiple modules,""" start="00:04:38.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it'll prompt us for which one we want to import it from.""" start="00:04:40.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We want to import it from swanky-python,""" start="00:04:43.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""not from the standard library.""" start="00:04:45.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Then it will print a presentation of that object.""" start="00:04:48.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Within the REPL, this is not really useful""" start="00:04:52.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""because all REPL outputs are already presentations.""" start="00:04:55.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But I use this now whenever I would use print debugging,""" start="00:04:58.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""just whenever I would use insert print statements in my program""" start="00:05:02.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""to see what's going on, I have it print a presentation""" start="00:05:05.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""because that way I can go back and inspect it later,""" start="00:05:08.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""copy it to the REPL and further manipulate it and so on.""" start="00:05:11.200" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Inspector""" start="00:05:16.600" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Next up, let's look at the inspector more.""" start="00:05:16.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""If we go back and inspect the file object,""" start="00:05:20.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""you can write custom inspector views""" start="00:05:25.580" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""for different kinds of objects.""" start="00:05:27.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So far, I just have a couple. One for sequences,""" start="00:05:28.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""one for mappings, and one for every other kind of object.""" start="00:05:32.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Like if we inspect a mapping, there's a shortcut""" start="00:05:36.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""inspect last result, which is what I normally use""" start="00:05:45.980" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""to open the inspector. Then we see the values,""" start="00:05:48.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and each value in the inspector is a presentation""" start="00:05:52.380" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that we can go on to inspect, and so on.""" start="00:05:56.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Let's go back to inspecting the file object.""" start="00:05:58.420" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Again, we can inspect each of the values,""" start="00:06:03.980" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""we can copy them back to the REPL and so on.""" start="00:06:06.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""It just displays all the attributes for the class""" start="00:06:10.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and their values.""" start="00:06:13.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can configure what attributes we want to show.""" start="00:06:15.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""There's a transient menu where we can toggle""" start="00:06:18.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""if we want to show private attributes, dunder attributes,""" start="00:06:21.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""doc strings, so on, or everything,""" start="00:06:23.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which is a bit much to show by default.""" start="00:06:26.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So we'll reset it to the default.""" start="00:06:28.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""In the future, I want to add graphical inspector views""" start="00:06:33.720" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""for different kinds of objects, and also support""" start="00:06:37.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""showing plots in both the inspector and the REPL,""" start="00:06:40.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but that's future work I haven't started on yet.""" start="00:06:43.000" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Evaluating Python""" start="00:06:47.720" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Let's look at the different options for evaluating Python.""" start="00:06:47.720" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So we can evaluate a whole file.""" start="00:06:52.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can evaluate just a class.""" start="00:06:59.100" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can evaluate just the method we're working on.""" start="00:07:00.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can evaluate a Python statement,""" start="00:07:03.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and it will show the result in an overlay next to the cursor.""" start="00:07:06.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can select some code and just evaluate the highlighted region.""" start="00:07:11.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can sync the REPL to the active file.""" start="00:07:17.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So now everything we evaluate in the REPL will be in the""" start="00:07:24.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""context of the eval_demo module.""" start="00:07:27.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can also set the module that the REPL is in.""" start="00:07:29.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can go back to main.""" start="00:07:35.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But let's go back to the eval_demo module for now.""" start="00:07:38.280" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Updating""" start="00:07:43.680" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""One useful thing is when you update a class or a function,""" start="00:07:43.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it updates old instances of that class or function.""" start="00:07:49.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So right now, f.bar is foobar.""" start="00:07:54.540" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But if we edit that class, it will actually edit the code""" start="00:07:58.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""for the old instance of that class.""" start="00:08:03.720" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""And that's provided by code I copied""" start="00:08:05.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""from IPython's autoreload extension.""" start="00:08:07.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""It helps when you're trying to develop in Python""" start="00:08:12.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""without having to restart the Python process""" start="00:08:14.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""whenever you make a change.""" start="00:08:16.499" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Auto reload in Python is a big topic""" start="00:08:20.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that I don't really have time to go into here,""" start="00:08:22.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but right now it is more limited""" start="00:08:26.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""than what is done in Common Lisp.""" start="00:08:29.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Like for example, if you have a data class in Python""" start="00:08:32.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and you add a new field to the data class,""" start="00:08:35.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it won't automatically update old instances""" start="00:08:37.620" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""of the data class with a new field.""" start="00:08:41.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So there's more that needs to be done with that,""" start="00:08:43.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but I am perhaps naively optimistic""" start="00:08:46.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that Python's runtime is quite dynamic and flexible,""" start="00:08:50.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and that I can fully implement autoreload in Python,""" start="00:08:54.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but there's still work to be done,""" start="00:08:59.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and it's a big topic to go into.""" start="00:09:02.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Next up, let's look at the backtrace buffer.""" start="00:09:05.420" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But as it is right now, autoreload is actually useful.""" start="00:09:08.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""I mostly develop in Python without having to restart the process""" start="00:09:12.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and without running into issues from old state""" start="00:09:16.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that hasn't been updated properly.""" start="00:09:19.600" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Backtraces""" start="00:09:22.900" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""So if we go on to look at the backtrace buffer,""" start="00:09:22.900" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""whenever we get an exception in Python...""" start="00:09:26.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Let's go back to it.""" start="00:09:32.820" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Whenever we get an exception, it will...""" start="00:09:37.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""let's change the code so that it actually""" start="00:09:41.420" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""gets an exception...""" start="00:09:43.699" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""we will get an interactive backtrace buffer""" start="00:09:49.966" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""where we can browse the source code for the different stack frames""" start="00:09:52.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and the local variables within the stack frames,""" start="00:09:57.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which are all presentations that we can inspect and so on.""" start="00:10:00.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can also open a REPL in the context of any stack frame.""" start="00:10:04.340" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Or we can, when we go to the source for a given stack frame,""" start="00:10:10.620" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""we can select some Python code and evaluate it""" start="00:10:16.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""within the context of that stack frame.""" start="00:10:20.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""One major limitation compared to SLIME for Common Lisp""" start="00:10:25.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""is that in Common Lisp, you have the option to""" start="00:10:30.700" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""restart or resume execution from a given stack frame""" start="00:10:33.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""after an exception happens, where in Python,""" start="00:10:38.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""what we have right now is pretty much equivalent to""" start="00:10:42.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""the postmortem debugger.""" start="00:10:45.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""You can view the state that the call stack was in""" start="00:10:47.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""at the time of the exception,""" start="00:10:50.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but you can't actually resume execution,""" start="00:10:51.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which you often might want to do,""" start="00:10:55.660" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""because when you're coding in a dynamic language,""" start="00:10:57.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""you're going to get runtime errors.""" start="00:10:59.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So if you're writing a script that does like some sort of""" start="00:11:01.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""long-running computation or processes a ton of files""" start="00:11:04.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and gets an exception parsing one file halfway through,""" start="00:11:08.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""normally you'd have to fix the script, and then rerun it""" start="00:11:11.940" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and have it process all the same files all over again,""" start="00:11:16.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and lose a bunch of time for every bug you run into""" start="00:11:19.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and fix you have to make.""" start="00:11:23.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So right now we've got a kind of mediocre workaround""" start="00:11:24.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which is you can add the restart decorator to a function""" start="00:11:28.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and then... where in the case of a script""" start="00:11:34.020" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""processing a bunch of files,""" start="00:11:37.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""you would add the restart decorator to the function""" start="00:11:38.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that processes a single file.""" start="00:11:41.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""You'd add it to the function""" start="00:11:43.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that represents kind of the smallest unit of work""" start="00:11:45.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that might fail with an exception,""" start="00:11:47.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Then, when you get an exception,""" start="00:11:50.220" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""you can actually edit the function.""" start="00:11:54.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Like, if we edit it so it doesn't throw an error,""" start="00:11:57.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and then we can resume execution,""" start="00:12:01.020" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""then it will return from foo using the""" start="00:12:07.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""the new version of baz,""" start="00:12:12.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""without having to run the script from the beginning again.""" start="00:12:15.041" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So in the example of a script that processes a bunch of files,""" start="00:12:18.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that would let you,""" start="00:12:22.380" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""as you run into files that cause an exception,""" start="00:12:24.300" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""fix your code to deal with it""" start="00:12:27.620" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and resume execution without having to restart the script""" start="00:12:29.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""from the beginning.""" start="00:12:31.881" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But this is obviously a pretty terrible hack,""" start="00:12:33.081" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""having to add the restart decorator to the function.""" start="00:12:36.121" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""I would like it to be able to restart from any function.""" start="00:12:38.841" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""without needing the decorator, as you can in Common Lisp,""" start="00:12:46.740" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but I think that will require patching CPython""" start="00:12:49.632" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and I really have no idea how to do that.""" start="00:12:54.032" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So if you do know anything about CPython internals""" start="00:12:56.580" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and are interested in helping, please reach out.""" start="00:13:00.532" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""pydumpling""" start="00:13:03.721" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Another feature we have with the backtrace buffer is""" start="00:13:03.721" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""there's this library called PyDumpling""" start="00:13:07.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which can serialize a traceback and store it to a file.""" start="00:13:09.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So you can use PyDumpling with your applications running in""" start="00:13:14.660" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""production to serialize a traceback""" start="00:13:17.860" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""whenever they have an exception and save it to a file.""" start="00:13:21.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Then you can transfer the file locally""" start="00:13:24.900" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and load it into your local Emacs with slime-py-load-pydumpling.""" start="00:13:28.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""This will load the same backtrace buffer,""" start="00:13:38.860" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and you see all the same local variables""" start="00:13:41.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""at the time of the exception.""" start="00:13:44.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""You can inspect them and get a REPL""" start="00:13:45.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""in the context of the stack frame.""" start="00:13:48.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Well, this will only work for variables""" start="00:13:51.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that can be serialized with pickle.""" start="00:13:54.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Or actually, the library uses dill,""" start="00:13:57.620" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which can serialize a bit more than pickle can.""" start="00:13:59.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But yeah so this can help you inspect and debug errors""" start="00:14:03.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""for applications running in production remotely""" start="00:14:10.201" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that you don't want to have SLIME connected to 24-7.""" start="00:14:12.881" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Documentation browser""" start="00:14:20.060" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Next up, let's look at the documentation browser.""" start="00:14:20.060" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can bring up documentation for any module,""" start="00:14:24.860" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and all this information is generated""" start="00:14:29.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""from runtime introspection,""" start="00:14:33.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""from the doc strings for the module""" start="00:14:35.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and the classes and so on.""" start="00:14:37.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So you won't see documentation for libraries""" start="00:14:39.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that you don't have actually loaded""" start="00:14:41.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""into your running Python process.""" start="00:14:43.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Then you can go browse to classes.""" start="00:14:45.940" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""It'll show all the attributes, their methods, and so on.""" start="00:14:50.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""By each method to the right, it will show""" start="00:14:54.720" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""the base class where the method was originally inherited from.""" start="00:14:57.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""You can also bring up a screen with all the Python packages""" start="00:15:02.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that are installed, and browse that with imenu,""" start="00:15:09.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and bring up information on any package and so on.""" start="00:15:14.440" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Thread view""" start="00:15:20.360" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Next up, let's take a look at the thread view.""" start="00:15:20.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So let's run this and then bring up the thread view""" start="00:15:28.500" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and this will show information on all running threads.""" start="00:15:31.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""You can configure it to refresh after a given interval,""" start="00:15:35.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""like every second, but I don't have that set up right now,""" start="00:15:38.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""so I have to manually refresh it.""" start="00:15:41.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Probably the most useful thing is that""" start="00:15:45.660" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""you can bring up a backtrace for any thread""" start="00:15:47.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which won't pause the thread or anything,""" start="00:15:49.740" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but will just give you the call stack""" start="00:15:51.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""at the time you requested the backtrace.""" start="00:15:53.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""You can again view the stack frames, local variables,""" start="00:15:55.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""open a REPL in the context of the thread, and so on.""" start="00:15:59.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""There's also a viewer for async tasks,""" start="00:16:04.140" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but I'm not going to demo that right now,""" start="00:16:07.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""because for that to work, you have to start swanky-python""" start="00:16:10.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""after the async event loop has started,""" start="00:16:14.160" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""from within the same thread.""" start="00:16:16.600" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""If you go to the project readme,""" start="00:16:18.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""there's a demo of how to use the async task viewer""" start="00:16:20.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""with a fastapi project.""" start="00:16:23.920" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Tracing functions""" start="00:16:27.440" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Next up, let's look at tracing functions.""" start="00:16:27.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So here we got some random error,""" start="00:16:33.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""because this is still very much a work in progress.""" start="00:16:36.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But it looks like it executed""" start="00:16:39.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""correctly this time.""" start="00:16:42.360" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So now let's mark the fibonacci function""" start="00:16:43.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""for tracing and execute it.""" start="00:16:47.566" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can see, every time the function is called,""" start="00:16:50.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""all its arguments and return values.""" start="00:16:56.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Again, there are presentations that we can inspect and so on.""" start="00:16:58.240" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But let's inspect a more complex object, like a file object.""" start="00:17:02.900" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""If we trace the count_lines function and run that code,""" start="00:17:06.080" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""then we can inspect the file it was passed, or the file object.""" start="00:17:11.340" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""One pitfall is that in Python, objects are mutable.""" start="00:17:15.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So in the trace buffer, the string representation""" start="00:17:21.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that's printed is the string representation""" start="00:17:25.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""at the time it was passed to the function.""" start="00:17:27.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But when we go to inspect it,""" start="00:17:31.220" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""we're inspecting the object as it is right now,""" start="00:17:32.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which can be different than it was at the time""" start="00:17:34.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""the function saw it. So for this file object, for example,""" start="00:17:37.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it's closed now, when it was open at the time""" start="00:17:41.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""the function used it.""" start="00:17:44.280" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""AI integrations""" start="00:17:47.800" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Next up, let's look at AI integrations.""" start="00:17:47.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So if you're used to SLIME with Common Lisp,""" start="00:17:50.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Emacs actually has a built-in AI that can help with the transition.""" start="00:17:54.520" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So it's just a joke, I actually really like Python.""" start="00:18:09.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""And for more serious AI integrations,""" start="00:18:14.560" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""I have some ideas for the future""" start="00:18:18.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but I haven't implemented anything yet.""" start="00:18:19.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""I think right now, people are mostly passing source code to LLMs""" start="00:18:21.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""but since we're embedded in the Python process at runtime,""" start="00:18:27.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""we have a lot of more information available,""" start="00:18:32.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""like maybe we can trace all calls to functions,""" start="00:18:35.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and when we have a bug,""" start="00:18:39.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""we can feed the trace to the LLM,""" start="00:18:41.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and the LLM can point out maybe""" start="00:18:46.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""when this function was called with these arguments,""" start="00:18:48.720" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""its return value doesn't make sense,""" start="00:18:51.960" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""so maybe that's the root cause of your bug.""" start="00:18:53.880" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""If you have any ideas of potential LLM or AI integrations,""" start="00:18:55.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""let me know. I'm happy to discuss.""" start="00:19:02.360" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""LSP-type features""" start="00:19:06.000" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""Next up, let's look at standard LSP-type features.""" start="00:19:06.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So we've got completions. It's fuzzy completions right now,""" start="00:19:09.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""so it's showing everything with a PR in the name.""" start="00:19:14.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can bring up documentation for each one.""" start="00:19:16.320" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""When we start calling a method in the minibuffer at the bottom""" start="00:19:21.780" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""it'll show the signature.""" start="00:19:26.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""There's some refactoring available.""" start="00:19:28.860" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""We can extract a function or variable,""" start="00:19:33.720" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""or rename something,""" start="00:19:37.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""like, let's rename fib to fib2,""" start="00:19:39.500" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and it will rename all the uses of it.""" start="00:19:42.920" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""All these features are based on Jedi,""" start="00:19:47.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""which is the Python library used by IPython.""" start="00:19:49.760" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But as it is right now,""" start="00:19:55.400" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""if you want the most complete Python development experience""" start="00:19:57.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""in Emacs, I'd probably recommend using LSP""" start="00:20:02.040" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""for everything LSP can do, and then just using swanky-python""" start="00:20:05.580" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""for the object inspector and backtrace buffer,""" start="00:20:10.440" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and the interactive features it has""" start="00:20:13.680" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""that an LSP can't provide.""" start="00:20:15.360" video="mainVideo-swanky" id="subtitle"]]
+
+<div class="transcript-heading">[[!template new="1" text="""Wrapping up""" start="00:20:18.032" video="mainVideo-swanky" id="subtitle"]]</div>[[!template text="""And that's it really.""" start="00:20:18.032" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""Shortly we'll have questions and answers""" start="00:20:23.340" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""as part of EmacsConf, and later on,""" start="00:20:25.866" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""if you have any questions, ideas, or issues""" start="00:20:28.800" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""feel free to reach out over email""" start="00:20:31.200" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""or create an issue on the repository.""" start="00:20:34.640" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""I should probably warn you,""" start="00:20:38.000" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""if you want to try out the project:""" start="00:20:39.332" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""so far I'm probably the only user of it""" start="00:20:41.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""and I've only tested it on my own Emacs setup,""" start="00:20:45.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""so it's quite likely you'll run into issues""" start="00:20:48.280" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""trying to get it installed and working.""" start="00:20:50.840" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""But if you do run into problems, please reach out,""" start="00:20:53.480" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""let me know. I'm happy to help and try and fix them.""" start="00:20:56.120" video="mainVideo-swanky" id="subtitle"]]
+[[!template text="""So that's it. Thanks for listening.""" start="00:20:59.280" video="mainVideo-swanky" id="subtitle"]]
+
+</div>
+
+Captioner: sachac
+
Questions or comments? Please e-mail [sczi@disroot.org](mailto:sczi@disroot.org?subject=Comment%20for%20EmacsConf%202023%20swanky%3A%20Swanky%20Python%3A%20Interactive%20development%20for%20Python)
diff --git a/2025/info/swanky-before.md b/2025/info/swanky-before.md
index c3be7dd6..96c4efe0 100644
--- a/2025/info/swanky-before.md
+++ b/2025/info/swanky-before.md
@@ -8,12 +8,12 @@ The following image shows where the talk is in the schedule for Sat 2025-12-06.
Format: 22-min talk ; Q&A: ask questions via Etherpad/IRC; we'll e-mail the speaker and post answers on this wiki page after the conference Etherpad: <https://pad.emacsconf.org/2025-swanky>
Etherpad: <https://pad.emacsconf.org/2025-swanky>
Discuss on IRC: [#emacsconf-dev](https://chat.emacsconf.org/?join=emacsconf,emacsconf-dev)
-Status: Ready to stream
+Status: Now playing on the conference livestream
<div>Times in different time zones:</div><div class="times" start="2025-12-06T15:45:00Z" end="2025-12-06T16:10:00Z"><div class="conf-time">Saturday, Dec 6 2025, ~10:45 AM - 11:10 AM EST (US/Eastern)</div><div class="others"><div>which is the same as:</div>Saturday, Dec 6 2025, ~9:45 AM - 10:10 AM CST (US/Central)<br />Saturday, Dec 6 2025, ~8:45 AM - 9:10 AM MST (US/Mountain)<br />Saturday, Dec 6 2025, ~7:45 AM - 8:10 AM PST (US/Pacific)<br />Saturday, Dec 6 2025, ~3:45 PM - 4:10 PM UTC <br />Saturday, Dec 6 2025, ~4:45 PM - 5:10 PM CET (Europe/Paris)<br />Saturday, Dec 6 2025, ~5:45 PM - 6:10 PM EET (Europe/Athens)<br />Saturday, Dec 6 2025, ~9:15 PM - 9:40 PM IST (Asia/Kolkata)<br />Saturday, Dec 6 2025, ~11:45 PM - 12:10 AM +08 (Asia/Singapore)<br />Sunday, Dec 7 2025, ~12:45 AM - 1:10 AM JST (Asia/Tokyo)</div></div><div><strong><a href="/2025/watch/dev/">Find out how to watch and participate</a></strong></div>
-
+<div class="vid mainVideo"><video controls preload="none" id="mainVideo-swanky"><source src="https://media.emacsconf.org/2025/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.webm" />captions="""<track label="English" kind="captions" srclang="en" src="/2025/captions/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.vtt" default />"""<p><em>Your browser does not support the video tag. Please download the video instead.</em></p></video><div></div>Duration: 21:03 minutes<div class="files resources"><ul><li><a href="https://pad.emacsconf.org/2025-swanky">Open Etherpad</a></li><li><a href="https://media.emacsconf.org/2025/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--intro.vtt">Download --intro.vtt</a></li><li><a href="https://media.emacsconf.org/2025/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--intro.webm">Download --intro.webm</a></li><li><a href="https://media.emacsconf.org/2025/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.opus">Download --main.opus (18MB)</a></li><li><a href="https://media.emacsconf.org/2025/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.vtt">Download --main.vtt</a></li><li><a href="https://media.emacsconf.org/2025/emacsconf-2025-swanky--swanky-python-interactive-development-for-python--scott-zimmermann--main.webm">Download --main.webm (56MB)</a></li><li><a href="https://youtu.be/fy0ofT1I54U">View on Youtube</a></li></ul></div></div>
# Description
<!-- End of emacsconf-publish-before-page --> \ No newline at end of file