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

Whisper Transcript | Transcript Only Page

00:00:00.880 | This is a tutorial where we're going to be talking about
00:00:13.280 | more and different and interesting ways to develop software and I am here with Hamel.
00:00:18.960 | Do you want to say hi Hamel? Hi everybody. Hamel's joining us from where are you Hamel?
00:00:25.920 | I'm actually at the RStudio conference in Washington DC. Tomorrow we're doing the launch of
00:00:32.480 | MBDev because it's built on Cordo which is so I'm here in a hotel room.
00:00:40.080 | Awesome thanks for joining us in the midst of your conference. All right let me share my screen here.
00:00:55.760 | Great so as Hamel mentioned we're going to be using MBDev today which works with Cordo
00:01:05.360 | to take a bunch of Jupyter notebooks and to turn it into a complete software package.
00:01:21.920 | So we're going to walk through how we would go about doing that from scratch.
00:01:26.240 | It's actually much more exciting than that but go on get us excited.
00:01:31.520 | Not only will we create a software package but we'll show you how you can get a documentation
00:01:39.920 | site, a beautiful documentation site that's searchable for free. You'll get CI for free.
00:01:46.400 | You'll get an amazing way to do unit tests and testing all within the same context. It's something
00:01:53.920 | that has made me more productive at least 10 times more productive while building all kinds
00:02:02.320 | of different software projects. I think you know I've been using MBDev for about four years now
00:02:08.400 | or three years and I've done a lot of different types of or made a lot of different types of
00:02:14.400 | software. Everything from CLI apps to API clients to you know I've worked on the extensions of the
00:02:23.680 | Python programming language with Jeremy and a bunch of other things. It's interesting how many
00:02:30.320 | different use cases that it's a really good fit for. I think it's wonderful so I'm really excited
00:02:39.680 | to show it to everybody. What about you Jeremy? I think the big thing for me is I don't really
00:02:48.000 | enjoy writing software very much but I'm not using MBDev now because I don't get as much get into
00:02:55.440 | that flow state which is such a pleasure. Using a notebook because I'm doing exploratory programming
00:03:02.560 | I'm really in the zone the whole time. I very rarely have mysterious bugs because
00:03:11.040 | everything along the way I've built an exploratory way. I know exactly how it works and it's very
00:03:16.480 | easy to fix any problems so I'm kind of in this continuous zone of productivity which feels
00:03:23.760 | enjoyable you know and I've had various ways of trying to achieve something like that before
00:03:29.680 | MBDev existed but I never had the same experience that this gives me.
00:03:33.440 | The other thing as you mentioned is like you get all this stuff for free you know so the fact that
00:03:45.680 | I can quickly whip out in a couple of hours a complete project with continuous integration
00:03:50.160 | tests documentation PIP installers all that stuff is pretty cool. So that's what we're going to
00:03:56.640 | build today just a kind of a fun little sample project it's not going to do anything interesting
00:04:02.800 | this one it's going to be based on this book Think Python by Alan Downey which is a really great
00:04:09.840 | book and we're going to build a deck of cards so they're basically going to be inspired by
00:04:15.680 | the deck of cards idea that comes from his book to do a bit of OO programming in Python and we're
00:04:21.680 | going to end up with a documentation site a conda package a PyPy package tests and continuous
00:04:29.920 | integration which before this video is finished. Am I missing anything Hamel? Is that what we're
00:04:35.680 | going to have? Yeah yeah we can have all that. Great so the starting point for creating an MBDev
00:04:43.760 | package is to create a repository the smoothest path is generally to use GitHub it's not strictly
00:04:53.280 | required so here we are on GitHub I'll go ahead and create a repository and call it
00:05:02.800 | MBDevCards for example give it a description okay
00:05:22.640 | give it a patchy license create the repo there it is okay so that's step one so what we're going
00:05:34.960 | to do now is we're going to clone this repo and turn it into an MPDev repository
00:05:41.760 | so I'll click the copy button I'm going to head over to my terminal
00:05:50.800 | and we'll clone it
00:05:57.840 | that repo MBDevCards and this is assuming that I've already got MBDev installed so to install MBDev
00:06:07.840 | you can follow the directions on the home page
00:06:14.640 | so there's a written tutorial and just above it you'll see the pip install or conda install
00:06:29.840 | commands you can use MBDev is very very lightweight it has very few dependencies
00:06:39.280 | dependencies are basically fast core and exec nb that's about it you don't even need Jupyter or
00:06:44.320 | anything to to run it obviously to author stuff you'll need Jupyter but yeah it's a very lightweight
00:06:49.760 | library so once you've got it installed if you type MBDev underscore and hit tab you'll find that
00:06:56.960 | there's a bunch of command line tools that it's installed for you and so you can get a list of
00:07:02.320 | them here on the nbdev main docs page and the one we're going to use is nbdev new
00:07:09.520 | and you can also see a list of them anytime if you do nbdev help in the terminal as well
00:07:17.760 | at the you know you get a short description along them okay and you know this is actually a good
00:07:26.640 | example Hamill of how we don't have to do anything to keep our documentation up to date because our
00:07:35.280 | documentation being an nbdev is written in nbdev and so the documentation actually uses exclamation
00:07:40.960 | mark that means run a shell command in Jupyter followed by that shell command and the output
00:07:46.000 | here so the output in our documentation will always be up to date because it's running the
00:07:51.920 | actual code so this is this is huge because like the way that most people create documentation is
00:07:58.480 | they copy and paste code into markdown and they copy and paste the output into markdown and it
00:08:03.120 | becomes stale it becomes a big headache and that's why nobody writes documentation but this is why
00:08:08.720 | nbdev is for lazy people like me i'm very lazy you know not not prepared to do things twice
00:08:16.000 | okay so nbdev new so you can pass minus h to any nbdev command to find out how to use it
00:08:27.120 | in this case it's very very simple you just this got no command line arguments so you can just
00:08:33.520 | go ahead and just run nbdev here so as you can see here when i run nbdev new it
00:08:40.800 | figures out all the details of my repo and it creates a file called settings.any and settings.any
00:08:50.560 | is your your home for all the stuff that you need for your app or your library and the
00:09:01.840 | the neat thing about this for a lazy person like me is that you
00:09:09.280 | only have to put this stuff in one place at one time like the version number and your details and
00:09:15.280 | so forth yeah it means that you don't have to worry about putting these things in multiple
00:09:24.240 | places for your documentation for your pi pi install or whatever it's all going to come from
00:09:28.640 | one place and you don't have to worry about it that is great otherwise you know like package
00:09:35.440 | management has so much boilerplate it's overwhelming and this makes it to where you can
00:09:41.520 | actually do it because it like it stays sane.
00:09:44.640 | So the next thing we're going to do then is create um
00:09:59.040 | let's see what we've got here so it's given us an oocore notebook an index notebook
00:10:07.360 | a readme a setup
00:10:11.920 | and some style sheet info so we'll learn about what all these things are in a moment
00:10:20.080 | so let's start by opening up the the home page that you're going to be using
00:10:27.440 | um so index.i.pi.nb is going to become your home page
00:10:32.480 | as you can see and oocore is the home page for what home page for the docs yeah home page for
00:10:42.640 | your documentation website exactly and also become like the the first thing that you create
00:10:49.440 | for your library and we're only really well we're going to use a couple of modules so that's
00:10:53.600 | that's fine um in fact um i think for this one we're not necessarily going to have one called
00:10:59.760 | core i think we're going to have cards and a deck we're going to put them just for
00:11:05.600 | explanation into two different modules so maybe we'll create cards first
00:11:09.040 | so maybe we'll rename this to oocards or card yeah card you want to say something about the
00:11:20.960 | oo in front does that mean anything to you i mean i like yeah i think it's helpful to have
00:11:26.320 | things have some ordering like the order in which things are designed to be a read and be built like
00:11:35.120 | not so much how the software builds it but how you built it so that you know the nice thing about
00:11:39.920 | this kind of literate programming approach is that because the documentation is the source
00:11:44.720 | that's because because the source code uh notebooks somebody who wants to get up to speed on your
00:11:49.280 | library is reading notebooks you know um in fact you know nb dev is a good example of that so if
00:11:55.920 | we go to the github repo for nb dev and we're like okay well let's learn about how this software is
00:12:01.440 | written i can start on the very first one and i know that's that's what i need to start reading
00:12:08.000 | to start understanding how this is written and um you know i can start okay so here's what the
00:12:15.280 | settings that any is and here's where it comes from in fact even better might be if we look at
00:12:20.880 | exec nb which is the um library that we've written to help work uh with notebooks
00:12:32.960 | because you can really see if we start at the very start here
00:12:40.480 | like literally you know it's prose because like this was my expiration when i started writing
00:12:45.920 | this is like what's a notebook so i started opening them and reading them and printing out
00:12:49.200 | what's in them so therefore when you know when you the reader start reading my code
00:12:54.240 | you're following me on my journey of understanding yeah what what's what's going on at every step and
00:13:02.080 | then you can see when i've written a function you can understand why i've written that function
00:13:06.800 | because i've just explored up to a point where we can see that's the function i now need
00:13:10.720 | um so for example nb to dict is basically doing things that i've just done step by step in prose
00:13:17.600 | so yeah that's the main thing you know the ordering of the file names is also used by default to order
00:13:26.880 | the table of contents in your documentation so that's another good reason to have it
00:13:31.120 | make some sense so that's the only reason i'm using those numbers there
00:13:38.160 | um so yeah we're also going to have a
00:13:40.400 | a deck of cards so we'll call this 101 deck so that's going to be a home page that's going
00:13:53.760 | to be a module for cards this will be a module for decks um
00:13:59.520 | in real life i would probably be putting these in the same module because they're not going to be
00:14:05.360 | very big but this is just good for demo i think um so each notebook is going to produce one .py file
00:14:14.960 | one module uh so what do you want that module to be called this is going to go up here so
00:14:22.640 | there's you'll see there's some special comments
00:14:30.240 | here in the notebook they always start with hash pipe and if you've ever used quarter
00:14:35.760 | before this will look very familiar because in quarter it's exactly the same
00:14:40.720 | you can see special comments with hash pipe here as well
00:14:55.360 | so these comments that are used in quarter and nbdev are used to tell quarter or nbdev
00:15:00.800 | something about this code so for example we'll be learning about showdoc in a moment and we have to
00:15:08.560 | import showdoc but the fact that we're doing that is something that the reader of your documentation
00:15:13.680 | doesn't care about so we hide this from the documentation that's built so this special one
00:15:20.880 | here called default x this is the default export this is what we're going to export
00:15:25.120 | symbols here into what module we're going to import it into a module called card.py
00:15:30.480 | this is marked down in our notebook and so this is where we can start typing things that we want
00:15:37.600 | to appear both for the reader of our source code and also it's going to end up in our documentation
00:15:42.800 | so we could write for example
00:15:49.280 | card a basic playing card
00:15:53.760 | a simple api for creating and using playing cards so this is the description
00:16:12.400 | okay it might be worth the point that this having this header one in this note block is a kind of
00:16:23.040 | an nbdev shortcut and what happens is this becomes the title of your page and that that quote block
00:16:31.440 | becomes the description of your page when it renders. Yeah maybe a good way to understand
00:16:37.920 | how this works is to look at a library so let's take exec nb again and open up one of its notebooks
00:16:47.280 | so let's take 01 nbio
00:16:50.000 | and at the same time also open up
00:16:55.440 | its rendered documentation and so you'll see here we've got nbio and shell and here in the
00:17:05.280 | documentation here they are nbio and shell and if we look at the notebook
00:17:10.160 | you can see here is the header one so that's become the title and the contents
00:17:20.400 | and the description here has come from the description
00:17:25.600 | and that's used in things like the metadata of the page as well
00:17:33.380 | you can see the title for example gets built from that automatically
00:17:40.320 | so that's a good way to kind of understand how to use nbdev and how your choices make things appear
00:17:50.080 | is by looking at the sample and then you'll see each of the second level headings ends up as
00:17:56.000 | and third level headings ends up as in the table of contents here
00:17:59.360 | so um i kind of like to think about well how do i how do i want my um
00:18:11.200 | how do i want this to behave you know and so we're basically going to be we're going
00:18:17.760 | to be creating a playing card um so i'm going to want to have some kind of like
00:18:21.840 | something which i could do like create a card passing it like a numeric suit
00:18:29.280 | and a rank
00:18:30.000 | so we could
00:18:36.080 | create uh you know a list of suits for example
00:18:58.160 | let's pop that up here maybe
00:18:59.680 | um so we've now got a bunch of suits so you could like say suits one for example
00:19:09.520 | why is that not printing out anything
00:19:16.720 | is it something about the font perhaps or
00:19:23.680 | hmm yeah something about python's unicode handling maybe
00:19:39.680 | my computer it actually visually looks different than this i don't get colored like i don't yeah
00:19:48.000 | oh okay like yeah i don't ever see these colors or anything on my computer when on the same
00:19:53.280 | notebook so this is where like nb dev is really helpful right because you know i'm not going to
00:20:01.520 | end up with some weird bug deep in my code because i'm exploring as i go um i thought you could like
00:20:07.520 | split things hmm okay so i think what we'll do instead since we discovered that doesn't work
00:20:16.240 | which i think just shows my ignorance about how python uses unicode
00:20:21.760 | let's put them create a list of strings instead
00:20:26.080 | okay that's more like what i was expecting
00:20:38.400 | my guess is maybe i think like some emojis are actually like consists of multiple kind of
00:20:47.440 | code points or something i don't quite remember the terminology and it kind of ends up changing
00:20:52.080 | the color like you know like a flag or something yeah um
00:20:59.760 | okay so that's that's useful um
00:21:06.240 | so then we're going to have all the different card ranks
00:21:17.520 | and so there isn't really a rank zero so there's a bit of placeholder there
00:21:24.960 | and so this this code is kind of loosely based on alan downey's book
00:21:37.440 | so cool so um so if we want to create a class that represents a card we'll say we want a class
00:21:45.600 | and it represents a card
00:21:48.480 | and so when we create one of these
00:21:55.040 | python calls the dunder init when you create an object of a class and so we're going to be passing
00:22:04.320 | in some suit and some rank and so then we'll just be setting self dot dot suit
00:22:15.680 | just to store it away basically and self dot rank
00:22:20.240 | we'll set them equal to the suit and the rank now i should mention like um
00:22:30.000 | as you well know hamill the way i the way i write
00:22:33.680 | code is different to like the way most people write python code and um
00:22:42.160 | and in particular there's a lot of like specific recommendations in how to write
00:22:48.560 | python code in a document called pep 8 um which is kind of like a default style guide for python code
00:22:57.200 | um yeah folks who are working in an organization that uses pep 8 they don't take my particular
00:23:02.720 | approach to coding as a role model um i will say though that i've been coding for 40 years now
00:23:10.560 | and coding nearly every day for 30 years um and my particular approach to coding is not
00:23:19.040 | random there's a particular reason for it um which i've documented on the fast ai coding style
00:23:25.680 | and it based on kind of many decades of work from people much smarter than me
00:23:30.240 | particularly ken iverson the cheering award winner um i just want to say yeah i like my
00:23:36.960 | way of coding and that's what i'm going to show here um but if you're um yeah working in an
00:23:43.680 | organization that uses python in a more traditional way you should go with your own organization's
00:23:49.840 | coding style the style that i've developed uh as i say it's partly based on years of research
00:23:57.600 | from other languages decades of research it's also partly based on experience with um exploratory
00:24:02.880 | and literate programming in particular so it more closely follows the kind of style you would see
00:24:08.720 | from lisp or apl or julia programmers or f sharp programmers like programming languages that
00:24:16.400 | where working in the repel working in an interactive and exploratory way is more a part of the
00:24:20.880 | the culture and toolkit of that um yeah of that environment can i say something about this
00:24:29.040 | please so you know i think it's uh uh important if you are interested in nbdev to approach it
00:24:37.040 | with the right mindset and i think uh one mindset that's been helpful for a lot of people is to
00:24:44.960 | you know look at and the things that we're showing you today as a dialect of python because not only
00:24:51.760 | are we you know going to show you uh you know this way of developing code in a notebook but there's
00:24:58.880 | also uh you know there are some extensions to the python programming language that jeremy has made
00:25:05.200 | that that kind of enhance the repel experience and so if you think of it as a dialect then you can
00:25:11.440 | kind of open up your mind to you know a different different ways of working and also different
00:25:16.960 | conventions um so i think that's really important yeah makes sense so while you're talking i was
00:25:25.600 | just starting to fill in the kind of like information i would want to be passing on to a
00:25:29.920 | reader of my code or documentation
00:25:37.600 | that's great i think this is like really is this is super powerful for many reasons like
00:25:41.920 | often i find that when i'm writing the documentation like this um you know i discover
00:25:48.960 | bugs in my code or i discover clunky things in my code like hey this is very hard to explain
00:25:53.680 | and i end up refactoring it because of that yeah definitely
00:26:09.120 | okay so try to explain in our docs as we go why things are as they are
00:26:38.240 | okay so there's a nice right so now we can create a card
00:26:53.360 | um so that's not very helpful um and so that's the that's the default representation
00:27:06.480 | so we can override that using dunder str which is a dunder str which is the python way of saying
00:27:14.960 | this is how i want to stringify my object and i think a simple way to do this would be just
00:27:22.720 | to use an f string and say this is like a first of all the rank so that would be the self dot rank
00:27:32.400 | and then we would want to look that up in all the ranks
00:27:37.680 | and then we'd want to do something similar for suits
00:27:49.680 | okay and so that's what we're going to see if we print it
00:27:59.040 | like so now as you'll see it's a little bit different to how it's represented in a notebook
00:28:03.680 | so quite often i like my notebook representation to be the same as how it's printed it's an easy
00:28:08.720 | way to do that is by default jupiter will use the dunder repra method to return the
00:28:17.360 | representation in the notebook so if i just say i want this to be the same as the string version
00:28:22.000 | i can just do that as we see and so
00:28:28.800 | so we can say uh so we can add some documentation here
00:28:34.880 | um a playing card
00:28:44.080 | gave it by passing in a passing in rank
00:28:50.480 | from ranks and search from suits
00:29:12.720 | is an example of creating and displaying a card and for the attentive people that are really
00:29:22.720 | playing close attention these back ticks that jeremy put in his doc string just keep that in
00:29:27.200 | the back of your mind that's actually doing something special and you'll see that yeah i
00:29:31.120 | mean we can look at it now so what's going to happen automatically is this is going to be
00:29:34.480 | turned into documentation so for example if we look at this one that's a good example so here's
00:29:43.680 | a function called dict2nb you can see here that it's created some documentation and in fact we
00:29:58.720 | could make the documentation nicer well let's take a look at it first
00:30:01.760 | so there's the documentation it's going to auto generate it's going to call this function called
00:30:08.720 | showdoc you actually don't have to put it in here manually it'll do it automatically when we build
00:30:12.400 | the docs but you can kind of get a bit of a preview something which i think is nice to do is to give
00:30:18.160 | each parameters and documentation so to do that you can use something we invented called documents
00:30:26.560 | and this works like so you basically put a comment after a parameter and say and give it a comment so
00:30:35.120 | an index into suits and so that's kind of nice because i can now make my doc string a bit simpler
00:30:45.680 | and in fact i don't need to say passing in rank and suit because you can already
00:30:53.280 | already see that they're right here so really at this point we can strip it all the way back
00:30:57.680 | you know and having like oh in my opinion having
00:31:05.120 | over lever verbose documentation isn't a good idea if you have more information than needed
00:31:10.320 | then you know it's it's distracting to the reader you want the right amount of information
00:31:16.240 | so at this point you can see here this is how it's going to be represented
00:31:21.360 | that's the exact information i want very clear and so the other thing we might then do is say well
00:31:26.160 | what what what type is expected okay so that way you don't have to include that in the doc string
00:31:33.040 | because again when we spit out this show doc it's going to show me those types both in the table
00:31:40.080 | and also in the signature so this again it's me being lazy i don't want to include any more
00:31:46.800 | information than i and i have to as i say we don't actually need this here it'll be auto added for us
00:31:53.360 | so after a while you kind of get used to what things look like so you don't need it but if
00:31:58.480 | you do add it it's fine it will say that you've added it it won't add it twice
00:32:02.000 | an advanced feature we won't necessarily be discussing today is you know you can
00:32:09.440 | document other code bases with show doc yeah you know there's another reason you might want to
00:32:15.520 | use it exactly so you might create docs for something that you've written without nbdev
00:32:21.600 | okay docs for somebody else's library and that would be done by using show doc and importing
00:32:26.640 | their library so for example if we wanted to you know document something from exec nb for example
00:32:36.880 | i could import something like that thing we were just looking at
00:32:47.920 | you know and start writing some markdown prose and also add wherever i wanted to
00:32:58.000 | the actual documentation and this bit here this header show doc blah is not going to appear the
00:33:04.160 | only thing that appears in the documentation is the markdown output okay so i think you know in
00:33:16.960 | general we probably want to be able to recognize you know when when cards are the same or when
00:33:23.200 | they're less than or greater than some other card so what i kind of like to do for that is
00:33:32.720 | i kind of like creating tests to check that it's working correctly you can either create them
00:33:38.160 | before or after it doesn't really matter too much with exploratory programming
00:34:00.160 | so i would kind of be saying things so you can import
00:34:03.920 | some basic testing functionality from fast core
00:34:09.680 | and you know again this approach of importing wildcards it's like not the normal approach in
00:34:18.480 | a lot of python libraries but for exploratory programming it works pretty well it's certainly
00:34:25.040 | not unheard of like for example the python standard library itself has a really famous library called
00:34:33.920 | tkenter and you'll see that all of the examples in it actually use wildcard imports so it's actually
00:34:40.560 | used in the python standard library itself but it's it's only a good idea it's like it's something
00:34:48.560 | you'd only want to do for libraries that are explicitly designed to work this way because
00:34:53.040 | there's a somewhat advanced python feature called dunderall which ensures that things
00:34:57.360 | work correctly when you do this normally that's a lot of work to create and not really worth the
00:35:02.640 | effort nb dev libraries do it automatically so one nice thing about nb dev libraries is
00:35:07.840 | that they work very well in rep or oriented programming because they'll support using
00:35:12.000 | wildcard imports but again it's something which if you're at an organization that uses kind of
00:35:18.320 | pet bait and stuff you might want to explicitly import each thing carefully
00:35:23.040 | um but you know one nice thing about exploratory programming is for people that aren't very
00:35:30.240 | familiar with their ide they often like don't know where symbols come from or what they mean
00:35:37.440 | so for example there's a thing in um fast core test core test for quality test act um in jupiter
00:35:45.920 | you don't have to worry about like you don't have to look up the top to find it because at any point
00:35:49.520 | you can just stick a question mark after it and it will tell you exactly where it came from
00:35:54.000 | and furthermore you can actually just put a second question mark and that'll give you the source code
00:35:59.280 | for it um or you can write it without any question marks and it'll just tell you the details of the
00:36:07.600 | source and the parameters uh or you can just hit um open parenthesis and then shift tab and you'll
00:36:15.120 | get all the information here so there's lots of ways you know in a REPL based environment of getting
00:36:20.720 | all this information without scrolling around and wasting time um so we're going to test that for
00:36:28.800 | example a card like that should be equal to a card like that um where else a card with a different
00:36:46.560 | suit should not be equal and a card with a different rank should not be equal
00:36:59.280 | okay so if we run that it doesn't work um because we haven't defined a quality yet so the way you
00:37:11.840 | define a quality in python is by defining a dunder equality
00:37:20.240 | and we could just check that the two tuples are the same so if we check that uh self.suit
00:37:34.240 | comma self.rank equals oh we don't need b it's going to be past the self yourself and then one
00:37:44.960 | other thing uh and check that against um a.suit comma a.rank
00:38:01.280 | there we go so they all pass um now we're not showing uh like totally perfect software engineering
00:38:10.080 | principles here we should be checking that the types are the same and stuff like that but we
00:38:13.680 | could at least give some indication um that the types are meant to be a card um this is a slightly
00:38:22.000 | weird python thing that python doesn't know what a card is into when you're inside a card so nowadays
00:38:27.760 | you have to put it in quotes um uh you know like i tend to like having my functionality all in one
00:38:40.320 | place though so what i would tend to do at this point is i would often split this out so in
00:38:45.200 | jupiter you can hit ctrl shift hyphen to split a cell out and you know i'd quite like to kind of
00:38:52.800 | have all this stuff defined down here in one place so i'd have like equals not equal you know and so
00:39:01.760 | forth um so to define a um a method outside of its class which is kind of something that's pretty
00:39:11.120 | common in things like c++ for example um you can also do it in python using something from
00:39:18.480 | another thing from fast core so the main things in fast core live inside its uh utils
00:39:26.640 | and one of the things that will give us is something called patch
00:39:30.640 | so we can add a quality to fast core just by saying patch and we're just going to say what
00:39:38.080 | do you want to patch i want to patch card and one nice thing is now that actually exists i
00:39:44.320 | don't need this weird quote thing anymore so if i now can i define i can define as you can see here
00:39:52.480 | i've defined card and then i've patched in a quality later and this is part of the what i
00:39:59.760 | was talking about the dialect of python this is one of the extensions that make it easier to write
00:40:05.360 | code in a notebook and so i'd be inclined at this point to kind of introduce a section in my
00:40:10.720 | documentation here um or comparison operators okay um and so we'll also medically we'll
00:40:24.880 | automatically get documentation for this um so then we could do less than or equal to or less than i
00:40:32.400 | guess um so we would expect um one three to be less than two three
00:40:56.640 | so this one eight two we just use assert
00:41:12.480 | there we go um
00:41:24.800 | and similar thing greater than
00:41:29.040 | so lt and gt is what python uses for less than and greater than
00:41:35.280 | so this should not be the case for greater than
00:41:52.240 | and then for less than or greater than we would expect
00:41:55.200 | that to be true okay so that's all passing
00:42:10.000 | all right i think that's basically our playing card um so at this point we can
00:42:21.280 | 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
00:42:34.080 | and type nv dev underscore export yeah we can type nv dev underscore export
00:42:49.760 | and you'll see we've now got um an nv dev underscore cards directory
00:42:58.240 | with a card.py file
00:43:03.440 | we've decided not to use core after all so i'll get rid of that
00:43:10.560 | um so like one thing we could do at this point is to see whether that seems to be working okay
00:43:18.640 | by importing it
00:43:25.120 | so you're in the index uh notebooks that's the home page that's going to be our home page exactly
00:43:32.880 | um so we can say like this web provides a card
00:43:43.920 | class you can use to create display and compare playing cards
00:43:57.520 | add one comma three um suits are numbered according to this list
00:44:13.680 | so you can see all that stuff has been imported into our environment from nv devs.com so that's
00:44:26.080 | pretty handy so we'll call this nv dev underscore cards
00:44:35.840 | and we're not automating this bit unfortunately we probably should be we'll have to copy the
00:44:43.280 | description over
00:45:09.840 | so we might put a link here to where the inspiration for this comes from
00:45:13.600 | a playing card
00:45:23.360 | a deck of cards
00:45:28.880 | demo of nv dev
00:45:36.160 | based on ideas
00:45:50.000 | um think python two think python second edition by allen downey
00:46:04.320 | all right
00:46:16.160 | install using
00:46:30.160 | using
00:46:54.000 | so the hyphen or underscore yeah so this is a this is our first um little tricky issue
00:47:06.880 | which is that um hyphen and underscore are a special character in python python modules
00:47:18.320 | can't have a hyphen hyphen and pi pi i don't think can have an underscore at least you don't
00:47:25.280 | normally see them so we actually are going to have a different name um so ideally i wouldn't
00:47:31.840 | have picked a name with an underscore because that's the only basically the only character
00:47:36.320 | that has this weirdness um but that's okay um so to fix this we have to change it in
00:47:44.560 | settings.ini to say that we've actually got two different things um so the lib name
00:47:52.640 | is actually i guess nv dev hyphen cards and then the lib path
00:48:03.840 | is actually nv dev underscore cards that's a little bit confusing um this uh percent s business is um
00:48:16.240 | part of the thing from the standard the python standard library called config plaza um it just
00:48:22.640 | copies the variables from user and lib name up here and you can override them if you like
00:48:31.520 | um all right um should we maybe have a look at the documentation now
00:48:39.600 | okay so to preview your documentation um you can type nv dev underscore preview
00:48:51.280 | and that will fire up a um that will fire up a quarter web server for you um and as you can see
00:49:04.800 | it will automatically install quarter um if you don't already have it um if you're on linux it'll
00:49:11.600 | do it automatically i'm on mac though so it's going to pop up this window
00:49:24.080 | quarter is updated recently regularly so it's not a bad idea to make sure you've got the latest
00:49:31.200 | version anyway all right so let's run that again now that's installed
00:49:43.280 | so that's going to build each of my pages and then what's going to happen is it's going to sit
00:49:52.880 | running in the background and uh and so it's just going to sit there running a little server on port
00:49:57.760 | 3000 so if we now go go to um click on there um it's popped it open here
00:50:08.320 | and so yeah let's take a look so nv dev cards actually let me pop this on the other
00:50:20.640 | screens we can compare them more easily so you can see how the heading the summary
00:50:28.560 | there it all is um if you look at the card page that that's interesting since yeah so let's head
00:50:40.080 | over here and compare it to our card page all right um okay so you can see all this stuff's been hidden
00:50:53.040 | there's this info that we've got
00:50:56.160 | and as you can see we've got the auto generated docs here
00:51:08.640 | okay now this is a mistake this shouldn't be appearing and the reason why is that it's not
00:51:14.080 | being exported because i didn't export it so i'll copy this export here and i'll paste it here
00:51:21.040 | there we go and then um i've saved i just hit save and you can see that it's um
00:51:36.800 | automatically done this here um
00:51:41.680 | and because these are like start with underscore they're like considered hidden by python so it
00:51:49.200 | doesn't automatically create show doc um
00:51:52.080 | we kind of don't do that because i you know we think a lot of people would not like it's
00:52:02.400 | it's not that we're creating really something for users called dunder equals so like you could say
00:52:07.440 | show doc if you felt that your users were quite advanced and would know what that meant
00:52:11.600 | you know you'd do it something like this
00:52:16.080 | and it would pop up like so um you know personally i try not to expect my users
00:52:26.240 | to understand stuff like that so i would rather just kind of put in some markdown i think
00:52:32.240 | and in fact we could make all this a little bit shorter by then putting all this stuff together
00:52:35.680 | merge so shift m merges
00:52:40.240 | a bit more concise
00:53:13.840 | test of less than and this is this is really cool i mean i find this part to be really nice like
00:53:28.080 | you can choose which tests you want to show to your users and which ones you don't yeah because
00:53:34.320 | it makes sense to do it sometimes it doesn't make sense to yeah in fact you know let's just for
00:53:38.960 | example let's say the you know we just want to show them one test of each
00:53:43.120 | which is fine like you don't necessarily want to overwhelm them with examples
00:53:51.760 | so in that case i would write hide here
00:53:54.880 | and these are just the
00:53:59.600 | actual tests rather than
00:54:08.320 | yeah rather than showing examples
00:54:12.400 | yes it's probably not a bad way just one example of most of them okay um so i really like the way
00:54:24.800 | you get this kind of auto generated documentation i mean now that i kind of know what the docs are
00:54:30.080 | going to look like i don't use it that much myself unless i'm doing more advanced websites
00:54:36.400 | an example of a more advanced website would be the nb dev home page
00:54:40.400 | the nb dev home page is actually generated from a notebook
00:54:48.480 | and so that one it was certainly helpful to have the auto generated preview
00:54:55.520 | okay shall we do you want to do you want to show do you want to talk about since we
00:55:05.440 | spent some time on tests do you want to kind of how do we know do ci now or should we do
00:55:12.160 | actually do the deck first and then we'll do ci yeah you can but you can show local tests i think
00:55:17.760 | the low yeah local tests of course i've got about those all right so uh for local tests
00:55:23.200 | so i'm just going to run this in the background we'll leave that running so if you just type nv dev
00:55:27.680 | underscore test and that's going to basically make sure all of your tests pass um
00:55:35.360 | and you know we don't like to give you more information than needed so it just
00:55:38.640 | tells you if they do let's break one save that
00:55:46.320 | and so you can see now it tells you that in zero zero card sell 20
00:56:01.440 | my colors could be better but i'll fix that my term sometime it shows you exactly where
00:56:05.760 | the problem occurs and it expected three of hearts and it got a three of diamonds and you
00:56:10.800 | can see it even uses the correct representation thanks to fast core test um and at the end it'll
00:56:18.560 | summarize a list of any notebooks that failed so we'll go and fix that save it rerun our test
00:56:31.200 | there we go
00:56:33.680 | for these situations to see what you can pass in it'll run the tests in parallel using as many
00:56:43.040 | workers as you have cpus by default you can see how long they take to run you can look at just
00:56:48.960 | particular files so forth there's lots of options you can give it i was going to say when you're
00:56:54.080 | using nv dev and you're debugging in real life um i think it's worth it to mention the hotkey for
00:57:00.080 | reload and run all um yeah definitely yeah that's that's really useful yeah exactly so you want
00:57:07.440 | because when you run nv dev test it's going to run your notebook from top to bottom so to rerun your
00:57:12.000 | notebook from top to bottom if you just hit zero zero that restarts the kernel um and then there
00:57:20.160 | isn't a hotkey built in for running all cells but it's a good idea to add one by hitting help edit
00:57:26.400 | keyboard shortcuts let's run them all and we're all good to go
00:57:32.080 | a lot of i mean a lot of people including me like to explore very interactively in notebooks
00:57:40.480 | and often go back up and rerun a cell and change things and see what happens um but um yeah that's
00:57:46.240 | a very good idea from time to time to to hit zero zero and then rerun all the cells or at least to
00:57:52.160 | head over to your terminal and run nv dev uh test um yeah and another thing is like you know like
00:58:02.000 | when you get a failed test in your terminal you do what i do um just sharing my workflow is i come
00:58:08.160 | back and i do restart run all i get the error in the notebook and then i hit the interactive debugger
00:58:12.720 | like the percent percent debug and i kind of go from there see what's going on yeah so um
00:58:21.440 | so just to explain if we
00:58:24.320 | put a bug in here
00:58:30.720 | you get an error you can hit percent debug
00:58:37.360 | and it will drop you into an interactive debugger which is called pdb it comes with python and you
00:58:45.600 | can do things like find out the value of any variable like suit you can find out the stack
00:58:52.640 | trace of where we are you can get a listing of source code and so forth and you can yeah
00:58:58.800 | basically write any python expression you like um figure out how to fix the bug
00:59:10.320 | and away you go all right let's do a deck of cards
00:59:17.120 | so we'll export this to something called deck
00:59:33.120 | so generally speaking you know in your second
00:59:52.240 | notebook it's pretty likely you're going to want to import this stuff from your previous notebook
01:00:00.960 | and at the moment i won't be able to do that actually yes hang on can i let's try it
01:00:07.360 | maybe because you did an editable install oh uh cards i think got it wrong yeah the card yeah
01:00:17.440 | did i do a local install i don't remember doing that i guess i must have
01:00:25.120 | so the reason that works i guess or maybe i did that without even thinking about it if you type
01:00:29.920 | pip install minus e dot in your git repo basically it's going to install the thing you're currently
01:00:37.920 | working on as a library and it's going to be pointing at your actual source code so every
01:00:42.800 | time you update it it'll be there so that's how come i can import it um all right so and now you
01:00:52.400 | might want to put the import in a separate cell and export it perhaps yeah exactly so let's do
01:00:59.680 | that because that's actually going to be part of the library is that we're probably going to want
01:01:04.000 | to use these cards so that should be part of the exported and actually we should probably look at
01:01:11.600 | that so let's take a look at um oops i just managed to break everything did i oh um
01:01:19.760 | let's just make sure this is actually going to run
01:01:26.080 | there we go so because i've got the um um quarter server nb dev server running in the background
01:01:33.520 | it's constantly trying to compile my code so i had it in a non-compiling state just before
01:01:37.680 | just write complain um so if i take a look at the nb dev cards card file you can see this is the
01:01:47.760 | source that's created for me um and so for example if we now like let's see
01:02:00.800 | so a full deck of cards is going to contain
01:02:13.600 | um every for every suit and for each suit for every card it's going to contain a card
01:02:21.520 | so that's not very helpful
01:02:31.200 | so we can do the same trick we've seen before which is to
01:02:41.760 | just join the cards together when we're stringifying it and set the representation
01:02:46.480 | to be the same as the string
01:02:47.840 | there we are um
01:02:55.440 | so now if we might just stop running our server for a bit a bit annoying um so when we could now
01:03:07.440 | export and we can see
01:03:13.360 | we've now got a deck.py and you know you can um
01:03:24.000 | treat this just like you would any normal source code so for example i use vim so if i go to card
01:03:31.600 | and i hit ctrl right square bracket and vim it jumps me straight to the definition of card
01:03:36.160 | um so like you can do what you know i can jump back so you can still like you know if you use vs
01:03:43.840 | code you can still use it just like the normal way that you would look through source code
01:03:49.200 | or of course we can use the trick we've seen before for example double question mark to get
01:03:56.240 | why isn't that working
01:04:07.360 | oh yeah because it's not exported your fast core utils test oh yeah that was a mistake
01:04:28.080 | okay so that needs to be exported because that's part of what we actually need
01:04:34.640 | you should put all your imports in the separate cell by itself maybe it's a good time to
01:04:43.440 | mention that oh and then just a moment so then we need to export the hat
01:04:48.240 | and so then we could check that that's working
01:04:53.680 | that's looking hopeful so now zero zero run
01:05:05.040 | oh sorry i shouldn't look at deck i should look at card example there you go you can get the
01:05:20.560 | source code all right so um so Hamilton started talking about a um one little wrinkle you have
01:05:31.360 | to be aware of when creating your code in nvdev which is that there's one um golden rule you have
01:05:40.720 | to keep in mind and that golden rule is that you your cells should either contain imports
01:05:49.840 | or non-imports but not a mixture of the two so you'll see this doesn't contain any imports
01:06:00.240 | this doesn't contain anything but imports um and the reason for that is that when it builds your
01:06:05.680 | docs it has to be able to go through and run every one of your cells containing imports
01:06:12.480 | in order that it can then run all of your show doc cells correctly but it's not going to run
01:06:17.440 | any other cells so just remember don't have a cell that contains both a import command and also
01:06:25.840 | something else that's the only slightly weird rule you have to remember
01:06:36.960 | i think something else that i'd like to add to our deck is just to know how big it is
01:06:43.040 | so dunder len gives you that
01:06:52.960 | and so by so let's first create a deck call it deck
01:06:57.440 | i notice a difference in case here this is my object and this is my class and this is my
01:07:11.200 | instantiated object there we go another thing that's useful is to know if a card is in the deck
01:07:27.360 | so in python they use a special dunder thing called dunder contains for that
01:07:36.320 | let's see if the um so just remind myself what are the
01:07:44.080 | suits so a one of clubs is that in the deck
01:07:51.680 | okay so give this a doc string
01:08:08.880 | great
01:08:29.600 | all right i guess um we might want to create a hand now but you might want to then deal a hand
01:08:36.480 | or something right um well yeah but probably to do that we want to be able to um select a card
01:08:48.480 | from the deck so i guess first of all let's just uh see if we've got all the information we need
01:08:59.200 | here so um let's say when we initially create a deck four of the cards will be present
01:09:18.720 | that should be 52 cards and so this is where i'd put a test rather than just displaying it
01:09:30.720 | because that way we're both showing the user what we're expecting and we're also ensuring
01:09:37.040 | that that continues to be the case in the future just as a reminder for people not familiar the
01:09:44.240 | test eq is a wrapper around a cert that just will give you a nicer error message yeah pretty much
01:09:51.520 | the cert that they're equal so if they're not it'll let you know what they actually were
01:09:58.240 | so as you can see like most of the fast ai library code tends to be just a couple of lines so this
01:10:05.680 | line this is one line of code this function's defined with two lines of code um so normally
01:10:12.240 | you can like yeah we've i mean there's good documentation for all this stuff of course
01:10:16.000 | so if you go to the fast core docs you can go to test and see examples of all of them
01:10:24.960 | yeah but often you can just quickly look at the source code if you want to see exactly what's
01:10:31.920 | going on
01:10:54.960 | uh the ace clubs
01:11:12.720 | okay so let's make it so that we can remove something from the deck so we could just go
01:11:16.720 | ahead and edit the the class but as i say i kind of like to add things in after the fact you know
01:11:24.000 | just keep things nice and separated uh we should generally it doesn't matter whether you have a
01:11:30.640 | space or not um after your pipe character but i know that at least at the moment in
01:11:39.040 | knitter which is an r library it doesn't like the space so it's probably not a bad idea
01:11:46.960 | most of the stuff you'll see in all the stuff you'll see in quarter always has a space
01:11:52.800 | all right so let's patch in
01:12:04.000 | a pop to pop off a card from a deck
01:12:07.040 | so some index
01:12:11.040 | can default for the last card
01:12:16.720 | and so again we could add documents to this
01:12:32.480 | remove and return
01:12:43.440 | um okay so we'll just return self.cards.pop
01:13:07.280 | so i wanted to import that
01:13:14.240 | oh and you got to tell it what to patch
01:13:28.240 | um so let's try popping something so if we pop something we should get the king of
01:13:37.120 | spades you would expect
01:13:38.160 | and we did so if we create a new deck here
01:13:49.760 | and pop it off
01:13:53.360 | again i would tend to turn this into a test
01:14:04.240 | there we go
01:14:26.640 | okay so let's export that make sure everything's running okay it is
01:14:33.920 | and so we can now do our preview again
01:14:55.600 | so we can now say we've got our index page our card page and our deck page are all here
01:15:17.920 | there's all our documentation
01:15:19.600 | so one thing you'll notice here is that the back tick we used here for card
01:15:29.200 | has automatically become a hyperlink here
01:15:35.440 | to the documentation for that um i won't be able to click on it right now because it's um
01:15:43.520 | okay because we haven't put it on github yet um but this will end this will link to
01:15:48.320 | in effect i just copy the link you'll get the idea copy link address paste it so that's where
01:15:54.800 | our documentation for that's going to end up being so you can just use back ticks and it will
01:15:59.760 | automatically linkify as we call it um that to the documentation
01:16:05.040 | so that's pretty handy
01:16:11.120 | and it works across all your bdev projects uh yeah because exactly we have yeah and not just
01:16:21.200 | mvdev projects in fact there's um there's a number of other things including the python standard
01:16:28.560 | library and pandas and numpy and so forth that you can install indexes for those so that they'll
01:16:36.080 | automatically linkify as well um cool so then you know we can show that this seems to be working
01:16:43.520 | correctly um that's going to rerun this okay so um so just rerun that from the top to make
01:16:55.920 | that all work again um all right so that should be um
01:17:05.200 | in our deck now you can see that that's auto generated that documentation
01:17:23.600 | and we can also like check that pops behaving the way we'd expect it to be
01:17:27.600 | by getting a remove in there as well
01:17:39.760 | so i'm just going to copy and paste this one from when i built before to save a bit of time for us
01:17:52.080 | and so again we can check to see how the preview is looking
01:18:10.080 | and so when we see this is not appearing here we know what we forgot to export it
01:18:16.240 | there we go now it's appeared
01:18:42.240 | so it's not just for methods we could also create a function so i've got a function that i've created
01:18:45.840 | earlier just for example
01:18:58.240 | so here's one that draws some cards with all our replacement
01:19:08.240 | so let's draw
01:19:14.720 | that in cards
01:19:18.240 | without replacement
01:19:25.520 | oh needed a shuffle about to add a shuffle
01:19:37.040 | if we just put that straight into the class just to show how that would look there's a shuffle
01:19:43.120 | so since the shuffle here is directly in the class we need to say whereabouts we want to
01:19:50.160 | document it so the way we would do that would be to put a show doc
01:19:54.480 | there we go um
01:20:10.960 | so we're going to need to import random here
01:20:24.960 | let me just put it up
01:20:29.360 | oh one thing i should mention it's okay to put an import statement in an exported cell
01:20:34.560 | because they're going to be run either way so that's the one difference is you can put
01:20:38.160 | an import inside a cell that's exported there we go
01:20:43.520 | let's take a look at how our documentation is looking
01:20:54.640 | nice all right ammo where should we head now um oh um
01:21:03.840 | did we want to create the command line interface
01:21:08.240 | yeah sure are you want to do a visualization to make sure your
01:21:15.280 | draws are doing or okay um i mean that's already the tutorial it's not really anything
01:21:21.040 | extra okay no problem yeah i mean the ci lies in the actually the ci lies in the written
01:21:25.520 | new tutorial as well it's also really not anything extra so because this is already pretty long um
01:21:30.880 | and it's not really mv dev specific so yeah check out the um the the written version of
01:21:37.520 | the tutorial of this online for a couple more um handy little tricks um
01:21:49.360 | okay so
01:21:51.360 | to get it ready to send off to github
01:21:56.960 | to send it up to github one thing that i like to do is to make sure that there's like no unnecessary
01:22:07.360 | metadata in the notebooks and so um you can manually check do that by using nb dev clean
01:22:18.000 | there's also some githubs you can install to do this automatically you can look up in the docs
01:22:21.760 | if you're interested um and so if you um have a look now we can see what's what we've got
01:22:33.680 | ready to go to github um so we can add all that um yeah not a bad idea just to
01:22:45.520 | make sure you've exported everything um
01:22:49.120 | make sure that the tests are working
01:22:53.280 | so they're basically the three things uh check the files so there's going to be
01:23:00.720 | continuous integration added doc documentation deployment added uh the notebooks we created
01:23:10.560 | some information about the website our home page um the two modules we created
01:23:18.080 | our settings file setup file sidebar and a style sheet that all sounds good to me
01:23:27.760 | too tired today
01:23:35.600 | okay so that gets sent off to github where we should find it
01:23:42.480 | there it is and so github has something called github actions that automatically
01:23:53.840 | runs things that are in this github github workflows folder um so one thing you'll find here
01:24:02.560 | is that when you push or when somebody puts in a pull request um it's going to
01:24:09.520 | run nb dev test amongst other things and it will also
01:24:18.320 | uh run something called quarter ghp which will set up your github pages so while we wait for that
01:24:29.520 | i'm going to go into pages and tell it that gh pages is the branch so that's
01:24:38.960 | basically what it uses for github pages um
01:24:45.360 | okay and so if we look at actions we can see there we are it's that's all run
01:24:55.120 | so the ci sensor continuous integrations that means our tests are passed and the deployment
01:25:01.040 | was completed so you can actually click on these things to see them or the steps that they run
01:25:07.600 | and you'll see that installs all the python stuff and you can see it
01:25:13.680 | basically looks the same as our quarter did when we ran stuff locally
01:25:19.680 | and then finally automatically github creates our web page for us
01:25:25.280 | and so you can see it's visually building it there looks like it's finished building it
01:25:30.720 | okay it's finished and you can see it tells us here where it's been built too
01:25:37.600 | so what i like to do is i tend to copy that
01:25:39.680 | and then i go into my settings
01:25:47.600 | and paste it here
01:25:54.000 | there's our website awesome now links are working
01:26:14.080 | okay so that's looking good um shall we put this on pai pai hamel yeah
01:26:22.480 | you know and that ci i mean
01:26:28.400 | i just want to point out like most projects at this point you would only just have code
01:26:35.440 | so at this point we have ci we have a documentation site and this is the point
01:26:43.600 | where you know when i'm personally coding and i'm kind of down at this point and it's exciting
01:26:48.720 | because i just like my colleague you know i just say hey like i created that tool you wanted
01:26:53.680 | and then here's the website and they're like you have a website i'm like yeah check it out
01:27:00.480 | you can install it yeah yeah and then they're like wow it's and then also you can just pip
01:27:06.160 | install it and also you have ci like how did you do all this such a short amount of time
01:27:12.560 | yeah it's awesome
01:27:20.720 | our readme is not working oh you know the other thing we should have done is run nb dev docs
01:27:36.240 | and um the reason for that is that um so that's kind of does a trial run of the same thing that
01:27:46.880 | um that it's going to have that github actions is going to do um but what it also does
01:27:54.720 | is it updates our readme to contain the same contents as the homepage does
01:28:02.800 | um keep forgetting i haven't got this alias installed
01:28:10.240 | so if we now look the other thing which we've got is
01:28:26.160 | a beautiful readme uh so that comes from our index.ipy and b and it's going to be the same as
01:28:33.600 | the home page it won't be as beautiful as the home page because it's using more limited markdown but
01:28:37.840 | the basic you know the basics are there um okay so we can see that there's an nb dev pipy to upload to
01:28:51.680 | pipy and there's a another one for conda doing conda or just nb dev release to do both
01:28:58.960 | i'm just going to do pipy for now all right so let's run it
01:29:04.080 | and it uploads to pipy
01:29:09.680 | and just at the end it's also automatically bumped the version
01:29:20.640 | so you can see here it's gone to version 2
01:29:22.400 | so i tend to like just go git commit -am bump
01:29:33.520 | and it says i can view it here
01:29:48.320 | there it is and you can see it's even put the project description project home page
01:30:01.840 | it's all been done automatically which i think is all the metadata it looks like a
01:30:10.800 | very professional and polished library for something that we spent under two hours on
01:30:16.880 | um absolutely and and furthermore like it's
01:30:20.800 | you know it's not just for little quick two-hour things like this you know i've
01:30:27.600 | well you and i have both written libraries that have taken years
01:30:31.440 | with thousands of lines of code and dozens of modules and tens or hundreds of thousands of users
01:30:36.800 | yeah no it's great and imagine what it's like to make a pr into one of these projects so
01:30:44.480 | let's just say i don't have any idea how deck works at all i'm like what is this deck thing
01:30:50.320 | i've never seen it i don't know and um without knowing anything about the code i just go to that
01:30:57.520 | notebook and there's all there's already the entry point for me there's the code and then there's
01:31:02.080 | sample based on how to run it with documentation right there so there's no confusion and then i can
01:31:09.760 | you know i've noticed that we got a lot of pull requests with just high quality ones making the
01:31:14.560 | documentation better making the tests better because people read it they get confused and
01:31:19.920 | they just resolve their confusion they just say i'm just going to edit this real quick and submit
01:31:24.160 | a pr yeah i mean let's let's have a look at an example so yeah fastai is an example of a package
01:31:29.680 | which uses mbdev it's got over 20 000 stars on github over 7 000 forks so here's an example of
01:31:39.440 | an open pr and one thing that we'd use which is fine is there's a thing called review nb
01:31:44.640 | it actually means i can click a button here and because it's yeah because it's all in notebooks
01:31:50.160 | you know i can immediately see the documentation that i've added and i can make comments on the
01:31:56.480 | documentation and if you know i can see the source code that's been changed i can see if
01:32:02.240 | they've added or removed any tests i can see if any of the output graphs or whatever have changed
01:32:08.560 | and yeah it's it's it's nice both for me as the person reviewing that pr and also as you say for
01:32:19.200 | the person making that pr that we're very much all on the same page about what's about what's
01:32:25.200 | going on so yeah i find we don't get pretty pretty high quality pull requests to our projects
01:32:30.320 | and what's great is so this is only kind of the tip of the iceberg like there's a lot of exciting
01:32:38.160 | things so you can use the same tools that we showed today to just make a website let's say
01:32:44.640 | you don't want to necessarily write code maybe you want to just write a blog so we'll have
01:32:50.960 | a tutorial or video like at some time in the near future that shows okay how do you do write a blog
01:32:56.720 | and then also you can just have a website too that maybe you just want to make a tutorial on
01:33:02.000 | let's say how to use fastai or how to use your favorite library you can use same set of tools to
01:33:06.480 | do that and there's all kinds of advanced stuff that you could do too like quartile is very
01:33:11.920 | powerful you can make books from it slides all kinds of different formats and so it's the same
01:33:19.200 | you know for making any kind of technical content uh as well so yeah it's it's really exciting yeah
01:33:25.440 | and it's a whole different philosophy of how to write software um and uh yeah i think it's
01:33:31.760 | you and i've both got the experience now that it's made us more productive and we're having
01:33:36.080 | more fun so it's good to be doing this tutorial to think about people watching it that are going to
01:33:40.880 | join this journey as well so thanks everybody for watching and hamil thanks so much for joining in
01:33:47.120 | the tutorial um late at night before you're doing a keynote tomorrow i really appreciate it all right
01:33:54.480 | that's good bye thank you