Back to Index

nbdev tutorial -- zero to published project in 90 minutes


Chapters

0:0 Welcome
4:39 Turn a github repo into a nbdev repo
6:48 What are nbdev commands?
8:17 How to find out the docs of a nbdev command
8:37 What does nbdev_new give us?
10:54 How should you name ipynbs for your library
15:32 What does the heading 1 and heading 2 do?
18:2 Create a Card module
19:1 Creating suits
21:23 Creating a class
26:54 How to overwrite the __str__ and __repr__
29:3 How to write docs for input argument
33:13 Create tests for the class
37:6 How to define equality
38:32 How to define a function outside of a class
42:18 How to export your Card py file with nbdev_export
48:33 How to preview your documentation
55:2 How to do local test
56:52 How to do debugging in real life
60:51 Which cells should be exported with #|export
62:9 Creating Deck module
66:41 How to overwrite the __len__ and __contains__
75:22 Automatic links
78:48 Creating a function
81:50 Pushing back to github
87:21 run nbdev_docs to put the homepage inside README.md
88:47 Release Your Library
90:16 How nbdev makes PR easier for all

Transcript

This is a tutorial where we're going to be talking about 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 MBDev because it's built on Cordo 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 MBDev today which works with Cordo to take a bunch of Jupyter notebooks and to turn it into a complete software package.

So we're going to walk through how we would go about doing that from scratch. It's actually much more exciting than that but go on get us excited. Not only will we create a software package but we'll show you how you can get a documentation site, a beautiful documentation site that's searchable for free.

You'll get CI for free. You'll get an amazing way to do unit tests and testing all within the same context. It's something that has made me more productive at least 10 times more productive while building all kinds of different software projects. I think you know I've been using MBDev 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 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. 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 MBDev now because I don't get as much get into that flow state which is such a pleasure. Using a notebook because I'm doing exploratory programming I'm really in the zone the whole time.

I very rarely have mysterious bugs because everything along the way I've built an exploratory way. I know exactly how it works and it's very easy to fix any problems 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 MBDev existed but I never had the same experience that this gives me.

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 a 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 Alan Downey which is a really great book and we're going to build a deck of cards so they're basically going to be inspired by the deck of cards idea that comes from his book to do a bit of OO programming in Python and we're going to end up with a documentation site a conda package a PyPy package tests and continuous integration which before this video is finished.

Am I missing anything Hamel? Is that what we're going to have? Yeah yeah we can have all that. Great so the starting point for creating an MBDev 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 MBDevCards for example give it a description okay 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 MPDev repository so I'll click the copy button I'm going to head over to my terminal and we'll clone it that repo MBDevCards and this is assuming that I've already got MBDev installed so to install MBDev 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 conda install commands you can use MBDev is very very lightweight it has very few dependencies dependencies are basically fast core and exec nb that's about it you don't even need Jupyter or anything to 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 MBDev underscore and hit tab you'll find that there's a bunch of command line tools that it's installed for you and so you can get a list of them here on the nbdev main docs page and the one we're going to use is nbdev new and you can also see a list of them anytime if you do nbdev help in the terminal as well at the you know you get a short description along them okay and you know this is actually a good example Hamill of how we don't have to do anything to keep our documentation up to date because our documentation being an nbdev is written in nbdev and so the documentation actually uses exclamation mark that means run a shell command in Jupyter followed by that shell command and the output here so the output in our documentation will always be up to date because it's running the actual code so 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 and paste the output into markdown and it becomes stale it becomes a big headache and that's why nobody writes documentation but this is why nbdev is for lazy people like me i'm very lazy you know not not prepared to do things twice okay so nbdev new so you can pass minus h to any nbdev command to find out how to use it in this case it's very very simple you just this got no command line arguments so you can just go ahead and just run nbdev here so as you can see here when i run nbdev new it figures out all the details of my repo and it creates a file called settings.any and settings.any is your 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 pi pi install or whatever it's all going to come from one place and you don't have to worry about it that is great otherwise you know like package management has so much boilerplate it's overwhelming and this makes it to where you can actually do it because it like it stays sane.

So the next thing we're going to do then is create um let's see what we've got here so it's given us an oocore 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 the home page that you're going to be using um so index.i.pi.nb is going to become your home page as you can see and oocore is the home page for what home page for the docs yeah home page for your documentation website exactly and also become like the the first thing that you create for your library and we're only really well we're going to use a couple of modules so that's that's fine um in fact um 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 i mean i like yeah i think it's helpful to have things have some ordering like the order in which things are designed to be a read and be built like not so much how the software builds it but how you built it so that you know the nice thing about this kind of literate programming approach is that because the documentation is the source that's because because the source code uh notebooks somebody who wants to get up to speed on your library is reading notebooks you know um 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 um you know i can start okay so here's what the settings that any is and here's where it comes from in fact even better might be if we look at exec nb which is the um library that we've written to help work uh with notebooks because you can really see if we start at the very start here like literally you know it's prose because like this was my expiration when i started writing this is like what's a notebook so i started opening them and reading them and printing out what's in them so therefore when you know when you the reader start reading my code you're following me on my journey of understanding yeah what what's what's going on at every step and then you can see when i've written a function you can understand why i've written that function because i've just explored up to a point where we can see that's the function i now need um 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 um so yeah we're also going to have a a deck of cards so we'll call this 101 deck so that's going to be a home page that's going to be a module for cards this will be a module for decks um 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 um so each notebook is going to produce one .py file one module uh 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 nbdev are used to tell quarter or nbdev something about this code so for example we'll be learning about showdoc in a moment and we have to import showdoc but the fact that we're doing that is something that the reader of your documentation doesn't care about so we hide this from the documentation that's built so this special one here called default x this is the default export this is what we're going to export symbols here into what module we're going to import it into a module called card.py this is marked down in our notebook and so this is where we can start typing things that we want to appear both for the reader of our source code and also it's going to end up in our documentation so we could write for example card a basic playing card a simple api for creating and using playing cards so 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 nbdev shortcut and what happens is this becomes the title of your page and that that quote block becomes the description of your page when it renders.

Yeah maybe a good way to understand how this works is to look at a library so let's take exec nb again and open up one of its notebooks so let's take 01 nbio and at the same time also open up its rendered documentation and so you'll see here we've got nbio and shell and here in the documentation here they are nbio 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 um 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 nbdev 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 um i kind of like to think about well how do i how do i want my um 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 um 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 uh you know a list of suits for example let's pop that up here maybe um 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 or hmm yeah something about python's unicode handling maybe my computer it actually visually looks different than this i don't get colored like i don't yeah 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 um i thought you could like split things hmm okay so i think what we'll do instead since we discovered that doesn't work which i think just shows my ignorance about how python uses unicode let's put them create a list of strings instead okay that's more like what i was expecting 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 um okay so that's that's useful um so then we're going to have all the different card ranks um and so there isn't really a rank zero so there's a bit of placeholder there and so this this code is kind of loosely based on alan downey's book so cool so um 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 init 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 um as you well know hamill the way i the way i write code is different to like the way most people write python code and um and in particular there's a lot of like specific recommendations in how to write python code in a document called pep 8 um which is kind of like a default style guide for python code um yeah folks who are working in an organization that uses pep 8 they don't take my particular approach to coding as a role model um i will say though that i've been coding for 40 years now and coding nearly every day for 30 years um and my particular approach to coding is not random there's a particular reason for it um 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 cheering award winner um i just want to say yeah i like my way of coding and that's what i'm going to show here um but if you're um yeah 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 uh 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 um 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 where working in the repel working in an interactive and exploratory way is more a part of the the culture and toolkit of that um yeah of that environment can i say something about this please so you know i think it's uh uh important if you are interested in nbdev to approach it with the right mindset and i think uh one mindset that's been helpful for a lot of people is to you know look at and the things that we're showing you today as a dialect of python because not only are we you know going to show you uh you know this way of developing code in a notebook but there's also uh you know there are some extensions to the python programming language that jeremy has made that that kind of enhance the repel experience and so if you think of it as a dialect then you can kind of open up your mind to you know a different different ways of working and also different conventions um so i think that's really important yeah makes sense so while you're talking i was just starting to fill in the kind of like information i would want to be passing on to a reader of my code or documentation that's great i think this is like really is this is super powerful for many reasons like often i find that when i'm writing the documentation like this um 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 okay so try to explain in our docs as we go why things are as they are okay so there's a nice right so now we can create a card um so that's not very helpful um and so that's the that's the default representation so we can override that using dunder str which is a dunder str 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 dot rank and then we would want to look that up in all the ranks and then we'd want to do something similar for suits okay and so that's what we're going to see if we print it like so now as you'll see it's a little bit different to how it's represented in a notebook so quite often i like my notebook representation to be the same as how it's printed it's an easy way to do that is by default jupiter will use the dunder repra method to return the representation in the notebook so if i just say i want this to be the same as the string version i can just do that as we see and so so we can say uh so we can add some documentation here um a playing card gave it by passing in a passing in rank from ranks and search 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 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 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 there's the documentation it's going to auto generate it's going to call this function called showdoc 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 parameters and documentation so to do that you can use something we invented called documents and this works like so you basically put a comment after a parameter and say and give it a comment so an index into suits and so that's 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 you know and having like oh in my opinion having over lever verbose documentation isn't a good idea if you have more information than needed then you know it's it's distracting to the reader you want the right amount of information so at this point you can see here this is how it's going to be represented that's the exact information i want very clear and so the other thing we might then do is say well what what what type is expected okay so that way you don't have to include that in the doc string because again when we spit out this show doc it's 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 and i have to as i say we don't actually need this here it'll be auto added for us so after a while you kind of get used to what things look like so you don't need it but if you do add it it's fine it will say that you've added it it won't add it twice an advanced feature we won't necessarily be discussing today is you know you can document other code bases with show doc yeah you know there's another reason you might want to use it exactly so you might create docs for something that you've written without nbdev okay docs for somebody else's library and that would be done by using show doc and importing their library so for example if we wanted to you know document something from exec nb for example i could import something like that thing we were just looking at you know and start writing some markdown prose and also add wherever i wanted to the actual documentation and this bit here this header show doc blah is not going to appear the only thing that appears in the documentation is the markdown output okay so i think you know in general we probably want to be able to recognize you know when when cards are the same or when they're less than or greater than some other card so what i kind of like to do for that is i kind of like creating tests to check that it's working correctly you can either create them before or after it doesn't really matter too much with exploratory programming 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 has a really famous library called tkenter 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 it's 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 dunderall 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 nb dev libraries do it automatically so one nice thing about nb dev libraries is that they work very well in rep or oriented programming because they'll support using wildcard imports but again it's something which if you're at an organization that uses kind of pet bait and stuff you might want to explicitly import each thing carefully um but you know one nice thing about exploratory programming is for people that aren't very familiar with their ide they often like don't know where symbols come from or what they mean so for example there's a thing in um fast core test core test for quality test act um in jupiter you don't have to worry about like you don't have to look up the top to find it because at any point you can just stick a question mark after it and it will tell you exactly where it came from and furthermore you can actually just put a second question mark and that'll give you the source code for it um or you can write it without any question marks and it'll just tell you the details of the source and the parameters uh or you can just hit um 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 um so we're going to test that for example a card like that should be equal to a card like that um 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 um 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 uh self.suit comma self.rank equals oh we don't need b it's going to be past the self yourself and then one other thing uh and check that against um a.suit comma a.rank there we go so they all pass um now we're not showing uh 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 um that the types are meant to be a card um 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 um uh 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 jupiter you can hit ctrl shift hyphen to split a cell out and you know i'd quite like to kind of have all this stuff defined down here in one place so i'd have like equals not equal you know and so forth um so to define a um a method outside of its class which is kind of something that's pretty common in things like c++ for example um you can also do it in python using something from another thing from fast core so the main things in fast core live inside its uh utils and one of the things that will give us is something called patch so we can add a quality to fast core just by saying patch and we're just going to say what do you want to patch i want to patch card and one nice thing is now that actually exists i don't need this weird quote thing anymore so if i now can i define i can define as you can see here i've defined card and then i've patched in a quality 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 in a notebook and so i'd be inclined at this point to kind of introduce a section in my documentation here um or comparison operators okay um and so we'll also medically we'll automatically get documentation for this um so then we could do less than or equal to or less than i guess um so we would expect um one three to be less than two three so this one eight two we just use assert there we go um and similar thing greater than so lt and gt is what python uses for less than and greater than so this should not be the case for greater than and then for less than or greater than we would expect that to be true okay so that's all passing all right i think that's basically our playing card um so at this point we can um we can try it out and so to create um our card.py file um we can head over to here to our terminal and type nv dev underscore export yeah we can type nv dev underscore export and you'll see we've now got um an nv dev underscore cards directory with a card.py file we've decided not to use core after all so i'll get rid of that um so like one thing we could do at this point is to see whether that seems to be working okay by importing it so you're in the index uh notebooks that's the home page that's going to be our home page exactly um so we can say like this web provides a card class you can use to create display and compare playing cards add one comma three um suits are numbered according to this list so you can see all that stuff has been imported into our environment from nv devs.com so that's pretty handy so we'll call this nv dev underscore cards and we're not automating this bit unfortunately we probably should be we'll have to copy the description over so we might put a link here to where the inspiration for this comes from a playing card a deck of cards demo of nv dev based on ideas um um think python two think python second edition by allen downey all right install using using or so the hyphen or underscore yeah so this is a this is our first um little tricky issue which is that um hyphen and underscore are a special character in python python modules can't have a hyphen hyphen and pi pi 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 um 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 um but that's okay um so to fix this we have to change it in settings.ini to say that we've actually got two different things um so the lib name is actually i guess nv dev hyphen cards and then the lib path is actually nv dev underscore cards that's a little bit confusing um this uh percent s business is um part of the thing from the standard the python standard library called config plaza um it just copies the variables from user and lib name up here and you can override them if you like um all right um should we maybe have a look at the documentation now okay so to preview your documentation um you can type nv dev underscore preview and that will fire up a um that will fire up a quarter web server for you um and as you can see it will automatically install quarter um if you don't already have it um 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 uh and so it's just going to sit there running a little server on port 3000 so if we now go go to um click on there um it's popped it open here and so yeah let's take a look so nv dev cards actually 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 um 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 um okay so you can see all this stuff's been hidden there's this 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 um i've saved i just hit save and you can see that it's um automatically done this here um and because these are like start with underscore they're like considered hidden by python so it doesn't automatically create show doc um we kind of don't do that because i you know we think a lot of people would not like it's it's not that we're creating really something for users called dunder 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'd do it something like this and it would pop up like so um 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 m merges a bit more concise you 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 your users and which ones you don't yeah because it makes sense to do it sometimes it doesn't make sense to yeah in fact you know let's just for example let's say the you know we just want to show them one test of each which 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 yeah rather than showing examples yes it's probably not a bad way just one example of most of them okay um 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 home page the nb dev home page is actually generated from a notebook and so that one it was certainly helpful to have the auto generated preview okay 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 ci now or should we do actually do the deck first and then we'll do ci yeah you can but you can show local tests i think the low yeah local tests of course i've got about those all right so uh for local tests so i'm just going to run this in the background we'll leave that running so if you just type nv dev underscore test and that's going to basically make sure all of your tests pass um and you know we don't like to give you more information than needed so it just tells you if they do let's break one save that and so you can see now it tells you that in zero zero card sell 20 my colors could be better but i'll fix that my term sometime it 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 um and at the end it'll summarize a list of any notebooks that failed so we'll go and fix that save it rerun our test there we go for these situations 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 give it i was going to say when you're using nv dev and you're debugging in real life um i think it's worth it to mention the hotkey for reload and run all um yeah definitely yeah that's that's really useful yeah exactly so you want because when you run nv 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 zero zero that restarts the kernel um and then there isn't a hotkey built in for running all cells but it's a good idea to add one by hitting help edit keyboard shortcuts let's run them all and we're all good to go a lot of i mean a lot of people including me like to explore very interactively in notebooks and often go back up and rerun a cell and change things and see what happens um but um yeah that's a very good idea from time to time to to hit zero zero and then rerun all the cells or at least to head over to your terminal and run nv dev uh test um yeah and another thing is like you know like when you get a failed test in your terminal you do what i do um just sharing my workflow is i come back and i do restart run all i get the error in the notebook and then i hit the interactive debugger like the percent percent debug and i kind of go from there see what's going on yeah so um 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 um figure out how to fix the bug and away you go all right let's do a deck of cards so we'll export this to something called deck so generally speaking you know in your second notebook it's pretty likely you're going to want to import this stuff from your previous notebook and at the moment i won't be able to do that actually yes hang on can i let's try it maybe because you did an editable install oh uh cards i think got it wrong yeah the card 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 e dot in your git 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 um all right so and now you might want to put the import in a separate cell and export it perhaps yeah exactly so let's do that because that's actually going to be part of the library is that we're 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 um oops i just managed to break everything did i oh um let's just make sure this is actually going to run there we go so because i've got the um um quarter server nb dev server running in the background it's constantly trying to compile my code so i had it in a non-compiling state just before just write complain um 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 um and so for example if we now like let's see so a full deck of cards is going to contain um every for every suit and for each suit for every card it's 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 um so now if we might just stop running our server for a bit a bit annoying um so when we could now export and we can see we've now got a deck.py and you know you can um 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 ctrl right square bracket and vim it jumps me straight to the definition of card um so like you can do what you know i can jump back so you can still like you know if you use vs code you can still use it just like the normal way that you would look through source code or of course we can use the trick we've seen before for example double question mark to get why isn't that working um 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 then we need to export the hat and so then we could check that that's working that's looking hopeful so now zero zero 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 um so Hamilton started talking about a um one little wrinkle you have to be aware of when creating your code in nvdev which is that there's one um 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 um 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 i notice a difference in case here this is my object and this is my class and this is my instantiated object there we go another thing that's useful is to know if a card is in the deck so in python they use a special dunder thing called dunder contains for that let's see if the um so just remind myself what are the suits so a one of clubs is that in the deck okay so give this a doc string so great all right i guess um we might want to create a hand now but you might want to then deal a hand or something right um well yeah but probably to do that we want to be able to um select a card from the deck so i guess first of all let's just uh see if we've got all the information we need here so um let's say when we initially create a deck four of the cards will be present that should be 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 test eq is a wrapper around a cert that just will give you a nicer error message yeah pretty much the cert 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 so this line this is one line of code this function's defined with two lines of code um so normally you can like yeah we've i mean there's good documentation for all this stuff of course so 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 uh uh 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 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 uh we should generally it doesn't matter whether you have a space or not um after your pipe character but i know that at least at the moment in knitter which is an r library it doesn't like the space so it's probably not a bad idea most of the stuff you'll see in all the stuff you'll see in quarter always has a space 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 so remove and return okay um okay so we'll just return self.cards.pop so i wanted to import that oh and you got to tell it what to patch um 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 so 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 so we can now say 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 um i won't be able to click on it right now because it's um okay because we haven't put it on github yet um but this will end this will link to in effect 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 um that to the documentation so that's pretty handy and it works across all your bdev projects uh yeah because exactly we have yeah and not just mvdev projects in fact there's um there's a number of other things including the python standard library and pandas and numpy and so forth that you can install indexes for those so that they'll automatically linkify as well um cool so then you know we can show that this seems to be working correctly um that's going to rerun this okay so um so just rerun that from the top to make that all work again um all right so that should be um in our deck now you can see that that's auto generated that documentation and we can also like check that pops behaving the way we'd expect it to be by getting a remove in there as well so i'm just going to copy and paste this one from when i built before to save a bit of time for us and so again we can check to see how the preview is looking and so when we see this is not appearing here we know what we forgot to export it there we go now it's appeared um so it's not just for methods we could also create a function so i've got a function that i've created earlier just for example so here's one that draws some cards with all our replacement so let's draw that in cards without replacement oh needed a shuffle about to add a shuffle if we just 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 um um so we're going to need to import random here let me just put it up 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 ammo where should we head now um oh um did we want to create the command line interface yeah sure are you want to do a visualization to make sure your draws are doing or okay um i mean that's already the tutorial it's not really anything extra okay no problem yeah i mean the ci lies in the actually the ci lies in the written new tutorial as well it's also really not anything extra so because this is already pretty long um and it's not really mv dev specific so yeah check out the um the the written version of the tutorial of this online for a couple more um handy little tricks um 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 um you can manually check do that by using nb dev clean there's also some githubs you can install to do this automatically you can look up in the docs if you're interested um and so if you um have a look now we can see what's what we've got ready to go to github um so we can add all that um yeah not a bad idea just to make sure you've exported everything um make sure that the tests are working so they're basically the three things uh check the files so there's going to be continuous integration added doc documentation deployment added uh the notebooks we created some information about the website our home page um the two modules we created our settings file setup file sidebar and a style sheet that all sounds good to me too tired today 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 um so one thing you'll find here is that when you push or when somebody puts in a pull request um it's going to run nb dev test amongst other things and it will also uh 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 that's basically what it uses for github pages um 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 that installs all the python stuff and you can see it basically looks the same as our quarter did when we ran stuff locally and then finally automatically github creates our web page for us and so you can see it's 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 too so what i like to do is i tend to copy that and then i go into my settings and paste it here there's our website awesome now links are working okay so that's looking good um shall we put this on pai pai hamel yeah you know and that ci i mean i just want to point out like most projects at this point you would only just have code so at this point we have ci we have a documentation site and this is the point where you know when i'm personally coding and i'm kind of down at this 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 i'm like yeah check it out you can install it yeah yeah and then they're like wow it's and then also you can just pip install it and also you have ci like how did you do all this such a short amount of time yeah it's awesome our readme is not working oh you know the other thing we should have done is run nb dev docs and um the reason for that is that um so that's kind of does a trial run of the same thing that um that it's going to have that github actions is going to do um but what it also does is it updates our readme to contain the same contents as the homepage does um keep forgetting i haven't got this alias installed so if we now look the other thing which we've got is a beautiful readme uh so that comes from our index.ipy and b and it's going to be the same as the home page it won't be as beautiful as the home page because it's using more limited markdown but the basic you know the basics are there um okay so we can see that there's an nb dev pipy to upload to pipy and there's a another one for conda doing conda or just nb dev release to do both i'm just going to do pipy for now all right so let's run it and it uploads to pipy and just at the end it's also automatically bumped the version so you can see here it's gone to version 2 so i tend to like just go git commit -am bump and it says i can view it here there it is and you can see it's even put the project description project home page it's all been done automatically which i think is all the metadata it looks like a very professional and polished library for something that we spent under two hours on um absolutely and and furthermore like it's you know it's not just for 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 i've never seen it i don't know and um 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 based 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 with just high quality ones making the documentation better making the tests 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 fastai is an example of a package which uses mbdev 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 is there's a thing called review nb it 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 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 showed 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 fastai 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 could do too like quartile 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 uh as well so yeah it's it's really exciting yeah and it's a whole different philosophy of how to write software um and uh yeah i think it's you and i've both got the experience now that it's made us more productive and we're having more fun so it's good to be doing this tutorial to think about people watching it that are going to join this journey as well so thanks everybody for watching and hamil thanks so much for joining in the tutorial um late at night before you're doing a keynote tomorrow i really appreciate it all right that's good bye thank you