Back to Index

nbdev tutorial (sped up)


Chapters

0:0
4:23 Create a Repository
5:14 Install Mbdev
5:55 Command Line Tools
8:48 Home Page
16:41 List of Strings
19:19 Coding Style
25:38 Document Other Code Bases with Show Doc
32:6 Comparison Operators
36:43 Documentation
48:18 Golden Rule
56:49 Create the Command Line Interface
57:46 Git Hooks
58:50 Github Actions
58:54 Github Workflow
62:5 Readme

Transcript

This is a tutorial where we're gonna be talking about NBDev, how to use it and kind of the development process. It'll be a fairly brief introduction and there'll be more videos coming soon with more and different and interesting ways to develop software. And I am here with Hamill. Do you want to say hi, Hamill?

Hi, everybody. Hamill's joining us from... Where are you, Hamill? I'm actually at the RStudio conference in Washington, DC. Tomorrow we're doing the launch of NBDev because it's built on Quarto, which is... So I'm here in a hotel room. Awesome. Thanks for joining us in the midst of your conference.

All right, let me share my screen here. Great. So as Hamill mentioned, we're going to be using NBDev today, which works with Quarto to take a bunch of Jupyter notebooks and to turn it into a complete software package. So we're going to walk through how we would go about doing that from scratch.

It's actually much more exciting than that, but... Go on, get us excited. Not only will we create a software package, but we'll show you how you can get a documentation site, a beautiful documentation site that's searchable for free. You'll get CI for free. You'll get an amazing way to do unit tests and testing all within the same context.

It's something that has made me more productive, at least 10 times more productive, while building all kinds of different software projects. I've been using NBDev for about four years now, or three years, and I've made a lot of different types of software, everything from CLI apps to API clients to...

I've worked on extensions of the Python programming language with Jeremy and a bunch of other things. It's interesting how many different use cases that it's a really good fit for. I think it's wonderful, so I'm really excited to show it to everybody. What about you, Jeremy? How do you feel?

I think the big thing for me is I don't really enjoy writing software very much, but I'm not using NBDev now because I don't get as much get into that flow state, which is such a pleasure. Using a notebook, because I'm doing exploratory programming, I'm really in the zone the whole time.

I very rarely have mysterious bugs, because everything along the way I've built an exploratory way, I know exactly how it works, and it's very easy to fix any problems. I'm in this continuous zone of productivity, which feels enjoyable. I've had various ways of trying to achieve something like that before NBDev existed, but I never had the same experience that this gives me.

The other thing, as you mentioned, is you get all this stuff for free, so the fact that I can quickly whip out in a couple of hours a complete project with continuous integration tests, documentation, PIP installers, all that stuff is pretty cool. That's what we're going to build today, just a fun little sample project.

It's not going to do anything interesting, this one. It's going to be based on this book, Think Python, by Alan Downey, which is a really great book, and we're going to build a deck of cards. So they're basically going to be inspired by the deck of cards idea that comes from his book to do a bit of programming in Python and we're going to end up with a documentation site, a Conda package, a PyPy package, tests, and continuous integration, which before this video is finished.

Am I missing anything, Hamel? Is that what we're going to have? Yeah, we're going to have all done. Great. So the starting point for creating an nbdev package is to create a repository. The smoothest path is generally to use GitHub. It's not strictly required. So here we are on GitHub.

I'll go ahead and create a repository and call it nbdevcards, for example. Give it a description. Apache license, create the repo. There it is. Okay. So that's step one. So what we're going to do now is we're going to clone this repo and turn it into an nbdev repository.

So I'll click the copy button. I'm going to head over to my terminal and we'll clone it. That repo, nbdevcards, and this is assuming that I've already got nbdev installed. So to install nbdev, you can follow directions on the home page. So there's a written tutorial, and just above it, you'll see the PIP install or condor install commands you can use.

nbdev is very, very lightweight. It has very few dependencies. Dependencies are basically fast core and exec nb. That's about it. You don't even need Jupyter or anything to run it. Obviously, to author stuff, you'll need Jupyter. But yeah, it's a very lightweight library. So once you've got it installed, if you type nbdev underscore and hit tab, you'll find that there's a bunch of command line tools that it's installed for you.

And so you can get a list of them here on the nbdev main docs page. And the one we're going to use is nbdev new. And you can also see a list of them anytime if you do nbdev help in the terminal as well. You get a shortened description along them.

Great. And, you know, this is actually a good example, Hamel, of how we don't have to do anything to keep our documentation up to date, because our documentation being an nbdev is written in nbdev. And so the documentation actually uses exclamation mark. That means run a shell command in Jupyter, followed by that shell command, and the output here.

So the output in our documentation will always be up to date, because it's running the actual code. This is huge, because like the way that most people create documentation is they copy and paste code into markdown and they copy and paste the output into markdown and it becomes stale.

It becomes a big headache and that's why nobody writes documentation. This is why I take care. nbdev is for lazy people like me. I'm very lazy and I'm not prepared to do things twice. Okay, so nbdev new, so you can pass -h to any nbdev command to find out how to use it.

In this case, it's very, very simple. You just this got no command line arguments so you just go ahead and just run nbdev here. So as you can see here, when I run nbdev new, it figures out all the details of my repo and it creates a file called settings.any and settings.any is your home for all the stuff that you need for your app or your library.

And the neat thing about this for a lazy person like me is that you only have to put this stuff in one place at one time like the version number and your details and so forth. Yeah, it means that you don't have to worry about putting these things in multiple places for your documentation for your PyPy install or whatever it's all going to come from one place you don't have to worry about it.

That is great. Otherwise, you know, like package management has so much boilerplate, it's overwhelming. And this makes it to where you can actually do it because it like it stays sane. So the next thing we're going to do then is create, let's see what we've got here so it's given us an OO core notebook, an index notebook, a readme, a setup, and some style sheet info so we'll learn about what all these things are in a moment.

So let's start by opening up the homepage that you're going to be using. So index.i.py.mb is going to become your homepage, as you can see, and OO core homepage for what homepage for the docs. Yeah, I'm going to your website. Exactly. And also become like the first thing that you create for your library.

And we only really, well we're going to use a couple of modules so that's, that's fine. In fact, I think for this one we're not necessarily going to have one called core I think we're going to have cards and a deck, we're going to put them just for explanation into two different modules so maybe we'll create cards first.

So maybe we'll rename this to OO cards or card. You want to say something about the OO in front? Does that mean anything to you? I mean, I like, yeah, I think it's helpful to have things have some ordering, like the order in which things are designed to be a read and be built, like, not so much how the software builds it but how you built it so that you know the nice thing about this kind of literate programming approach is that because the documentation is the source because the source code are notebooks.

Somebody who wants to get up to speed on your library is reading notebooks, you know, and in fact you know nbdev is a good example of that. So if we go to the GitHub repo for nbdev and we're like okay well let's learn about how this software is written, I can start on the very first one.

No, that's, that's what I need to start reading to start understanding how this is written. And, you know, I can start. Okay, so here's what the settings.ini is and here's where it comes from. In fact, even better might be if we look at exec nbdev, which is the library that we've written to help work with notebooks, because you can really see if we start at the very start here.

Like literally, you know, it's pros, because like this is my expiration when I started writing this is like what's a notebook so I started opening them and reading them and printing out what's in them. So therefore when you know when you the reader start reading my code, you're following me on my journey of understanding.

Yeah, what what's, what's going on at every step and then you can see when I've written a function. You can understand why I've written that function because I've just explored up to a point where we can see that's the function I now need. So for example, nbdict is basically doing things that I've just done, step by step in pros.

So yeah, that's the main thing you know the ordering of the file names is also used by default to order the table of contents in your documentation so that's another good reason to have it make some sense. So that's the only reason I'm using those numbers there. So yeah, we're also going to have a deck of cards.

So we'll call this one, or one deck. So that's going to be a homepage that's going to be a module for cards this will be a module for decks. In real life I would probably be putting these in the same module because they're not going to be very big but this is just good for demo I think.

So each notebook is going to produce one.py file, one module. So what do you want that module to be called, this is going to go up here so there's, you'll see there's some special comments here in the notebook they always start with hash pipe and if you've ever used quarter before this will look very familiar, because in quarter, it's exactly the same.

You can see special comments with hash pipe here as well. So these comments that are used in quarter and nb dev are used to tell quarter or nb dev something about this code. So for example, we'll be learning about show doc in a moment and we have to import show doc.

But the fact that we're doing that is something that the reader of your documentation doesn't care about. So we hide this from the documentation that's built. So this special one here called default x this is the default export. This is what we're going to export symbols here into what module, we're going to import it into a module called card.py.

This is marked down in our notebook. And so this is where we can start typing things that we want to appear, both for the reader of our source code, and also it's going to end up in our documentation. So we could write, for example, a basic playing card. A simple API for creating and using playing cards.

So this is the description. It might be worth the point that this having this header one in this note block is a kind of an nb dev shortcut. And what happens is this becomes the title of your page and that that quote block becomes the description of your page when it renders.

Yeah, maybe a good way to understand how this works is to look at a library. So let's take exec nb again and open up one of its notebooks. So let's take 01nb.io. And at the same time, also open up its rendered documentation. And so you'll see here we've got nb.io and shell.

And here in the documentation here they are nb.io and shell. And if we look at the notebook, you can see here is the header one. So that's become the title and the contents and the description here has come from the description. And that's used in things like the metadata of the page as well.

You can see the title, for example, gets built from that automatically. So that's a good way to kind of understand how to use nb dev and how your choices, make things appear is by looking at the sample. And then you'll see each of the second level headings ends up as and third level headings ends up as in the table of contents here.

So, I kind of like to think about, well, how do I, how do I want my, how do I want this to behave, you know, and so we're basically going to be, we're going to be creating a playing card. So I'm going to want to have some kind of like, something which I could do, like, create a card passing it like a numeric suit and a rank.

So we could create, you know, a list of suits for example. Let's pop that up here maybe. So we've now got a bunch of suits so you could like say suits one example. Why is that not printing out anything. Is it something about the font, perhaps, or. Yeah, something about pythons Unicode handling maybe that would be my computer it actually visually looks different than this.

I don't get colored. Like, I don't. Okay. Like, yeah, I don't ever see these colors or anything on my computer when on the same notebook. So this is where like nb dev is really helpful right because, you know, I'm not going to end up with some weird bug deep in my code because I'm exploring as I go.

You could like split things. Okay, so I think what we'll do instead since we discovered that doesn't work, which I think just shows my ignorance about how Python uses Unicode. Let's put them create a list of strings instead. Okay, that's more like what I was expecting. I guess is maybe I think like some emojis are actually like consists of multiple kind of code points or something I don't quite remember the terminology and it kind of ends up changing the color, like you know like a flag or something.

Yeah. Okay, so that's, that's useful. So then we're going to have all the different card ranks. And so there isn't really a rank zero so it's got a placeholder there. And so this this code is kind of loosely based on Alan Downey's book. So cool. So, so if we want to create a class that represents a card.

We'll say we want a class, and it represents a card. And so when we create one of these Python calls that under in it. When you create an object of a class. And so we're going to be passing in some suit and some rank. And so then we'll just be setting self dot dot suit, just to store it away basically and self dot rank.

So with the suit and the rank. Now I should mention like, as you all know, Hamill the way I, the way I write code is different to like the way most people write Python code, and in particular, there's a lot of like specific recommendations and how to write Python code in a document called pep eight, which is kind of like a default style guide for Python code.

Yeah folks who are working in an organization that uses Pepe, they don't take my particular approach to coding as a role model. I will say though that I've been coding for 40 years now. And coding nearly every day for 30 years. And my particular approach to coding is not random, there's a particular reason for it, which I've documented on the fast AI coding style, and it based on kind of many decades of work from people much smarter than me.

Particularly, Ken Iverson the Turing Award winner. I just want to say, yeah, I like my way of coding and that's what I'm going to show here. But if you're working in an organization that uses Python in a more traditional way, you should go with your own organization's coding style.

The style that I've developed, as I said, it's partly based on years of research from other languages decades of research it's also partly based on experience with exploratory and literate programming in particular. So more closely follows the kind of style you would see from Lisp or APL or Julia programmers or F# programmers like programming languages that were working in the REPL working in an interactive and exploratory way is more a part of the culture and toolkit of that.

Yeah, of that environment. Can I say something about this? Right. So, you know, I think it's important if you are interested in nbdev to approach it with the right mindset. And I think one mindset that's been helpful for a lot of people is to, you know, look at the things that we're showing you today as a dialect of Python because not only are we, you know, going to show you, you know, this way of developing code in a notebook, but there's also, you know, there are some extensions to the Python programming language that Jeremy has made that that kind of enhance the REPL experience.

And so if you think of it as a dialect, then you can kind of open up your mind to, you know, a different different ways of working and also different conventions. So I think that's really important. Yeah, makes sense. So while you're talking, I was just starting to fill in the kind of like information I would want to be passing on to a reader of my code or documentation.

That's great. I think this is like really is this is super powerful for many reasons like often I find that when I'm writing the documentation like this. You know, I discover bugs in my code or I discovered clunky things in my code like hey this is very hard to explain and end up refactoring it because of that.

Yeah, definitely. So try to explain in our docs as we go why things are as they are. Okay, so there's a nice. Right, so now we can create a card. So that's not very helpful. And so that's the that's the default representation. So we can override that using done to STR which is a done to stir, which is the Python way of saying this is how I want to stringify my object.

So I think a simple way to do this would be just to use an f string and say this is like a first of all the rank. So that would be the self rank, and then we would want to look that up in all the ranks. And then we'd want to do something similar for suits.

Okay. And so that's what we're going to see if we print it. Like so. Now, as you'll see it's a little bit different to how it's represented in a notebook. So quite often I like my notebook representation to be the same as how it's printed. It's an easy way to do that is by default, Jupiter will use the dunder Repra method to return the representation in the notebook so if I just say I want this to be the same as the string version, I can just do that, as we see.

And so, so we can say, so we can add some documentation here. A playing card, given by passing in a single rank from ranks and search from suits. Okay. So this is an example of creating and displaying a card. And for the attentive people that are really playing close attention, these back ticks that Jeremy put in his doc string is keep that in the back of your mind that's actually doing something special, and you'll see that.

Yeah, I mean we can look at it now so what's going to happen automatically is this is going to be turned into documentation. So for example, if we look at this one. That's a good example. So here's a function called dict two nb. You can see here that it's created some documentation.

And in fact we could make the documentation nice. Well let's take a look at this. So this the documentation is going to auto generate, it's going to call this function called show doc you actually don't have to put it in here manually it'll do it automatically when we build the docs but you can kind of get a bit of a preview.

So what's nice to do is to give each parameters and documentation. So to do that you can use something we invented called documents. And this works like so. You basically put a comment after a parameter and say and give it a comment so an index into suits. And so kind of nice because I can now make my doc string, a bit simpler.

You can say passing in rank and suit because you can already already see that they're right here. So really at this point we can strip it all the way back. You know, and having like, in my opinion having over lever verbose documentation isn't a good idea if you have more information than needed, then you know it's it's distracting to the reader, you want the right amount of information.

So at this point, you can see here, this is how it's going to be represented. That's the exact information I want very clear. And so the other thing we might then do is say well what what what type is expected. Okay, so that way you don't have to include that in the doc string, because again when we spit out this show doc, it's both in the table, and also in the signature.

So this again it's me being lazy. I don't want to include any more information than I, and I have to. As I say we don't actually need this here to be auto added for us. So after a while you kind of get used to what things look like so you don't need it but if you do add it, it's fine, it will say that you've added it, it won't add it twice.

So an advanced feature we won't necessarily be discussing today is, you know, you can document other code bases with show doc. Yeah, you know, there's another reason you might want to use it. Exactly. So you might create docs for something that you've written without nb dev, okay docs for somebody else's library, and that would be done by using show doc and importing their library.

So, for example, if we wanted to, you know, document something from exec nb for example, I could import something like that thing we were just looking at, you know, and start writing some markdown prose and also add wherever I wanted to the actual documentation, and this bit here this header show doc blah is not going to appear the only thing that appears in the documentation is the markdown output.

Okay. So I think you know in general we probably want to be able to recognize, you know, when when cards are the same or when they're less than or greater than some other card. So what I kind of like to do for that is I kind of like creating tests to check that it's working correctly you can either create them before or after it doesn't really matter too much with exploratory programming.

I would kind of be saying things so you can import some basic testing functionality from fast core. And you're again this approach of importing wildcards it's like, not the normal approach in a lot of Python libraries but for exploratory programming it works pretty well. It's certainly not unheard of like for example the Python standard library itself has a really famous library called TK enter, and you'll see that all of the examples in it actually use wildcard imports, so it's actually used in the Python standard itself.

But it's, it's only a good idea it like it's something you'd only want to do for libraries that are explicitly designed to work this way, because there's a somewhat advanced Python feature called done to all which ensures that things work correctly when you do this. Normally that's a lot of work to create, and not really worth the effort and be dev libraries do it automatically.

So one nice thing about nb dev libraries is that they work very well in rep or oriented programming because they'll support using wildcard imports. But again, it's something which if you're at an organization that uses kind of Pepe and stuff, you might want to explicitly import each thing carefully.

One nice thing about exploratory programming is for people that aren't very familiar with the idea they often, like, don't know where symbols come from or what they mean. So for example, there's a thing in fast core test core test for quality test tech. In Jupiter, you don't have to worry about like you don't have to look up the top to find it because at any point you can just stick a question mark after it, and it will tell you exactly where it came from.

And furthermore, you can actually just put a second question mark and that'll give you the source code for it. Or you can write it without any question marks, and it will just tell you the details of the source and the parameters. So you can just hit open parenthesis and then shift tab, and you'll get all the information here.

So there's lots of ways, you know, in a rebel based environment of getting all this information without scrolling around and wasting time. So we're going to test that for example, a card like that should be equal to a card like that. Whereas a card with a different suit should not be equal.

And a card with a different rank should not be equal. Okay, so if we run that it doesn't work, because we haven't defined a quality yet so the way you define a quality in Python is by defining a dunder equality. And we could just check that the two tuples are the same so if we check that self.suit, self.rank equals, oh we don't need b, it's going to be past the self, yourself and then one other thing.

And check that against a.suit, a.rank. There we go. So they all pass. Now, we're not showing like totally perfect software engineering principles here we should be checking that the types are the same and stuff like that but we could at least give some indication that the types are meant to be a card.

This is a slightly weird Python thing that Python doesn't know what a card is when you're inside a card. So nowadays you have to put it in quotes. You know, like, I tend to like having my functionality all in one place though. So what I would tend to do at this point is I would often split this out so in Jupyter you can hit ctrl shift hyphen to split a cell out.

And, you know, I'd quite like to kind of have all this stuff defined down here in one place, so I'd have like equals, not equal, you know, and so forth. So to define a method outside of its class, which is kind of something that's pretty common in things like C++, for example, you can also do it in Python, using something from another thing from fast core.

So the main things in fast core, inside its utils, and one of the things that will give us is something called patch. So we can add a quality to fast core just by saying patch. And we're just going to say what do you want to patch, I want to patch card.

And one nice thing is now that actually exists I don't need this weird quote thing anymore. So if I now can I define, I can define, as you can see here I've defined card. And then I've patched in a quality. And this is part of the what I was talking about the dialect of Python, this is one of the extensions that make it easier to write code in a notebook.

And so I'd be inclined at this point to kind of introduce a section in my documentation here, or comparison operators. Okay. And so we'll automatically we'll automatically get documentation for this. So then we could do less than or equal to, or less than I guess. So we would expect one three to be less than two three.

So this will need to be just use assert. There we go. And similar thing greater than. So, LT and GT is what Python uses for less than and greater than. So this should not be the case for greater than. And then for greater than we would expect that to be true.

Okay, so that's all passing. All right. I think that's basically our playing card. So at this point, we can. We can try it out. And so, to create our card.py file. We can head over to here to our terminal and type nv dev underscore export. We can type nv dev underscore export.

And you'll see we've now got an nv dev underscore cards directory with a card.py file. We decided not to use core after all so I'll get rid of that. So like one thing we could do at this point is to see whether that seems to be working okay. By importing it.

And this year in the index notebooks that's the homepage. That's going to be our homepage exactly. So we can say, like, this web provides a card. Plus, you can use to create, display, and compare playing cards. One comma three. Suits are numbered according to this list. So you can see all that stuff has been imported into our environment from nv devs.card.

So that's pretty handy. So we'll call this nv dev underscore cards. And we're not automating this bit, unfortunately. We probably should be. We'll have to copy the description over. Might put a link here to where the inspiration for this comes from. A playing card. A deck of cards. Demo of nv dev.

Based on ideas from. Think of Python 2. Think of Python 2nd edition by Alan Downey. All right. Install using. So the hyphen or underscore. Yeah, so this is a, this is our first little tricky issue, which is that hyphen underscore special character in Python. Python modules can't have a hyphen.

Hyphen and pi pi I don't think can have an underscore. You don't normally see them. So we actually are going to have a different name. So ideally, I wouldn't have picked a name with an underscore, because that's the only basically the only character that has this weirdness. That's okay.

So to fix this, we have to change it in settings on any to say that we've actually got two different things. So the lib name is actually, I guess, nv dev hyphen cards. And then the lib path is actually nv dev underscore cards. That's a little bit confusing. This percent s business is part of the thing from the standard, the Python standard library, or config parser.

It just copies the variables from user and lib name up here. You can override them if you like. All right. Should we maybe have a look at the documentation now. Okay, so to preview your documentation. You can type nv dev underscore preview, and that will fire up a that will fire up a quarter web server for you.

And as you can see, it will automatically install quarter. If you don't already have it. If you're on Linux, it'll do it automatically. I'm on Mac those so it's going to pop up this window. Quarter is updated recently, regularly, so it's not a bad idea to make sure you've got the latest version anyway.

All right, so let's run that again. Now that's installed. So that's going to build each of my pages and then what's going to happen is it's going to sit running in the background, and, and so it's just going to sit there running a little server on port 3000. So if we now go to click on there.

It's popped it open here. And so, you know, let's take a look so nv dev cards and she let me pop this on the other screens we can compare them more easily. So you can see how the heading the summary here it all is. Look at the card page that that's interesting since yeah so let's head over here and compare it to our page.

All right. Okay, so you can see all this stuff's been hidden. This is info that we've got. And as you can see you've got the auto generated docs here. Okay, now this is a mistake. This shouldn't be appearing and the reason why is that it's not being exported because I didn't export it.

So I'll copy this export here, and I'll paste it here. There we go. And then I've saved I just hit save and you can see that it's automatically done this here. And because these are like start with underscore they're like considered hidden by Python so it doesn't automatically create show doc.

We kind of don't do that because I, you know, we think a lot of people would not like it's it's not that we're creating really something for users called under equals so like you could say show doc. If you felt that your users are quite advanced and would know what that meant.

You know you do it something like this. And it would pop up like so. You know, personally I try not to expect my users to understand stuff like that, so I would rather just kind of put in some markdown I think. And in fact we could make all this a little bit shorter by them putting all this stuff together.

So shift M merges. It more concise. It's just a test of less than. And this is this is really cool. I mean I find this part to be really nice like you can choose which tests, you want to show to users and which ones you don't. Yeah, because it makes sense to do it sometimes it doesn't make sense to share in fact you know let's just for example let's say that, you know, we just want to show them one test of each.

We certainly want to overwhelm them with examples. So in that case, I would write hide here. And these are just the actual tests rather than, rather than showing examples. Yes, it's probably not a bad way just one example of most of them. Okay. And now you've got this kind of auto generated documentation.

I mean now that I kind of know what the docs are going to look like I don't use it that much myself unless I'm doing more advanced websites. An example of a more advanced website would be the nb dev homepage. The nb dev homepage is actually generated from a notebook.

And so that one it was certainly helpful to have the auto generated preview. Do you want to, do you want to show, do you want to talk about since we spent some time on tests, do you want to kind of, how do we know, do you want to do CI now or should we do that, should we do the deck first and then we'll do CI.

Yeah, but you can show local test I think the low yeah local test of course they've got about those. All right, so for local tests. So I'm just going to run this in the background for leave that running. So if you just type nb dev underscore test, and that's going to basically make sure all of your tests pass.

And, you know, we don't like to give you more information than needed so it just tells you if they do. Let's break one. And so you can see now it tells you that in 00 card cell 20. My colors could be better, but I'll fix that my term sometime shows you exactly where the problem occurs and it expected three of hearts, and it got a three of diamonds and you can see it even uses the correct representation.

Thanks to fast for test. And at the end it'll summarize a list of any notebooks that failed. So we'll go and fix that. Save it. We run our test. There we go. And to see what you can pass in, it'll run the tests in parallel, using as many workers as you have CPUs by default.

You can see how long they take to run. You can look at just particular files, so forth. There's lots of options you can do it. I was going to say, when you're using nb dev and you're debugging in real life. I think it's worth it to mention the hotkey for reload in run all.

Oh, yeah, definitely. That's really useful. Yeah, exactly. So you want because when you run nb dev test it's going to run your notebook from top to bottom. So to rerun your notebook from top to bottom. If you just hit 00 that restarts the kernel. And then there isn't a hotkey built in for running all cells, but it's a good idea to add one by hitting help at a keyboard shortcuts around them all.

And we're all good to go. A lot of, I mean, a lot of people, including me, like to explore very interactively and notebooks and often go back up and rerun a cell and change things and see what happens. But, yeah, it's a very good idea from time to time to hit 00 and then rerun all the cells, or at least to head over to your terminal and run nb dev test.

Yeah, and another thing is like you know like when you get a failed test in your terminal, you do what I do. I'm just sharing my workflow is I come back and I do restart run all I get the error in the notebook and then I hit the interactive debugger, like the percent percent debug and I kind of go from there.

Yeah, what's going on. Yeah, so. So just to explain if we put a bug in here. You get an error. You can hit percent debug, and it will drop you into an interactive debugger, which is called PDB it comes with Python and you can do things like find out the value of any variable like suit.

You can find out the stack traces where we are. You can get a listing of source code and so forth. And you can, yeah, basically write any Python expression you like, figure out how to fix the bug. And away you go. All right, let's do a deck of cards.

So we'll export this to something called deck. So generally speaking, you know in your second notebook it's pretty likely you're going to want to import this stuff from your previous notebook. And at the moment I won't be able to do that. Actually yes. Can I. Let's try it. Maybe because you did an editable install of cards and they got it wrong.

Yeah, the card yeah. I don't remember doing that. I guess I must have. So the reason that works I guess maybe I did that without even thinking about if you type PIP install minus a dot in your get repo. Basically it's going to install the thing you're currently working on as a library, and it's going to be pointing at your actual source code so every time you update it, it'll be there.

So that's how come I can import it. All right, so, and now you might want to put the import in a separate cell and export it, perhaps. Yeah, exactly. So, let's do that, because that's actually going to be part of the library is that we probably going to want to use these cards.

So that should be part of the exported. And actually we should probably look at that so let's take a look at. Oops, I just managed to break everything today. Let's just make sure this is actually going to run. There we go. So because I've got the quarter server nb dev server running in the background it's constantly trying to compile my code so I had it in a non compiling stages before, just by complaint.

So if I take a look at the nb dev cards card file you can see this is the source that's created for me. So for example, if we now like. Let's see. So a full deck of cards is going to contain every for every suit and for each suit for every card is going to contain a cut.

So that's not very helpful. We can do the same trick we've seen before, which is to just join the cards together when we're stringifying it and set the representation to be the same as the string area. So now if we might just stop running our server for a bit bit annoying.

So when we could now export and we can see you've never decked up by and you know you can treat this just like you would any normal source code. So, for example, I use VIM so if I go to card and I get control right square bracket and VIM jumps me straight to the definition of card.

So like you can do what you know I can jump back so you can still like you know if you use vs code you can still use it just like the normal way that you would look through source code. Or of course we can use the trick we've seen before for example double question mark to get that working.

Oh yeah, because it's not exported your fast core utils test. Oh yeah, that was a mistake. Okay, so that needs to be exported, because that's part of what we actually need. You should put all your imports in the separate cell by itself. Maybe it's a good time to mention that oh and then just a moment so that we need to export the hat.

And so then we could check that that's working. That's looking hopeful. So now, 00 run. Oh, sorry, I shouldn't look at deck I should look at card example. There you go, you can get the source code. Alright, so. So Hamilton started talking about a wonderful wrinkle you have to be aware of when creating your code in nv dev, which is that there's one golden rule, you have to keep in mind.

And that golden rule is that you, your cells should either contain imports or non imports, but not a mixture of the two. So you'll see this doesn't contain any imports. This doesn't contain anything but imports. And the reason for that is that when it builds your docs, it has to be able to go through and run every one of your cells containing imports, in order that it can then run all of your show doc cells correctly.

But it's not going to run any other cells. So just remember don't have a cell that contains both a import command, and also something else. That's the only slightly weird rule, you have to remember. I think something else that I'd like to add to our deck is just to know how big it is.

So done the land gives you that. And so by. So let's first create a deck call it deck. I notice a difference in case here this is my object and this is my class and this is my instantiated object. Another thing that's useful is to know if a card is in the deck.

So in Python, they use a special dunder thing called dunder contains for that. Let's see if the. So just remind myself what are the suits. So we're one of clubs. Is that in the deck. Okay, so if there's a doc string. Great. All right, I guess we might want to create a hand now that you might want to then deal a hand or something right.

Well, yeah, but probably to do that we want to be able to select a card from the deck so I guess first of all, let's just see if we've got all the information we need here. So, let's say when we initially create a deck, all of the cards will be present.

The 52 cards. And so this is where I put a test, rather than just displaying it because that way we're both showing the user what we're expecting and we're also ensuring that that continues to be the case in the future. Just as a reminder for people not familiar, the test EQ is a wrapper around assert adjust will give you a nicer error message.

Yeah, pretty much so that they're equal. So if they're not, it'll let you know what they actually were. So as you can see like most of the fast AI library code tends to be just a couple of lines of this line, this is one line of code is functions defined with two lines of code.

Normally you can like, yeah, we've, I mean there's good documentation for all this stuff of course if you go to the fast core docs, you can go to test and see examples of all of them. Yeah, but often you can just quickly look at the source code if you want to see exactly what's going on.

The ace clubs. Okay, so let's make it so that we can remove something from the deck. So we could just go ahead and edit the class. But as I say I kind of like to add things in. After the fact, you know, just keep things nice and separated. We should generally, it doesn't matter whether you have a space or not.

I don't have to your pipe character, but I know that, at least at the moment in Nitter, which is an art library. It doesn't like the space. So it's probably not a bad idea. Most of the stuff you'll see in all the stuff you'll see in quarter always has a space.

Alright, so let's patch in a pop to pop off a card from a deck. And default for the last card. And so again, we could add documents to this. Move and return one. Okay, so we'll just return self dot cards dot pop. So I wanted to import that. And you got to tell it what to patch.

So let's try popping something so if we pop something we should get the king of spades you would expect. And we did. So if we create a new deck here and pop it off. Again, I would tend to turn this into a test. There we go. So let's export that make sure everything's running okay.

It is. And so we can now do our preview again. So we can now say we've got our index page our card page and our deck page are all here. It's all our documentation. So one thing you'll notice here is that the back tick we used here for card has automatically become a hyperlink here to the documentation for that.

I'm a bit of click on it right now because it's going because we haven't put it on GitHub yet. But this will end this will link to just copy the link you'll get the idea copy link address, paste it. So that's where our documentation for that's going to end up being.

So, you can just use back ticks and it will automatically linkify as we call it that to the documentation. So that's pretty handy. So that's across all your dev projects. Exactly. We have. Yeah. And not just MB dev projects in fact there's, there's a number of other things including the Python standard library and pandas and NumPy and so forth that you can install indexes for those so that they'll automatically linkify as well.

Cool. So then you know we can show that this seems to be working correctly. It's going to rerun this. Okay, so. So just rerun that from the top to make it all work again. All right, so that should be in our deck. Now you can see that that's auto generated that documentation.

And we can also like check that pops behaving the way we'd expect it to be by getting a remove in there as well. So I'm just going to copy and paste this one from when I built before to save a bit of time for us. And so again we can check to see how the preview is looking.

And so when we see this is not appearing here we know what we forgot to export it. There we go. Now it's appeared. So it's not just for methods we could also create a function so I've got a function that I've created earlier. This for example. So here's one that draws some cards with all our replacement.

Let's draw 13 cards without replacement. Need a shuffle, but to add a shuffle. Everybody's put that straight into the class, just to show you how that would look. There's a shuffle. So, since the shuffle here is directly in the class, we need to say whereabouts we want to document it.

So the way we would do that would be to show doc. There we go. So we're going to need to import random here. Oh, one thing I should mention it's okay to put an import statement in an exported cell, because they're going to be run either way. That's the one difference is you can put an import inside a cell that's exported.

There we go. Let's take a look at how our documentation is looking. Nice. All right, I'm all where should we head now. Oh, did we want to create the command line interface. Yeah, sure. Are you want to do visualization to make sure your bras are doing, or okay. I mean that's already the tutorial it's not really anything extra.

Okay, no problem. Yeah, I mean this year lies in the two actually this year lies in the written tutorial as well it's also really not anything extra so because this is already pretty long. And it's not really MB dev specific so yeah check out the, the written version of the tutorial of this online for a couple more handy little tricks.

Okay, so to get it ready to send off to GitHub to send it up to GitHub one thing that I like to do is to make sure that there's like no unnecessary metadata in the notebooks. And so, you can manually check do that by using nb dev clean. There's also some good hooks you can install to do this automatically you can look up in the docs if you're interested.

And so if you have a look now we can see what's what we've got ready to go to GitHub. You can add all that you're not a bad idea just to make sure you've exported everything. Make sure that the tests are working. Basically the three things, check the files so there's going to be continuous integration added docs documentation deployment added the notebooks we created some information about the website homepage.

The two modules we created a settings file setup file sidebar, and a style sheet. That all sounds good to me tired. Okay, so that gets sent off to GitHub, where we should find it. There it is. And so GitHub has something called GitHub actions that automatically runs things that are in this GitHub GitHub workflows folder.

So one thing you'll find here is that when you push or when somebody puts in a pull request. It's going to run nb dev test, amongst other things, and it will also run something called quarter PHP which will set up your GitHub pages. So while we wait for that.

I'm going to go into pages and tell it that GH pages is the branch that's basically what it uses or GitHub pages. Okay, and so if we look at actions, we can see area that's all run. So the CI sensor continuous integrations that means our tests are passed, and the deployment was completed so you can actually click on these things to see them, or the steps that they run.

And you'll see that installs all the Python stuff, and you can see it basically looks the same as our quarter did when we ran stuff locally. And then finally automatically GitHub creates our web page for us. And so you can see it's basically building it there looks like it's finished building it.

Okay, it's finished. And you can see it tells us here where it's been built to. So what I like to do is I tend to copy that. And then I go into my settings and paste it here. There's our website. Awesome. Now links are working. Okay, so that's looking good.

Shall we put this on pai pai Hamill. Yeah, you know and that's the CI, I mean, I just want to point out like most projects at this point, you would only just have code. So, at this point, we have CI, we have a documentation site, and this is the point where, you know, when I'm personally coding and I'm kind of down at this is exciting because I just like my colleague, you know I just say hey like I created a tool you wanted.

And then here's the website. They're like, you have a website. Yeah, check it out. You can install it. Yeah, and then they're like wow it's, and then also you can just pip install it and also you have CI like how did you do all this, such a short amount of time.

Yeah. It's awesome. It's not working. Oh, you know the other thing we should have done is run nb dev docs. And the reason for that is that. So that's kind of does a trial run of the same thing that that it's going to have that GitHub actions is going to do.

But what it also does is it updates our read me to contain the same contents as the homepage does. Keep getting I haven't got this earliest installed. So if we now look, the other thing which we've got is a beautiful read me. So that comes from our index to my plan B and it's going to be the same as the homepage it won't be as beautiful as a homepage because it's using more limited markdown but the basic you know the basics are there.

Okay, so we can see that there's an nb dev pipeline to upload to pipeline, and there's another one for Conda doing Conda, or just nb dev release to do both. I'm just going to do pipeline for now. Alright, so let's run it. And it uploads to pipeline. And just at the end it's also automatically bump the version.

You can see here it's gone to version two. So I tend to like just go get commit. I'm bump. And it says I can view it here. There it is. And you can see it's even what the project description project homepage has all been done automatically, which I think is all the metadata.

It looks like a very professional and polished library for something that we spent under two hours on. And, and furthermore, like, it's, you know, it's not just for a little quick to our things like this you know I've. Well you and I have both written libraries that have taken years with thousands of lines of code and dozens of modules and 10s or hundreds of thousands of users.

Yeah, no, it's great. Imagine what it's like to make a PR into one of these projects so let's just say I don't have any idea how deck works at all. I'm like what is this deck thing, never seen it, I don't know. And without knowing anything about the code.

I just go to that notebook and there's all there's already the entry point for me. There's the code and then there's sample is on how to run it with documentation right there so there's no confusion. And, you know, I noticed that we got a lot of pull requests just high quality ones making the documentation better.

Making the test better, because people read it they get confused and they just resolve their confusion they just say I'm just going to edit this real quick and submit a PR. Yeah, I mean let's let's have a look at an example so last year as an example of a package which uses MB dev.

It's got over 20,000 stars on GitHub over 7000 folks. So here's an example of an open PR. And one thing that we'd use which is fine is there's a thing called review and be actually means I can click a button here. And because it's yeah because it's all in notebooks, you know, I can immediately see the documentation that I've added, and I can make comments on the documentation.

And if you know I can see the source code that's been changed I can see if they've added or removed any tests. I can see if any of the output graphs or whatever have changed. But, yeah, it's, it's, it's nice, both for me as the person reviewing that PR, and also as you say for the person making that PR that we're very much all on the same page about what's about what's going on.

So yeah I find we didn't get pretty, pretty high quality pull requests to our projects. So this is only kind of the tip of the iceberg, like there's a lot of exciting things so you can use the same tools that we've shown today to just make a website, let's say you don't necessarily write code maybe you want to just write a blog.

So we'll have a tutorial or video, like at some time in the near future that shows Okay, how do you do write a blog. And then also, you can just have a website to that. Maybe you just want to make a tutorial on, let's say how to use fast.ai or how to use your favorite library you can use same set of tools to do that.

And there's all kinds of advanced stuff that you can do to like Corto is very powerful you can make books from it slides, all kinds of different formats. And so it's the same, you know, for making any kind of technical content as well so yeah it's it's really exciting.

Yeah, and it's a whole different philosophy of how to write software. And, yeah, I think it's you and I've both got the experience now that it's made us more productive and we're having more fun, so it's good to be doing this tutorial to think about people watching it that are going to join this journey as well.

Okay, thanks everybody for watching and Hamill. Thanks so much for joining in the tutorial late at night before you're doing a keynote tomorrow I really appreciate it. All right. That's good. Bye.