 This is a tutorial where we're going to be talking about NB Dev, 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 Hamel. Do you want to say hi, Hamel? Hi, everybody. Hamel's joining us from where are you, Hamel? I'm actually at the RStudio conference in Washington, D.C. Tomorrow we're doing the launch of NB Dev because it's built on Corto, 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 Hamel mentioned, we're going to be using NB Dev today, which works with Corto 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. 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 think I've been using NB Dev for about four years now, or three years. And I've done a lot of different types of, or made a lot of different types of software, everything from like CLI apps to API clients to, I've worked on the 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? I think the big thing for me is I don't really enjoy writing software very much, but I'm not using NB Dev now because I don't get as much get into that flow state, which is such a pleasure. So 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. So I'm kind of in this continuous zone of productivity, which feels enjoyable. And I've had various ways of trying to achieve something like that before NB Dev 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. So that's what we're going to build today, we're going to have a fun little sample project that's not going to do anything interesting this one. It's going to be based on this book, Think Python by Ellen Downey, which is a really great book. And we're going to build a deck of cards. So that basically going to be inspired by the deck of cards idea that comes from his book to do a bit of a programming in Python. And we're going to end up with a documentation site, a condo 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, yeah, we're going to have all that. Great. So the starting point for creating an NB Dev 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 NB Dev Cards, for example. Give it a description. Give it a patchy 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 NB Dev repository. I'm going to click the copy button. I'm going to head over to my terminal and we'll clone it. That repo, NB Dev Cards. And this is assuming that I've already got NB Dev installed. So to install NB Dev, you can follow the directions on the home page. So there's a written tutorial and just above it, you'll see the PIP install or condo install commands you can use. NB Dev is very, very lightweight. It has very few dependencies. Vendices 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 NB Dev 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 NB Dev main docs page. And the one we're going to use is NB Dev new. And you can also see a list of them anytime if you do NB Dev help in the terminal as well. You get a short description along. Great. And 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 actually NB Dev is written in NB Dev. 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 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. NB Dev is for lazy people like me. I'm very lazy and I'm not prepared to do things twice. Okay. So NB Dev new. So you can pass minus H to any NB Dev 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 can just go ahead and just run NB Dev here. As you can see here, when I run NB Dev new, it figures out all the details of my repo and it creates a file called settings.any. 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 can stay 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 Oocor 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 home page that you're going to be using. So index.ipy.nb is going to become your home page, as you can see. And Oocor. The home page for what? The home page for the docs. Yeah, home page for your documentation website, exactly. And Oocor is going to 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 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 Oocards or card. Yeah, card. You want to say something about the OO in front? Does that mean anything to you? Yeah, I think it's helpful to have things have some ordering, like the order in which things are designed to be A, red, and B built. Not so much how the software builds it, but how you built it. So the nice thing about this kind of literate programming approach is that because the source code are notebooks, somebody who wants to get up to speed on your library is reading notebooks. In fact, NB Dev is a good example of that. So if we go to the GitHub repo for NB Dev, and we're like, OK, well, let's learn about how this software is written. I can start on the very first one. And I know that's what I need to start reading to start understanding how this is written. And I can start. OK, 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 NB, which is the library that we've written to help work with notebooks. As you can really see, if we start at the very start here, like literally, you know, it's prose, because this was my exploration when I started writing this. It's like, what's a notebook? So I started opening them and reading them and printing out what's in them. So therefore, when you, the reader, start reading my code, you're following me on my journey of understanding 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, NB to dict is basically doing things that I've just done step by step in prose. 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 101 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 dot 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 dot 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 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 01 NB 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 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, for example, why is that not printing out anything? Is it something about the font, perhaps? Yeah, something about Python's Unicode handling maybe. My computer, it actually visually looks a little different than this. I don't get the color. Like I don't. Oh, 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 spit 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 that put them create a list of strings instead. Okay, that's more like what I was expecting. My 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 ranks zero so it's going to place over 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 the dunder 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. We'll set them equal to the suit and the rank. Now I should mention like, as you all know, Hamel, 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 in 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 pep eight, 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 say, 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 it more closely follows the kind of style you would see from Lisp, or APL, or Julia programmers, or Fsharp programmers, like programming languages that are working in the REPL, working in an interactive and exploratory way is more a part of the culture and toolkit of that environment. Can I say something about this? I think it's important if you are interested in MBDF to approach it with the right mindset. And I think one mindset that's been helpful for a lot of people is to look at the things that we're showing you today as a dialect of Python, because not only are we going to show you this way of developing code in a notebook, but there are some extensions to the Python programming language that Jeremy has made that kind of enhanced the REPL experience. And so if you think of it as a dialect, then you can kind of open up your mind to 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 information I would want to be passing on to a reader of my code or documentation. That's great. I think this is super powerful for many reasons. Often I find that when I'm writing the documentation like this, I discover bugs in my code or I discover clunky things in my code like, hey, this is very hard to explain and I end up refactoring it because of that. Yeah, definitely. So trying to explain in our docs as we go, why things are as they are. Okay, so there's our ace. All right, so now we can create a card. So that's not very helpful. And so that's the default representation. So we can override that using Dunder STR, which is a Dundersturer, which is the Python way of saying this is how I want to stringify my object. And 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. Well, as you'll see, it's a lot of 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. So 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 we can say, so we can add some documentation here. A playing card given by passing in a rank from ranks and suit from suits. Okay. Here's an example of creating and displaying a card. So for the attentive people that are really playing close attention, these backticks that Jeremy put in his dock string, just 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 dict2nb. You can see here some documentation. And in fact, we could make the documentation nicer. 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. Something which I think is nice to do is to give each parameter some 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 that kind of nice because I can now make my doc string a bit simpler. And in fact, I don't need to say passing in ranking 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 overly 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. We 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 going to show me those types 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, than I have to. As I say, we don't actually need this year 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 see that you've added it. It won't add it twice. 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. Yeah, exactly. So you might create docs for something that you've written without NB dev. Okay. Docs to 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 pros and also add wherever I wanted to the actual documentation. And this bit here, this header show doc 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. So I would kind of be saying things. So you can import some basic testing functionality from Fastcore. And, you know, 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 library itself. But it's, it's only a good idea. 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. MB dev libraries do it automatically. So one nice thing about MB dev libraries is that they work very well in repl 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 pep eight and stuff, you might want to explicitly import each thing carefully. But you know, one nice thing about exploratory programming is that people that aren't very familiar with their 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 dot test called test for quality test act. 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 it will 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. Or 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 repl 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. Where else 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 to be. 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 Gripity, you can hit ctrl-shift-typhon 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. 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 Fastcore. So the main things in Fastcore, if inside it, utils, and one of the things that will give us is something called patch. So you can add a quality to Fastcore 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 I can define, as you can see here, I've defined card, and then I've patched inequality. 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 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 one 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 less than or greater than we would expect that to be true. Okay. So that's all passing. 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 our terminal and type nvdev underscore export. We can type nvdev underscore export. And you'll see we've now got an nvdev underscore cards directory with a card.py file. We've 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. 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 class 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 nvdev.card. So that's pretty handy. So we'll call this nvdev underscore cards. And we're not automating this bit. Unfortunately, we probably should be. We have to copy the description over. So we might put a link here to where the inspiration for this comes from. A playing card. A deck of cards. Demo of nvdev. Based on ideas. Think Python 2. Think Python 2nd edition by Alan Downey. All right. Install using or. So the hyphen or underscore. Yeah. So this is a, this is our first little tricky issue. Which is that hyphen and underscore are a special character in Python. Python modules can't have a hyphen. And pypy I don't think can have an underscore. At least 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. But that's okay. So to fix this, we have to change it in settings. Any to say that we've actually got two different things. So the lip name is actually, I guess mbdev hyphen cards. And then the lip path is actually mbdev 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 lip name up here. And 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 mbdev 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 though. 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 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, yeah, let's take a look. So mbdev cards, if you let me pop this on the other screens, we can compare them more easily. So you can see how the heading, the summary, there it all is. If you look at the card page, that's interesting since. Yeah. So let's head over here and compare it to our card 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, we'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 pythons. 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. It's not that we're creating really something for users core done to equals. So like you could say show doc if you felt that your users were 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 then putting all this stuff together. Merge. So shift and merges. More concise. Test of less than. And this is, this is really cool. I mean, I find this part to be really nice. You can choose which tests you want to show to your 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. It is fine. Like you don't necessarily 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. So it's probably not a bad way, just one example of most of them. Okay. So I really like the way you get 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. Shall we 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 see I now or should we do that? Should we do the deck first and then we'll do see I. Yeah, but you could show local tests. I think the local local tests, of course, I've got about those. So for local tests, so I'm just going to run this in the background. We'll leave that running. So 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. That's break one. Save that. And so you can see now it tells you that in 00 card cell 20. So the colors could be better. But I'll fix that nighttime 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 core 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 tests. There we go. You can 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 hot key for reload and run all. Yeah, definitely. That's really useful. And what 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 hot key built in for running all cells. But it's a good idea to add one by hitting help edit keyboard shortcuts. It's around them all. And we're all good to go. I mean, a lot of people including me like to explore very interactively in notebooks and often go back up and rerun a cell and change things and see what happens. But that'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 air 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 trace of 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 where 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 the 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 a double install. And you got it wrong. Yeah, the card. Did I do a local install? I don't remember doing that. I guess I must have. So the reason that works, I guess, or maybe I did that without even thinking about it. 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. Alright, so. And now you might want to put the important 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. So just managed to break everything today. Oh, let's just make sure this is actually going to run. There we go. So because I've got the quarter server and be deaf server running in the background, it's constantly trying to compile my code. So I had it in a non-compiling stages before just like complain. 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. And 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 card. So that's not very helpful. So 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. Yeah. So now if we might just stop running our server for a bit, a bit annoying. So when we could now export. And we can see. We've now at a deck dot pi. 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 hit control right square bracket and Vim jumps me straight to the definition of card. You can do it, 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. Isn't 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 that. And so then we could check that that's working. That's looking helpful. So now 00 run. Oh, sorry, I shouldn't look at deck. I should look at card. There you go. You can get the source code. All right. So, so how am I to start talking about a one little wrinkle you have to be aware of when creating your code in NB 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 Dunder Len gives you that. And so by. So let's first create a deck call it deck. 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 the card is in the deck. So in Python they use a special Dunder think or Dunder contains for that. So just to remind myself what are the suits. So 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'd 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 testy queue is a wrapper around assert that just will give you a nicer error message. Yeah, pretty much assert 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 this 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. So generally, it doesn't matter whether you have a space or not after your type 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 quite all the stuff you'll see in quarter always has a space. All right, so let's patch in a pop to pop off a card from a deck. So some index can default for the last card. And so again we could add documents to this move and return. Okay, so we'll just return self.cards.pop. So I wanted to import that. And you've 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've got our index page, our card page and our deck page are all here. There'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 won't be to click on it right now because it's going because that we haven't put it on GitHub yet. But this will end this will link to and if I 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. And it works across all your big dev projects. Yeah, because exactly, we have. 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 umpire 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. Let's go to rerun this. Okay, so so just rerun that from the top to make the door 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 would expect it to be. By getting a remove in there as well. 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 we can we can check to see how the previous 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. 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 that in cards. Without replacement. Well, need a shuffle, but to add a shuffle. But it's put that straight into the class just to show how that would look. There's a shuffle. So since the shuffle here is directly in the class, we need to say we're about to be want to document it. So the way we would do that would be to put a 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. So 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 draws are doing or okay. It's not really anything extra. Okay. No problem. Yeah. The CLI is in the 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 written version of the tutorial of this online for a couple more handy little tricks. Okay. 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 MB dev clean. There's also some get 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. So we can add all that. Yeah, not a bad idea just to make sure you've exported everything. Make sure that the tests are working. So they're basically the three things. Check the files. So there's going to be continuous integration added document documentation deployment added the notebooks we created some information about the website homepage. The two modules we created our settings file setup file sidebar and a style sheet. That all sounds good to me. 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. And it's going to run and be dev test amongst other things. And it will also run something called quarter GHP 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 for GitHub pages. Okay. And so if we look at actions, we can see the 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 it installs all the Python stuff and you can see it basically looks the same as our quarter did when we ran stuff locally. And finally automatically it hub creates our web page for us. And so you can see it's easily 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. That's looking good. Shall we put this on PyPyHeml? Yeah. You know, and that's CI. I mean, I just want to point out like most projects at this point, you would only just have code. Yeah. 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 point. And it's exciting because I just, like my colleague, you know, I just say, hey, like I created that tool you wanted. And then here's the website. And they're like, you have a website? Yeah. Check it out. You can install it in PyPyHeml. Yeah. Yeah. And then they're like, wow. 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. I mean, it's not working. Oh, you know, the other thing we should have done is run nbdev 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. Getting up and 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 type high and be, 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 nbdev pypy to upload to pypy. And there's another one for conda doing conda or just nbdev release to do both. And it's going to do pypy for now. All right, so let's run it. And it uploads to pypy. 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 minus a bump. And it says I can view it here. There it is. And you can see it's even put 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. There's something that we spent under two hours on. And, and furthermore, like, it's, you know, it's not just for a little quick two hour 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 tens or hundreds of thousands of users. Yeah, no, it's great. And 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 then I can, you know, I noticed that we got a lot of pull requests with 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, yeah, as an example of a package which uses MB dev. It's got over 20,000 stars on GitHub over 7000 forks. 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. And 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 don't get pretty, pretty high quality pull requests to our projects. And what's great is 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 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 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 the quarter very powerful you can make books from it slides, all kinds of different formats. So it's the same, you know, for make 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 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. Thanks everybody for watching and hammer. 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. Thank you.