 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 DC. Tomorrow we're doing the launch of NB Dev because it's built on Korto, 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 Korto 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. Kind of 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 software, everything from like CLI apps to API clients to, you know, I've worked on the extensions of the Python programming language with Jeremy and a bunch of other things. And it's a very, it's interesting like how many different use cases that it's like a really good fit for. And it's, I think it's wonderful. So I'm really excited to show it to everybody. What about you, Jeremy? Like 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 NB Dev now because I don't get as much get into that flow state, which is such a pleasure, you know. So using a notebook because I'm doing exploratory programming. I'm really in the zone the whole time. And 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, you know, 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. You know, the other thing as you mentioned is like you get all this stuff for free, you know, 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 just to kind of 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 Ellen 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 a programming and Python and we're going to end up with a documentation site, a condo package, an IPI package, tests and continuous integration 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. Okay, patch your 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. So I'll 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. Vendors is a 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 of all of them. 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 NB Dev is written in NB Dev. And so the documentation actually uses exclamation mark that means run a shell command and 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. And this is this is huge because like the way that most people create documentation is they copy and paste code into markdown and they copy 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 don't not prepared to do things twice. Okay, so NB Dev new. 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. So 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 your home for all the stuff that you need for your app or your library. And the 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 PIPI install or whatever it's all going to come from one place and you don't have to worry about it. 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 and see what we've got here. So it's given us an O core notebook and index notebook and read me 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.ipa and B is going to become your homepage. As you can see, and core homepage for what homepage for the docs. Yeah, I'm pretty sure 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. Yeah, 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 literal programming approach is that because the documentation is the source because because the source code notebooks. Somebody who wants to get up to speed on your library is reading notebooks, you know. In fact, you know, NB Dev is a good example of that. So if we go to the GitHub repo for NB Dev and we're like okay well let's learn about how this software is written I can start on the very first one. And I know 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 that any years 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 pros, because like this is my exploration 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. So what's, 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, NB to dict is basically doing things that I've just done step by step in pros. 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 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 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. They're 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. This is the description. Okay. 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 a second be 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 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. Yeah, something about Python's Unicode handling maybe. My computer actually visually looks a little different than this. I don't get colored. I don't get 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 envy 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. But you could like spit things. Okay, so I think what we'll do instead since we discovered that doesn't work. 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 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, if we want to create a class that represents a card will say we want to 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. We'll just be setting self dot dot suit. Just to store it away basically, and self rank. We'll set them equal to the suit and the rank that 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 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 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 can I have a certain 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 organizations 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 it more closely follows the kind of style you would see from Lisp or APL or Julia programmers or F sharp programmers like programming languages that we're 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 place so, you know, I think it's important. If you are interested and be deaf 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. And I think that's one of the most important aspects of the project of Python, because not only are we, you know, going to show you, you know, this way of developing code and a notebook but there's also, you know, there are some extensions to the Python programming language that Jeremy has made that kind of enhance the rebel 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 ways of working. So, I think that's really important. Yeah, makes sense. So why you're talking I was just starting to fill in the kind of like information I would want to be passing on to a radar of my code or documentation. Because 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 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. Okay. So trying to explain in our docs as we go away things are as they are. Nice. Right, so now we can create a card. So that's not very helpful. And so that's that 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 the 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 want to do something similar for suits. Okay, and so that's what we're going to see if we print it like so. And as you'll see it's a little 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. So we can say, so we can add some documentation here, a playing card, passing in rank, ranks, and suit from suits. Okay, 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. So here's a function called dict to NB. You can see here that it's created some documentation. And in fact we could make the documentation nicer. Well let's take a look at it first. 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 I think it'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 you can put a index into suits. And so 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 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. 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, 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 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 have to. I'm going to say we don't actually need this year to be order 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. 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 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 suit 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 fast core. 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. It 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 this way when you do this. Normally that's a lot of work to create and not really worth the effort and be dev libraries to 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 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 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, but furthermore you can actually just put a second question mark and that'll give you the source code for it. 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 rebel based environment of getting all this information without scrolling around and wasting time. So I'm 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 the different ranks should not be equal. 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 dot suit comma self dot 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 dot suit comma a dot 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 into 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 I'm trippity you can hit control shift type and just put a cell out. And, you know, I'd quite like to kind of have all this stuff to find down here in one place would have like equals. You know, and so forth. So to define a method outside of its class, just 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. You can write it. 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 how can I define I can define, as you can see here I've defined card, and then I've patched inequality later. 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. Yeah, 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 also medically we'll automatically get documentation this. So then we could do less than or equal to, or less than I guess. So we would expect 13 to be less than 23. So this one need to be just use a search. There we go. And similar thing greater than so LT and GT is what Python users for less than and greater than. So this should not be the case for greater than less than for greater than we would expect 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 pie file. We can head over to heat to our terminal and type envy dev underscore export. We can type envy dev underscore export. We've now got an envy dev underscore cards directory with a card dot pie 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. Suits numbered according to this list. So you can see all that stuff has been imported into our environment from envy dev stock card. So that's pretty handy. So we'll call this envy dev 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 envy dev based on ideas. Think Python to think Python second edition by Ellen Daly. All right, install using or to the hyphen or underscore. Yeah, so this is a this is our first little tricky issue, which is that hyphen and underscore a special character in Python Python modules can't have a hyphen hyphen and Python I don't think can have an underscore or 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. That's okay. So to fix this, we have to change it in settings dot any to say that we've actually got two different things. So the lip name is actually, I guess, MB dev hyphen cards. And then the lip path is actually MB 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 lip name up here and you can override them if you like. Should we maybe have a look at the documentation now. Okay. So to preview your documentation. You can type MB 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. So let's run that again now that it'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 go to click on there. It's popped it open here. And so. Yeah, let's take a look so and be 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. It all is. If you look at the card page that 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 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 not that we're creating really something for users cord done to 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. Merge so shift and merges it more concise. 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. 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 that'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 envy dev homepage. The envy dev homepage is actually generated from a notebook. And so that one it was certainly helpful to have the auto generated preview. Okay. 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 the lecture we do the deck first and then we'll do see I. Yeah, but you could show local tests I think the local test of course I've got about those. All right so for local tests. So I'm just going to run this in the background believe that running. So just type envy 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 this 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. And for this 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. When you're using envy 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. Yeah, exactly. So you want because when you run envy 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. 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. Let's run them all. And we're all good to go. 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 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 and be 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. 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. Hang on. Can I? Let's try it. Maybe because you did it. Install. Oh, cards. They got it wrong. Yeah. 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. All right, 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. 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 Corto server and be deaf server running in the background, it's constantly trying to compile my code. So I had a dinner non compiling stages before just by a 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. 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. There we are. 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. And, you know, you can treat this just like you would any normal source code. So for example, I use them. So if I go to card and I hit control right square bracket and then jumps me straight to the definition of card. So like you can do, 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. Wasn't that working? Oh yeah, because it's not exported. You're a 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. Example. There you go. You can get the source code. All right. So, so how am I just started 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. It's a 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. And notice the 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 what Dunder contains for that. Let's see if the. 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. I'd say when we initially create a deck or other cards will be present that should be 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 DQ is a wrapper around assert that just will give you a nicer error message. Yeah, pretty much the set 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. So 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. 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 know that after your type character, but I know that at least at the moment in. Nitter, which is an our library. It doesn't like the space. So it's probably not a bad idea. Most of the stuff you'll see and all the stuff you'll see in quarter always has a space. 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 dot cards dot pop. So I wanted to import that. Oh, 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. Okay. 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 or here. And 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. Because exactly we have. Yeah. And not just MB dev projects. In fact, there's, there's a number of other things, including the 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. 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. 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 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. 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. So let's draw that in cards without replacement. Well needed 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 whereabouts we 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. That's the one difference is you can put an import inside a file that's exported. There we go. Let's take a look at how our documentation is looking. All right. Where should we head now? Oh, did we want to create the command line interface? Yeah. Sure. Or you want to do visualization to make sure your draws are doing okay. I mean, that's already in the tutorial. It's not really anything extra. Okay. No problem. Okay. I'm going to go ahead and export the files in the written tutorial as well. It's also really not anything extra 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. 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 to do that by using MB-dev clean. There's also some GitHub 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 documentation deployment added. The notebooks we created. Some information about the website. Our 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 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 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. So it's basically what it uses for GitHub pages. Okay. And so. If we look at actions, we can see. There we are. It's 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 web page. And that's what we did when we ran stuff locally. And then finally automatically. GitHub. Creates our web page for us. And so you can see it's. Visually 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. Now links are working. Okay. So. That's looking good. Shall we put this on Piper? Yeah. You know, and that's C. 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. 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. 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 read me is 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. Home page does. Getting it. 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. I pay and be and that's going to be the same as the home page. It won't be as beautiful as a home page because it's using more than the home page. So that's a good markdown. But the basic, you know, the basics are there. Okay. So we can see that there's an NB dev. Pi Pi to upload to. Pi Pi. And there's another one for conda doing conda. Or just NB dev release to do both. And it's going to do Pi Pi for now. All right. So let's run it. And it uploads to Pi Pi. And just at the end, it's also automatically bump the. 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. All the metadata. It looks like a very professional and polished library. For something that we spent. It looks like a very professional and polished library. It looks like a very professional and polished library. It looks like a very professional and polished library. For something that we spent. Under two hours on. Absolutely. 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. 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've noticed that we got a lot of pull requests. Just high quality ones, making the documentation better. Making the tests better. Because people read it. They get confused and they just resolved 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. It's an example of a package which uses MB dev. It's got. Over 20,000 stars on GitHub. Over 7,000 forks. So here's an example of an open PR. And one thing that we'd use, which is fine, as there's a thing called review and B, which 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 to 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. And what's great is, 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 shown today to just make a website. Let's say you don't want to 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 too, 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 the same set of tools to do that. And there's all kinds of advanced stuff that you can do too. Like Cortot 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 really exciting. Yeah. And it's a whole different philosophy of how to write software. And yeah, I think it's, you and I have both got the experience now that'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. So thanks everybody for watching. And Hamel, 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.