back to indexThe Fastlite DB library - Answer.AI dev chat #2
00:00:00.000 |
OK, welcome to our dev chat with Jono, Alexis, Luke. 00:00:12.280 |
that I just created over the weekend called Fastlight. 00:00:24.480 |
I think it just took me a day to write this, which 00:00:29.760 |
and it's really because it's just pasting together 00:00:49.320 |
at the moment to make web application programming easy 00:00:53.720 |
again, like put a PHP file in your home directory kind 00:00:58.680 |
of easy, or stick a .pl file in your CGI bin directory 00:01:11.160 |
where do we stick our data for our web application? 00:01:19.000 |
are SQLAlchemy, which is kind of the original ORM, 00:01:24.560 |
Object Relational Mapping, and the very trendy SQLModel 00:01:35.240 |
from Sebastian, the amazing author of FastAPI, 00:01:40.320 |
And yeah, because I think Sebastian's work tends 00:01:50.600 |
And I went through the kind of documentation, 00:01:56.080 |
And I kind of very quickly early on got to this bit 00:02:15.960 |
I'm not sure I'm clever enough to remember or do all that. 00:02:19.440 |
I think I'm probably too lazy to do it anyway. 00:02:32.520 |
forget how to use it, which is never a good sign. 00:02:39.080 |
And again, I kind of started looking at their quick start. 00:02:48.720 |
And magic names in them and both annotations and defaults. 00:02:57.560 |
And again, I was like, I don't think I can do all that. 00:03:15.600 |
You can kind of see them all there and move things around 00:03:21.240 |
I started to link these two together and understand, 00:03:31.720 |
Like, SQLAlchemy also has this SQLAlchemyCore piece, 00:03:38.440 |
which is part of this unified tutorial, which 00:03:46.120 |
is this foundational Alchemy database toolkit, whatever. 00:03:52.320 |
And I also got an O'Reilly book about SQLAlchemy and read that. 00:03:57.080 |
I still felt not smart enough to do that either. 00:04:07.320 |
and remembered that my friend Simon Willison had created 00:04:17.600 |
a thing called SQLiteUtils, which I remembered 00:04:21.600 |
was this command line application for using SQLite 00:04:41.800 |
looking at Simon's LLM library, one of the things 00:04:47.720 |
we discovered was that, like, actually the CLI was just 00:04:51.560 |
a kind of a thin wrapper for a really nicely designed Python 00:05:14.160 |
Database, name of file, table, insert, a list of dictionaries, 00:05:37.120 |
Do you guys have any thoughts about should I be-- 00:05:43.480 |
Or does this seem like a reasonable place to start? 00:05:51.920 |
And it's been mixed for me where I see something and say, 00:05:58.960 |
And then I either write or find a thing that looks simpler. 00:06:01.880 |
And then you hit a point where it's like, oh, 00:06:03.880 |
it would be really good if it did this or that. 00:06:08.640 |
And you end up with something that ends up just 00:06:12.280 |
But I'm assuming, because you're showing us this thing 00:06:14.480 |
and you're happy with it, that we're hopefully 00:06:16.400 |
not going to end up back at the convoluted Jeremy's 00:06:31.720 |
of a whole area of math called relational algebra 00:06:33.960 |
to be especially around dealing with relational data. 00:06:38.560 |
So I don't think you could ever hit a point where it's like, 00:06:45.040 |
When I say just use SQL, for somebody who doesn't know SQL, 00:06:52.480 |
It feels like in the past, when I did a lot of stuff 00:07:01.120 |
We were always talking about SQL versus ORMs. 00:07:17.640 |
ORMs tend to be quite a good fit for more static languages, 00:07:25.800 |
F# had this really cool thing called type providers, 00:07:39.480 |
I'm pretty sure you'd never get to a situation where it's like, 00:07:42.100 |
oh, we're going to have to add a whole lot of complexity. 00:07:44.200 |
You might get to a situation where it's like, 00:07:48.080 |
to make some of this SQL a bit easier to work with. 00:07:51.400 |
But even there, I feel like the way we do SQL is like-- 00:08:14.160 |
when something doesn't require you to learn a lot to use it. 00:08:17.520 |
So if it doesn't require you to learn its own concepts, 00:08:32.160 |
So presumably, if it wasn't doing what he needed it to do, 00:08:48.720 |
And so when I spoke to Sebastian, who writes SQL Model 00:09:04.040 |
that a big driver of how he writes things and what he writes 00:09:11.920 |
And I think this is actually often what's going on here, 00:09:14.560 |
is why not just use a create table statement? 00:09:29.760 |
because otherwise it doesn't know what fields are in a user 00:09:38.280 |
but also because this is working with identic, 00:09:44.760 |
to make sure at each time you're validating correctly. 00:09:50.880 |
And I guess if I was using a static language, 00:10:02.480 |
That's kind of why I use Python to a large degree. 00:10:23.680 |
He's got this talk called "Stop Drawing Dead Fish." 00:10:30.600 |
A live environment is something where you're directly 00:10:46.000 |
the rest of the notebook, it's a live environment, right? 00:10:56.240 |
so I, from sqliteutils, I've imported database. 00:11:02.360 |
And then this database, Chinook, is something you can download. 00:11:06.440 |
I've got it in the readme, how to download it. 00:11:13.800 |
if I hit Tab, I know these are the actual things in DB 00:11:30.320 |
it doesn't show me because they're considered private. 00:11:41.040 |
But having said that, I can do like this to see the tables. 00:12:10.720 |
designed to take advantage of Python's dynamic features. 00:12:16.080 |
So I can't hit Tab here or anything and see the tables. 00:12:20.760 |
So I have to copy and paste or go to tables, find it, 00:12:25.360 |
double-click it, come back, do this, et cetera. 00:12:34.040 |
So yeah, basically, the main thing I wanted to do 00:12:37.600 |
is make it so I can use this as easily as I can use files 00:12:43.240 |
and folders in Bash or ZSH by hitting Tab and seeing 00:12:48.720 |
So I'm going to show you what it looks like to use it. 00:13:08.760 |
this is the-- so because this is an nbdev project, 00:13:27.120 |
You can click up here to get to the documentation. 00:13:30.600 |
And the documentation is the same as the index.ipamd, 00:13:48.040 |
that in Jupyter or Colab or whatever and try it. 00:14:03.520 |
SQLAlchemy has a C property, which is similar. 00:14:05.960 |
So I'm going to try to borrow some ideas from SQLAlchemy 00:14:42.880 |
you're working in a notebook is to make sure things 00:14:46.920 |
So when I show the database tables object, which 00:14:50.520 |
I remember is the thing I've got this tab completion on it, 00:15:09.680 |
you can grab multiple tables by using an indexer, which 00:15:16.280 |
And yeah, as mentioned, we can also do our tab completion. 00:15:39.880 |
once you've got a table, which remember we got by doing this. 00:15:44.280 |
So from the database tables, we got the artist table. 00:15:50.680 |
And so you won't be surprised to hear that, first of all, 00:16:19.600 |
that has things like primary keys and various methods 00:16:32.360 |
In SQLite Utils, so the only thing a column basically 00:17:01.800 |
So since my thinking was like, yeah, let's just use SQL, 00:17:13.160 |
If I do an fstring, then I've got tab completion 00:17:27.760 |
So in this case, I've already got it in a variable. 00:17:44.560 |
All it's doing, if I say-- if I stringify that column name, 00:18:16.520 |
It's just giving you this convenient tab completion 00:18:27.520 |
So the reason that you were able to get a tab completion 00:18:30.600 |
on the first field, artist with a lowercase a, 00:18:35.640 |
defined that variable in the notebook environment, right? 00:18:41.280 |
So the things that get tab completed in Jupyter 00:19:00.560 |
so if I go g equals globals, and then I look up, 00:19:14.160 |
So I should find that there's a thing called open, for instance. 00:19:23.920 |
And that would be exactly the same as saying globals open. 00:19:31.200 |
this is what Python's syntax sugar is, you know? 00:19:37.960 |
You could write it with no bare words except the word globals, 00:19:45.920 |
So if you, say, were manipulating two SQLite databases, 00:19:50.320 |
and then you were still just authoring that query string, 00:19:55.680 |
was scoped to the tables in this database versus that database, 00:19:59.920 |
then you'd need to do something like db1.tables. 00:20:07.200 |
So you'd have db1 and db2, and you'd have db.t.blah. 00:20:15.160 |
have said, like, visual artists equals db visual.t.artist, 00:20:27.840 |
or whatever, and then there'd be other artists. 00:20:30.560 |
And yeah, so you can name them whatever you like. 00:20:39.120 |
that happens to contain a symbol of type db.table. 00:21:02.520 |
returns the result of executing that query on that database. 00:21:16.680 |
So this particular database doesn't come with any views. 00:21:22.120 |
which is all of the albums in the database created by ACDC, 00:21:44.000 |
And if you want to, you could have a look at that. 00:22:09.680 |
I don't know how common this is, if it's just a SQLite thing, 00:22:12.240 |
but this is just a shortcut for saying onAlbum.artistId 00:22:17.840 |
It's just a convenient way of doing an inner join when 00:22:23.720 |
Anyway, the main thing there is to say we created a view. 00:22:35.240 |
There's only one thing in db.v, because there's just one view. 00:22:53.680 |
I don't really understand a database until I see it. 00:22:57.840 |
And so you can see here this Chinook database 00:23:25.160 |
I don't know, which way around does it make sense for the-- 00:23:29.240 |
or should the primary key point to the many side? 00:23:38.400 |
They had to do one indicators and many indicators. 00:23:54.520 |
A track is-- could be a number of tracks in an invoice line. 00:24:22.400 |
And so we can combine that with that convenient indexing thing 00:24:42.920 |
as I try to build up my web application from a database. 00:25:01.480 |
write that are entirely around some piece of functionality 00:25:04.840 |
I want to create this type of file from that type of file. 00:25:07.600 |
And then the user interface is like a single command line 00:25:16.920 |
I haven't got any functionality, except the diagram. 00:25:27.160 |
for the command line, which is where he likes to work. 00:25:55.880 |
It's just that it's very optimized for the command line. 00:26:07.480 |
or translation of that to the type of Jupyter interactive 00:26:14.440 |
And Sebastien's like SQL Model and SQL Acme, in particular, 00:26:18.080 |
really focused on getting autocomplete and stuff 00:26:31.640 |
But it does mean that you have to-- at some point, 00:26:40.400 |
have some process that creates the tables from the class. 00:26:45.880 |
Or you have to have something that kind of codegens class 00:26:49.000 |
files that VS Code can read from the database. 00:26:55.040 |
But yeah, I like this kind of dynamic approach 00:27:08.200 |
And I don't have to learn the particular quirks, quirks 00:27:17.960 |
Yeah, that's why I don't really have to learn that. 00:27:27.960 |
to have this database interaction in the notebook. 00:27:33.160 |
or at least I have friends who work on companies 00:27:35.320 |
where you sit and write down the database on paper 00:27:42.480 |
is the types of data that we're committed to using. 00:27:49.960 |
And then my code would now use this static definition. 00:27:55.600 |
And then I'd want to follow that very rigidly. 00:27:57.800 |
And so then it really makes sense to have, OK, 00:28:03.600 |
or maybe it's in something that spits out SQL. 00:28:05.840 |
But then also I have a way to interact with that 00:28:07.800 |
for my code that's very known in advance and defined. 00:28:16.720 |
But also a lot of DBAs do it a very different way, which 00:28:26.480 |
And they tend to have auto-completion of column names 00:28:34.280 |
SQL was supposed to be human-friendly and interactive. 00:28:42.000 |
like it to be because of language design failures 00:28:54.800 |
The thought is, I think, that this interface that you've 00:28:58.880 |
presented has the same merits as the library assignments 00:29:03.760 |
that you're building on top of, which is it doesn't require you 00:29:09.600 |
Simon's thing just requires you to know about Python dictionaries 00:29:14.400 |
rather than learn a bunch of maybe cockamamie new things 00:29:23.200 |
And here, you're working with the autocomplete that's 00:29:34.280 |
It'll also work in pretty much any other interactive 00:29:37.740 |
Because you're saying, well, this is good for notebooks. 00:29:43.900 |
the Python prompt in Emacs where there's tab completion 00:29:53.400 |
Yeah, so let me show you how it's implemented. 00:29:56.860 |
I'm kind of curious how this works because you've 00:29:58.980 |
emphasized, well, you'd need to use all these static types 00:30:03.740 |
getting autocomplete without building a static type 00:30:13.660 |
I know a lot of your background has been Swift, 00:30:17.940 |
which is a static, largely a static language. 00:30:23.900 |
And it was created originally as a static language. 00:30:32.180 |
People are trying to turn it into a static language. 00:30:34.380 |
It's almost as if people feel like they're not 00:30:37.460 |
unless they have strong typing and all this stuff. 00:30:41.860 |
But actually, as you'll see, as we dig into how this works, 00:30:46.020 |
Python is very much designed to have a lot of flexibility 00:30:50.100 |
for the developer to provide dynamic behavior that 00:30:55.700 |
So in fact, if we look at the actual Python file that 00:31:13.300 |
if you think about how many blank lines there are, 00:31:27.700 |
So in practice, it might be more like 80 lines of code or less. 00:31:31.980 |
So yeah, it really is just using the functionality 00:31:37.860 |
that Python provides, that Guido created when he created Python. 00:31:43.220 |
And this is functionality that's been in Python 00:31:47.420 |
And it goes back to this basic idea of Python 00:31:49.820 |
is this very small kernel, which everything else is 00:32:02.460 |
the implementation, the source code of Fastlight 00:32:12.860 |
used Simon's thing before, I was trying to use Simon's thing. 00:32:18.660 |
And each time I found myself not having access 00:32:43.900 |
Now tables getter derives from getter plus one thing. 00:32:49.460 |
So it might be easier to pretend that this dir was actually 00:32:57.380 |
over here, and that this thing was called tables getter. 00:33:01.540 |
That's the same thing as what the inheritance does, right? 00:33:08.380 |
So remembering that basically this can be thought of as all 00:33:12.100 |
one thing, because that's how inheritance works, 00:33:17.220 |
what happens when we just write dt on its own? 00:33:21.620 |
How did it end up with this comma separated list of tables? 00:33:38.580 |
And what is displayed in a notebook, or in IPython, 00:33:42.300 |
or in lots and lots and lots of places in Python 00:33:44.540 |
and things around that, is defined by this dunder repra. 00:33:50.380 |
So something with two underscores on either side 00:33:55.540 |
will be called by Python or some other thing in some situation 00:34:00.380 |
So this is the thing that will get called-- specifically, 00:34:03.700 |
it's called when this function called repra is called. 00:34:09.020 |
And that function called repra is called by Python a lot. 00:34:13.100 |
And it's called by Jupyter any time something is displayed. 00:34:17.980 |
So when we say dt, it actually calls dunder repra. 00:34:30.100 |
So obviously, that means we want to know what is der dt. 00:34:35.300 |
And somehow, that's become a list of the things 00:34:39.460 |
Der is used, again, in lots of places in Python. 00:34:44.020 |
And specifically, any time you do an autocomplete, 00:34:55.380 |
So you'll see that is der is exactly the same as me going 00:35:36.380 |
Well, when you square bracket index into something in Python, 00:35:40.300 |
it calls this special thing, dunder get item. 00:35:44.660 |
So dunder get item, if you pass a single thing, 00:36:02.300 |
So if you passed in a single thing, for simplicity, 00:36:05.460 |
I'll just make it a list with one thing in it. 00:36:10.860 |
that table for each table that you requested. 00:36:33.820 |
And this one's got a bit of extra magic, which 00:36:45.700 |
because SQLiteUtils doesn't have a column class. 00:36:53.140 |
what table it came from so that I could have it spit out 00:37:15.620 |
That's going to give me a cols getter for the artist table. 00:37:58.140 |
So it's going to go through each column in the table. 00:38:00.300 |
And it's going to create a _col with the table 00:38:03.580 |
name and the column name, which it then saves away. 00:38:08.860 |
And then it then calls repre on each one of those columns, 00:38:39.780 |
So the representation of a column is just the column name. 00:38:50.340 |
Is it because you have to have the special functionality? 00:38:58.540 |
The only thing that's shared is kind of that, I guess. 00:39:17.940 |
But at that point, it's like you're not really saving it. 00:39:25.580 |
So when you have an f string, and you put something 00:39:39.380 |
And when you call that function, behind the scenes, 00:39:50.300 |
So this is-- people often get confused about dunderstr 00:40:03.300 |
So in that case, we have the table in quotes, and then 00:40:14.420 |
And then I think we've probably talked about patch before. 00:40:21.460 |
But just in case people missed that last time, 00:40:27.420 |
And it's just going to add this method or property 00:40:33.660 |
And since we said as prop, it makes it a property. 00:40:44.580 |
I think this is a lot more ergonomic than having 00:40:49.700 |
a whole separate fastlite.tables bracket database or something. 00:40:56.980 |
Generally speaking, the way I think about patches, 00:40:59.020 |
if there's something which I think to myself, 00:41:01.740 |
like, oh, I feel like I would have liked this library better 00:41:42.180 |
Now, this is super lazy, of which I'm not ashamed. 00:42:15.380 |
Like, for example, in URLs, queue is normally the query. 00:42:23.740 |
So again, just for interactive use, slight convenience. 00:42:27.820 |
And if you're someone who prefers the type query, 00:42:35.300 |
The whole thing with this is that it's malleable and flexible. 00:42:39.660 |
Yeah, and I didn't want to change the behavior that's 00:42:43.860 |
there's good reasons to use generators sometimes. 00:42:50.220 |
then you don't have to put the whole thing in memory. 00:43:09.700 |
So you can see now I've just added views getter, which 00:43:12.180 |
is exactly the same as tables getter, but it's view names. 00:43:44.740 |
I don't know much about how VS Code autocomplete works. 00:43:47.860 |
But given that Python does offer dynamic access to namespaces 00:43:58.540 |
what other constraint or goal is causing people 00:44:04.180 |
not to make use of that when they do autocomplete? 00:44:08.860 |
So if you're in a .py file and you're halfway through, 00:44:24.820 |
I mean, I do think there's room to create an extension, which 00:44:31.380 |
kind of, like, maybe there's some special comments 00:44:38.020 |
that describes what objects to create and how to create them 00:44:42.500 |
so that you then do get dynamic autocomplete. 00:44:45.500 |
But yeah, the thing that you're getting autocomplete 00:44:47.780 |
for in VS Code is classes, not objects on the whole. 00:44:56.820 |
people generally put type annotations in here 00:45:15.140 |
So yeah, OK, so I looked around for database schema diagrams, 00:45:51.780 |
And my biggest complaint was that most of them 00:46:05.900 |
And my problem is that in this type of diagram, 00:46:15.740 |
Like, the lines just go to random places on the boxes. 00:46:23.580 |
What readings dot what is connected to where in sensors? 00:46:28.220 |
So I don't know why this is such a common way of doing things. 00:46:32.300 |
But I grew up on Microsoft Access, which I have always 00:46:45.020 |
And Microsoft Access has always shown you exactly what field 00:47:06.620 |
like jumping in and out to different programs 00:47:10.700 |
Having said that, this seemed like way too difficult a task 00:47:17.140 |
for me to bother with, given the amount of time it would save. 00:47:35.220 |
And it converts a small piece of text into a diagram. 00:47:57.660 |
Just jump straight into the full specification. 00:48:03.540 |
I feel like GraphViz is right up there with FFmpeg, 00:48:06.060 |
where the correct way to use it is something like chatGBT 00:48:09.140 |
and/or find an example that looks kind of like what you 00:48:18.020 |
And of course, the galleries are ridiculously overcomplicated. 00:48:27.860 |
Oh my god, and I never want to be ready for that. 00:49:05.260 |
And the thing in curly brackets has a string, an arrow, 00:49:12.580 |
And that creates a picture of a directed graph 00:49:15.700 |
from string A to string B. And that's actually it. 00:49:23.020 |
So we could-- it's nice that we've got a playground now. 00:50:03.060 |
So actually, the basics of GraphViz are super simple. 00:51:09.820 |
So maybe we'll look at the thing that comes out of it as well. 00:51:17.620 |
We could just grab that and just go s equals and print-- 00:51:39.900 |
and get rid of stuff that's not really needed. 00:51:49.780 |
So here we are taking advantage of a quite interesting 00:52:01.220 |
and curious addition to GraphViz that instead of just having-- 00:52:05.620 |
so here, this is the thing I just showed you, right? 00:52:20.260 |
But you can define ahead of time what a thing is, 00:52:26.820 |
And in this case, we have defined ahead of time 00:52:33.940 |
And the album table, it's just-- as you can see, 00:52:42.140 |
GraphViz just so happens to have borrowed this HTML-like dialect 00:52:53.180 |
So a table has rows, and a row has cells, data. 00:53:07.820 |
is port, which is basically giving a name to this row. 00:53:16.420 |
it finds the album node, and it finds the artist ID port. 00:53:21.300 |
And that's going to be the start of its error. 00:53:24.180 |
And then this is just a Unicode key I put next to each one. 00:53:37.140 |
I believe this is called blanched almond, this color, 00:53:43.740 |
And without this left to right rank direction, 00:53:49.420 |
you can see it goes top to bottom, which is not great. 00:53:59.260 |
Because our screens are wider than they are tall. 00:54:01.300 |
So if it's tough for a screen, you normally want that. 00:54:03.980 |
You can make it a graph instead of a digraph. 00:54:18.860 |
I don't really know of a reason not to use digraph, 00:54:22.740 |
because you can add annotations to these edges 00:54:25.980 |
to say to add an arrow on both sides, or just on one side, 00:54:30.460 |
So I think always you just write digraph, g curly, 00:54:35.580 |
rank to lr, then your definition, and then curly. 00:54:52.220 |
So yeah, the way I did it was I just wrote one kind of by hand 00:55:00.340 |
did basic string manipulation until the string 00:55:11.900 |
So yeah, basically, I'm going to need a list of edges 00:55:20.940 |
So my edges just go through each of my tables 00:55:29.820 |
So this is why I don't use auto formatters like Black, 00:55:39.060 |
When you've got two things that are doing the same thing, 00:55:43.700 |
No auto formatter is going to put the extra space in there 00:55:53.780 |
So that's going to go through all the tables' foreign keys. 00:56:00.060 |
It's like, OK, yeah, it's just a list of foreign key. 00:56:11.380 |
goes from table.column to other table.otherColumn. 00:56:18.260 |
So it's nice how simple it feels when it comes out. 00:56:31.580 |
basically consist of the table part and the rows part. 00:56:36.860 |
So my table nodes is going to be a _tnode applied 00:56:57.260 |
And then this is the name of the table at the top in light gray. 00:57:06.900 |
So my row, I'm going to have a row for each column. 00:57:11.180 |
So if it's a primary key, it's going to have that on the end. 00:57:27.660 |
And then there's the actual text that appears. 00:57:49.220 |
that then ends up in the Python file I showed you. 00:57:52.300 |
And yeah, in this case, to create the home page/readme, 00:57:56.260 |
all I did was I actually just made a copy of this notebook, 00:58:01.060 |
deleted all of the actual source code definitions. 00:58:07.260 |
And most of it otherwise is basically the same. 00:58:22.700 |
And that's because of, I think, the retina rendering 00:58:27.100 |
or whatever they have where it's not pixel for pixel, 00:58:37.420 |
to make that appear correctly, which Jupyter Notebook does not 00:58:40.860 |
currently do by default. That'd be a nice thing to add, 00:58:45.940 |
So I just add manually something to tell Quarto 00:58:51.100 |
how wide I want it to be, which does not appear correctly here. 00:59:17.580 |
I mean, it was literally a day's work, I guess. 00:59:30.780 |
there's no other way you could have done this. 00:59:34.620 |
But it probably took seven years to get to the point 00:59:37.260 |
that it feels that way, which I think is a good sign. 00:59:43.980 |
Yeah, you're getting a lot of leverage out of the fact 00:59:46.380 |
that his library just works in plain old data, plain old Python 01:00:05.220 |
There's a great documentation page in the Python docs 01:00:33.100 |
here's how all the Python syntax falls out of it. 01:00:41.140 |
like a for loop is syntax sugar for calling next 01:00:48.820 |
So yeah, I think it's not something everybody needs 01:00:52.740 |
to know, but it's something that probably more people would 01:01:04.540 |
- It's a shame to be using a dynamic language and not 01:01:12.100 |
- Speaking of, the repro function is interesting. 01:01:19.540 |
maybe this isn't something people think about anymore-- 01:01:22.060 |
but encountering the idea that the representation of an object 01:01:25.740 |
was supposed to be also a representation that 01:01:33.060 |
- It doesn't seem like that's actually observed in Python. 01:01:37.740 |
And I think that is, strictly speaking, wrong. 01:01:59.420 |
that define how an object serializes and writes itself. 01:02:16.100 |
- Yeah, well, the thing is, before I was a Swift nerd, 01:02:32.460 |
But then I also want it to look nice in IPython, 01:02:39.180 |
for example, which doesn't have a markdown wrapper. 01:02:47.060 |
- So repromarkdown gives you the representation in markdown. 01:02:53.060 |
yeah, basically, Jupyter has a system by which it 01:02:59.180 |
- And so I think if there's a repromime bundle, it uses that. 01:03:05.460 |
If that doesn't exist, it uses repromarkdown. 01:03:08.020 |
If that doesn't exist, it uses repromarkdown. 01:03:19.620 |
I think no one actually observes rep or read compatibility-- 01:03:25.940 |
I think it's kind of-- the idea that that should exist 01:03:28.140 |
is a holdover from another language other times. 01:03:40.180 |
seeing it get used maybe to teach some other people how 01:03:44.660 |
- I'm going to use it next to build a web application 01:03:50.500 |
So hopefully, I'll be able to show you that tomorrow. 01:04:10.220 |
- Python just moved a lot when I wasn't paying attention. 01:04:14.980 |
So you don't want to put yourself in a ghetto.