diff options
| author | EmacsConf <emacsconf-org@gnu.org> | 2025-12-06 10:45:37 -0500 |
|---|---|---|
| committer | EmacsConf <emacsconf-org@gnu.org> | 2025-12-06 10:45:37 -0500 |
| commit | 8b4b8108e1ae24c3b2a976ddfa4a6779c10f0f34 (patch) | |
| tree | a3ccc508c0cc55b029c9ee8e5012b703ab637cb2 | |
| parent | b987d21df6fb0ce2ccf24a501824e900e320a350 (diff) | |
| download | emacsconf-wiki-8b4b8108e1ae24c3b2a976ddfa4a6779c10f0f34.tar.xz emacsconf-wiki-8b4b8108e1ae24c3b2a976ddfa4a6779c10f0f34.zip | |
Automated commit
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 |
