{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# End-To-End Walkthrough\n",
    "\n",
    "> A step-by-step guide to using nbdev\n",
    "- order: 1\n",
    "- image: ../images/card.png\n",
    "- aliases: [/tutorial.html]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "hide_input": false
   },
   "outputs": [],
   "source": [
    "#|hide\n",
    "from nbdev.showdoc import show_doc\n",
    "from nbdev.qmd import div"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The written tutorial below shows you how to create a Python package from scratch using nbdev.\n",
    "\n",
    "Alternatively, you can watch this video tutorial where Jeremy Howard and Hamel Husain guide you through a similar process step by step:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.text-center}\n",
    "\n",
    "<iframe width=\"560\" height=\"315\" src=\"https://www.youtube-nocookie.com/embed/l7zS8Ld4_iA\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen style=\"max-width: 100%; margin: auto\" class=\"rounded\"></iframe>\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Installation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You'll need the following software to complete the tutorial, read on for specific installation instructions:\n",
    "\n",
    "1. Python\n",
    "2. A Python package manager: we recommend conda or pip\n",
    "3. Jupyter Notebook\n",
    "4. nbdev\n",
    "5. Quarto\n",
    "\n",
    "If you haven't worked with Python before, we recommend getting started with the [Anaconda Individual Edition](https://www.anaconda.com/products/individual) and using the conda package manager."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install Jupyter Notebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Launch a terminal and install Jupyter Notebook by entering:\n",
    "\n",
    "```sh\n",
    "conda install notebook\n",
    "```\n",
    "\n",
    "...or\n",
    "\n",
    "```sh\n",
    "pip install notebook\n",
    "```\n",
    "\n",
    "...if you're using the pip package manager.\n",
    "\n",
    "Enter `y` (for yes) if prompted. Installation should take a few seconds, during which text will be printed in the terminal. You'll know its completed when you see the terminal prompt and are able to type again.\n",
    "\n",
    "You can now launch Jupyter by entering:\n",
    "\n",
    "```sh\n",
    "jupyter notebook\n",
    "```\n",
    "\n",
    "This should open the Jupyter home page in a new browser tab:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/jupyter-home-page.png){.px-1 .py-2 fig-align=\"center\" .border .rounded .shadow-sm}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-tip collapse=\"true\"}\n",
    "\n",
    "#### Why not Jupyter Lab?\n",
    "\n",
    "As Jupyter power users we still prefer Jupyter Notebook (with [customizations](#install-collapsible-headings-and-toc2)) over more feature-full alternatives like Jupyter Lab, VSCode, or PyCharm. We find Jupyter Notebook simpler, faster, and more robust.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install nbdev"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to install nbdev itself. Jupyter Notebook comes with its own terminal, so we'll use that moving forward.\n",
    "\n",
    "In the Jupyter home page (shown in the previous section), click the \"New\" dropdown on the right side, then \"Terminal\".\n",
    "\n",
    "A browser tab should open with a blank terminal:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/jupyter-blank-terminal.png){.pt-2 fig-align=\"center\" .border .rounded .shadow-sm}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For Mac and Linux, enter:\n",
    "\n",
    "```sh\n",
    "conda install -c fastai nbdev\n",
    "```\n",
    "\n",
    "...or for Mac, Linux and Windows:\n",
    "\n",
    "```sh\n",
    "pip install nbdev\n",
    "```\n",
    "\n",
    "...if you're using pip.\n",
    "\n",
    "Type `y` (for yes) when prompted, and wait a few seconds until nbdev is installed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install Quarto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "nbdev provides a command to install the latest version of Quarto. In the terminal, enter:\n",
    "    \n",
    "```sh\n",
    "nbdev_install_quarto\n",
    "```\n",
    "\n",
    "Your password may be requested at this point. Since nbdev is open source, you can read [the source code](https://github.com/fastai/nbdev/blob/master/nbdev/quarto.py) of this command to verify that it isn't doing anything malicious. Or, if you prefer, you may instead follow [Quarto's official installation instructions](https://quarto.org/docs/get-started/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## First steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By the end of this section you'll have your own nbdev repo with tests, continuous integration, streamlined PyPI & conda packaging, and a documentation website."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create an empty GitHub repo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create an empty GitHub repo using the convenient link [github.com/new](https://github.com/new). If you get stuck, you might find GitHub's [_Create a repo_](https://docs.github.com/en/get-started/quickstart/create-a-repo) page helpful.\n",
    "\n",
    "Remember to add a description, since nbdev will use that later. Don't add a README file, .gitignore, or license file just yet.\n",
    "\n",
    "If you're using the web interface, it should look something like this before you click \"Create Repository\":"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/github-create-new-repo.png){.p-2 .pb-3 fig-align=\"center\" .border .rounded .shadow-sm width=600px}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You should then be redirected to your new repo:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/github-repo-empty.png){.pb-2 fig-align=\"center\" .border .rounded .shadow-sm}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-tip collapse=\"true\"}\n",
    "\n",
    "#### Try GitHub's powerful CLI\n",
    "\n",
    "GitHub's web interface is a great way to get started. As you grow more experienced, you might want to explore [the GitHub CLI](https://github.com/cli/cli) (command line interface). We often prefer to use command line tools for repetitive tasks where we're likely to make mistakes. Having those tasks written as small scripts in your terminal means that you can repeat them with little effort.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Initialise your repo with nbdev"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now clone your repo from the Jupyter terminal you started [earlier](#install-nbdev) (or create a new terminal following those instructions if needed). If you get stuck here, you might find GitHub's [_Cloning a repository_](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) page helpful."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since we created a repo named `nbev-hello-world` with the `fastai` user, we can clone it as follows:\n",
    "\n",
    "```sh\n",
    "git clone https://github.com/fastai/nbdev-hello-world.git\n",
    "```\n",
    "\n",
    "Then `cd` (change directory) to our repo:\n",
    "\n",
    "```sh\n",
    "cd nbdev-hello-world\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You may have seen this message while cloning:\n",
    "\n",
    "```\n",
    "You appear to have cloned an empty repository.\n",
    "```\n",
    "\n",
    "...since the repo is completely empty. Let's add some files!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "nbdev provides the `nbdev_new` command to initialise an empty git repository. It'll infer information about your project from git and GitHub, and ask you to input anything remaining. It will create files in your repo that:\n",
    "\n",
    "- Streamline publishing Python packages to PyPI and conda.\n",
    "- Configure Quarto for publication-grade technical documentation.\n",
    "- Setup GitHub actions to test notebooks and build and deploy Quarto docs to GitHub pages."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Initialise your nbdev repo by entering:\n",
    "\n",
    "```sh\n",
    "nbdev_new\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It may ask you to enter information that it couldn't infer from git or GitHub."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-note}\n",
    "\n",
    "`nbdev_new` assumes that your package name is the same as your repo name (with `-` replaced by `_`). Use the `--lib_name` option if that isn't the case.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Double-check your `settings.ini` file to ensure that it has all of the correct information. Then commit and push your additions to GitHub:\n",
    "\n",
    "```sh\n",
    "git add .\n",
    "git commit -m'Initial commit'\n",
    "git push\n",
    "```\n",
    "\n",
    "\n",
    "### Enable GitHub Pages\n",
    "\n",
    "You need to enable GitHub Pages for your repo by clicking on the \"Settings\" tab near the top-right of your repo page, then \"Pages\" on the left, then setting the \"Branch\" to \"gh-pages\", and finally clicking \"Save\".\n",
    "\n",
    "It should look similar to this after you click \"Save\":\n",
    "\n",
    "![](images/github-enable-pages.png){.pb-2 fig-align=\"center\" .border .rounded .shadow-sm}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now it's time to see all of the goodies nbdev gives you!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Check out your workflows"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Open GitHub Actions by clicking the \"Actions\" tab near the top of your repo page. You should see two workflow runs:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/github-actions-initial.png){fig-align=\"center\" .border .rounded .shadow-sm}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you opened this page shortly after pushing your initial commit, the runs may not have a green check (✅) because they're still \"In progress\" or \"Queued\". That's no problem, they shouldn't take much more than a minute to complete."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you see a red cross (❌), that means something failed. Click on the cross, then click *Details*, and you'll be able to see what failed. If you can't figure out what's wrong, search [the forum](https://forums.fast.ai/c/nbdev/48) in case someone else resolved the same issue, otherwise create a new post describing your issue in as much detail as you can, and we'll try our best to help you. Remember that including a link to an actual repo and/or GitHub Action is the best way for us to quickly identify what's wrong."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What do these workflows do?\n",
    "\n",
    "- **CI** -- The CI (continuous integration) workflow streamlines your developer workflow, particularly with multiple collaborators. Every time you push to GitHub, it ensures that:\n",
    "    - Your notebooks and libraries are in sync\n",
    "    - Your notebooks are cleaned of unwanted metadata (which pollute pull requests and git histories and lead to merge conflicts)\n",
    "    - Your notebook tests all pass\n",
    "- **Deploy to GitHub Pages** -- Builds your docs with Quarto and deploys it to GitHub Pages."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We provide these basic workflows out-of-the-box, however, you can edit their corresponding YAML files in the `.github/workflows/` folder to your liking."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that you'll need to enable GitHub Pages for your repo before you can access your docs website. We'll do that now."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Check out your docs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "nbdev hosts your docs on GitHub Pages---an excellent (and free!) way to host websites. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Head back to GitHub Actions and you should see a new workflow run: \"pages build and deployment\". As the name says, this workflow deploys your website contents to GitHub Pages."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/github-actions-pages.png){fig-align=\"center\" .border .rounded .shadow-sm}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Wait for the workflow run to complete, then open your website. By default it should be available at: `https://{user}.github.io/{repo}`. For example, you can view `fastai`'s `nbdev-hello-world` docs at <https://fastai.github.io/nbdev-hello-world>."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/nbdev-hello-world-site-initial.png){fig-align=\"center\" .border .rounded .shadow-sm}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-note}\n",
    "\n",
    "nbdev uses GitHub Pages by default because its easily accessible. However, you can use any host you like.  See [these docs](../explanations/docs.ipynb#deploying-your-docs-on-other-platforms) for more information on this.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recap"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You now have a base nbdev repo with continuous integration and hosted documentation! Here's a recap of the steps you took:\n",
    "\n",
    "- Created a GitHub repo\n",
    "- Initialised your repo with `nbdev_new`\n",
    "- Pushed to GitHub."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Make your first edit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this section, you'll make your first edit to the repo you created in [_First steps_](#first-steps)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install hooks for git-friendly notebooks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Step one when working with Jupyter notebooks in a new repo is to install nbdev's hooks (you can think of \"hooks\" as plugins or extensions to an application).\n",
    "\n",
    "Install them by entering this command in your terminal:\n",
    "\n",
    "```sh\n",
    "nbdev_install_hooks\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-note}\n",
    "\n",
    "The [clean hook](#nbdev_clean-on-saving-notebooks-in-jupyter) currently only supports Jupyter Notebook and Jupyter Lab. If you're using another notebook editor, like VSCode or PyCharm, you might want to use [nbdev's pre-commit hooks](/tutorials/pre_commit.ipynb) as well.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "See [_Git-friendly Jupyter_](git_friendly_jupyter.html) for more about how nbdev hooks work and how to customise them. Here's a short summary:\n",
    "\n",
    "- Fix broken notebooks due to git merge conflicts so that they can be opened and resolved directly in Jupyter.\n",
    "- Each time you save a Jupyter notebook, automatically clean unwanted metadata to remove unnecessary changes in pull requests and reduce the chance of git merge conflicts.\n",
    "- Automatically trust notebooks in the repo so that you can view widgets from collaborators' commits. For this reason, **you should not install hooks into a repo you don't trust**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-tip}\n",
    "\n",
    "nbdev hooks works on _any_ git repo, even if it doesn't use the broader nbdev system.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Build your library"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You should now create your package from your notebook by running:\n",
    "\n",
    "```shell\n",
    "nbdev_export\n",
    "```\n",
    "\n",
    "This will create Python modules for your notebooks. These modules will make up the contents of your Python package."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install your package"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You might have noticed that `nbdev_new` created a Python package in your repo. In our case, it was automatically named `nbdev_hello_world` by using our repo name `nbdev-hello-world` and replacing `-` with `_` to make it a valid Python package."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to install your package by entering this into your terminal:\n",
    "\n",
    "```sh\n",
    "pip install -e '.[dev]'\n",
    "```\n",
    "\n",
    "This is the recommended way to make a Python package importable from anywhere in your current environment:\n",
    "\n",
    "- `-e` -- short for \"editable\", lets you immediately use changes made to your package during development.\n",
    "- `.` -- refers to the current directory.\n",
    "- `[dev]` -- includes \"development\" requirements: other packages that your notebooks use solely for documentation or testing."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Preview your docs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "nbdev is an interactive programming environment that values fast feedback loops. The `nbdev_preview` command helps achieve this by using Quarto to render your docs on your computer and keep them updated as your edit your notebooks."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Start the preview by entering this into your terminal:\n",
    "\n",
    "```sh\n",
    "nbdev_preview\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It may say `Preparing to preview` for a few seconds while it gets started, and will eventually display something like:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Watching files for changes\n",
    "Browse at http://localhost:3000/\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Click the link to open the preview in a new browser tab. It should look exactly like your online docs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-tip}\n",
    "\n",
    "We often find it useful to keep a preview window open on the side while we're editing our notebooks in Jupyter.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Edit 00_core.ipynb"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, run `jupyter notebook`, and click `00_core.ipynb` (note: if you started this tutorial using `nbdev_new` then this file will be in the `nbs` folder). You don't *have* to start your notebook names with a number, but we find it helpful to show the order that your project should be read in -- even though it could have been created in a different order."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Add your own frontmatter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You'll see something that looks a bit like this:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.py-2 .px-3 .mb-4 fig-align=\"center\" .border .rounded .shadow-sm}\n",
    "\n",
    "**core**\n",
    "\n",
    "> Fill in a module description here\n",
    "\n",
    "```python\n",
    "#| default_exp core\n",
    "```\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's explain what these special cells means:\n",
    "\n",
    "- The first is a markdown cell with nbdev's markdown _frontmatter_ syntax that defines notebook metadata used by Quarto, our documentation engine (see the [frontmatter](/api/09_frontmatter.ipynb) reference page for more). It contains:\n",
    "    - H1 header (\"core\") -- defining the page title\n",
    "    - Quote (\"Fill in a module description here\") -- defining the page description\n",
    "- The second is a code cell with a _directive_ `default_exp` which decides which module this notebook will export to (see the [Directives](/explanations/directives.ipynb) explanation for more). Currently, it exports to the `core` module."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, rename the notebook, replace the title and description, and change the default export module for your own project."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once you're done, save the notebook. The live preview started in the previous section should update with your latest changes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Rerun all cells in your notebook to ensure that they work, and to export the updated modules."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-tip}\n",
    "\n",
    "We find the \"restart kernel and run all cells\" Jupyter command (the ⏩ button) so invaluable that we bind it to a keyboard shortcut. A common criticism of notebooks is that out-of-order execution leads to irreproducible notebooks. In our experience, making \"restart and rerun\" a habit solves this problem.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running the notebook exports Python modules because of the last cell which contains:\n",
    "\n",
    "```python\n",
    "#| hide\n",
    "import nbdev; nbdev.nbdev_export()\n",
    "```\n",
    "\n",
    "What does this mean?\n",
    "\n",
    "- `#| hide` is a directive (like `#| default_exp`) which excludes a cell from both your exported module and docs\n",
    "- `nbdev_export` is the command used to export your notebooks to Python modules.\n",
    "\n",
    "We recommend including a cell like this at the bottom of all of the notebooks you want to export."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "::: {.callout-warning}\n",
    "\n",
    "Remember to delete any unused modules that aren't exported by a notebook or otherwise needed by your package. This is likely to happen if you change the default export of a notebook -- nbdev doesn't remove the old module. This is intended, since nbdev is designed to work with hybrid packages that use .py modules (with no corresponding notebook) as well as those exported from notebooks.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Add your own function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Add a new code cell below the `#| default_exp` cell with a function. For example:\n",
    "\n",
    "```python\n",
    "#| export\n",
    "def say_hello(to):\n",
    "    \"Say hello to somebody\"\n",
    "    return f'Hello {to}!'\n",
    "```\n",
    "\n",
    "Notice how it includes `#| export` at the top -- this is a directive (like `#| default_exp`) that tells nbdev to include the cell in your exported module and in your documentation.\n",
    "\n",
    "The documentation should look like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "::: {.py-2 .px-3 .mb-4 .border .rounded .shadow-sm}\n",
      "\n",
      "---\n",
      "\n",
      "[source](https://github.com/fastai/nbdevblob/master/nbdev/tutorial.py#L7){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
      "\n",
      "### say_hello\n",
      "\n",
      ">      say_hello (to)\n",
      "\n",
      "Say hello to somebody\n",
      "\n",
      ":::\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "#| echo: false\n",
    "#| output: asis\n",
    "def say_hello(to):\n",
    "    \"Say hello to somebody\"\n",
    "    return f'Hello {to}!'\n",
    "\n",
    "print(div(show_doc(say_hello)._repr_markdown_(),\n",
    "          classes='py-2 px-3 mb-4 border rounded shadow-sm'.split()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Add your own examples, tests, and docs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One of the superpowers of notebook-driven development is that you can very easily add examples, tests, and documentation right below your code."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Include regular code cells, and they'll appear (with output) in your docs, for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello Isaac!'"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "say_hello(\"Isaac\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a test too! When you run `nbdev_test` it will execute this cell (and all other test cells) and fail if they raise any exceptions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For tests, it's preferred to use more explicit `assert`s:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert say_hello(\"Hamel\")==\"Hello Hamel!\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "...or functions from [`fastcore.test`](https://fastcore.fast.ai/test.html), which behave like `assert` but also display the actual and expected values if they differ:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fastcore.test import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_eq(say_hello(\"Hamel\"), \"Hello Hamel!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another superpower of notebook-driven development is that your examples can include plots, images, and even JavaScript widgets. For example, here's an SVG circle:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import display,SVG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"100\"><circle cx=\"50\" cy=\"50\" r=\"40\"/></svg>\n"
      ],
      "text/plain": [
       "<IPython.core.display.SVG object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(SVG('<svg height=\"100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"40\"/></svg>'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Prepare your changes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before commiting your changes to GitHub we recommend running `nbdev_prepare` in the terminal, which bundles the following commands:\n",
    "\n",
    "- `nbdev_export`: Builds the .py modules from Jupyter notebooks\n",
    "- `nbdev_test`: Tests your notebooks\n",
    "- `nbdev_clean`: Cleans your notebooks to get rid of extreanous output for Github\n",
    "- `nbdev_readme`: Updates README.md from your index notebook."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Edit index.ipynb"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now you're ready to create your documentation home page and README.md file; these are both generated automatically from index.ipynb. Open the Jupyter Notebook home page, then click on index.ipynb to open it."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We recommend including a longer description about what your package does, how to install it, and how to use it (with a few examples which import and use your package). Remember, examples can be code cells with real outputs rather than plain markdown text."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Push to Github"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can now commit and push your changes to GitHub. As we mentioned before, always remember to run `nbdev_prepare` before you commit to ensure your modules are exported and your tests pass. You can use `git status` to check which files have been generated or changed. Then:\n",
    "\n",
    "```sh\n",
    "git add .\n",
    "git commit -m'Add `say_hello`; update index' # Update this text with your own message\n",
    "git push\n",
    "```\n",
    "\n",
    "This will kick-off your GitHub Actions. Wait a minute or two for those to complete, then check your updated repo and documentation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recap"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Congratulations, you've used all of the basics needed to build delightful projects with nbdev! Here's a recap of the steps you took:\n",
    "\n",
    "- Installed hooks for git-friendly notebooks with `nbdev_install_hooks`\n",
    "- Installed your package with `pip install -e '.[dev]'`\n",
    "- Previewed your docs with `nbdev_preview`\n",
    "- Added your own frontmatter, function, tests, and docs to 00_core.ipynb\n",
    "- Prepared your changes with `nbdev_prepare`\n",
    "- Updated index.ipynb with your own information\n",
    "- Pushed to GitHub.\n",
    "\n",
    "Read on to learn about more advanced nbdev functionality. Also see our _Explanations_ in the sidebar on the left for deep-dives on specific topics."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Advanced functionality"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Add a class"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a class in `00_core.ipynb` as follows:\n",
    "\n",
    "```python\n",
    "#| export\n",
    "class HelloSayer:\n",
    "    \"Say hello to `to` using `say_hello`\"\n",
    "    def __init__(self, to): self.to = to\n",
    "        \n",
    "    def say(self):\n",
    "        \"Do the saying\"\n",
    "        return say_hello(self.to)\n",
    "```\n",
    "\n",
    "This will automatically appear in the docs like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/fastai/nbdevblob/master/nbdev/tutorial.py#L12){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### HelloSayer\n",
       "\n",
       ">      HelloSayer (to)\n",
       "\n",
       "Say hello to `to` using `say_hello`"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/fastai/nbdevblob/master/nbdev/tutorial.py#L12){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### HelloSayer\n",
       "\n",
       ">      HelloSayer (to)\n",
       "\n",
       "Say hello to `to` using `say_hello`"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class HelloSayer:\n",
    "    \"Say hello to `to` using `say_hello`\"\n",
    "    def __init__(self, to): self.to = to\n",
    "\n",
    "    def say(self):\n",
    "        \"Do the saying\"\n",
    "        return say_hello(self.to)\n",
    "\n",
    "show_doc(HelloSayer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Document with show_doc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, methods aren't automatically documented. To add method docs, use `show_doc`:\n",
    "\n",
    "```python\n",
    "show_doc(HelloSayer.say)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/fastai/nbdevblob/master/nbdev/tutorial.py#LNone){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### HelloSayer.say\n",
       "\n",
       ">      HelloSayer.say ()\n",
       "\n",
       "Do the saying"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/fastai/nbdevblob/master/nbdev/tutorial.py#LNone){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### HelloSayer.say\n",
       "\n",
       ">      HelloSayer.say ()\n",
       "\n",
       "Do the saying"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(HelloSayer.say)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And add some examples and/or tests:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello Alexis!'"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "o = HelloSayer(\"Alexis\")\n",
    "o.say()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Add links with backticks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice above there is a link from our new class documentation to our function. That's because we used backticks in the docstring:\n",
    "```python\n",
    "    \"Say hello to `to` using `say_hello`\"\n",
    "```\n",
    "These are automatically converted to hyperlinks wherever possible. For instance, here are hyperlinks to `HelloSayer` and `say_hello` created using backticks."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up autoreload"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since you'll be often updating your modules from one notebook, and using them in another, it's helpful if your notebook automatically reads in the new modules as soon as the Python file changes. To make this happen, just add these lines to the top of your notebook:\n",
    "\n",
    "```\n",
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up prerequisites"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If your module requires other modules as dependencies, you can add those prerequisites to your `settings.ini` in the `requirements` section. The requirements should be separated by a space and if the module requires at least or at most a specific version of the requirement this may be specified here, too.\n",
    "\n",
    "For example if your module requires the `fastcore` module of at least version 1.0.5, the `torchvision` module of at most version 0.7 and any version of `matplotlib`, then the prerequisites would look like this:\n",
    "```python\n",
    "requirements = fastcore>=1.0.5 torchvision<0.7 matplotlib\n",
    "```\n",
    "\n",
    "In addition to `requirements` you can specify dependencies with other keywords that have different scopes.  Below is a list of all possible dependency keywords:\n",
    "\n",
    "- `requirements`: Passed to both pip and conda setup\n",
    "- `pip_requirements`: Passed to pip setup only\n",
    "- `conda_requirements`: Passed to conda setup only\n",
    "- `dev_requirements`: Passed to pip setup as a development requirement\n",
    "\n",
    "For more information about the format of dependencies, see the pypi and conda docs on creating specifications in [setup.py](https://packaging.python.org/tutorials/packaging-projects/#creating-setup-py) and [meta.yaml](https://docs.conda.io/projects/conda-build/en/latest/resources/define-metadata.html), respectively."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up console scripts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Behind the scenes, nbdev uses that standard package `setuptools` for handling installation of modules. One very useful feature of `setuptools` is that it can automatically create [cross-platform console scripts](https://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-console-scripts-entry-point). nbdev surfaces this functionality; to use it, use the same format as `setuptools`, with whitespace between each script definition (if you have more than one).\n",
    "\n",
    "```\n",
    "console_scripts = nbdev_export=nbdev.cli:nbdev_export\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Upload to pypi"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you want people to be able to install your project by just typing `pip install your-project` then you need to upload it to [pypi](https://pypi.org/). The good news is, we've already created a fully pypi compliant installer for your project! So all you need to do is register at pypi (click \"Register\" on pypi) if you haven't previously done so, and then create a file called `~/.pypirc` with your login details. It should have these contents:\n",
    "\n",
    "```\n",
    "[pypi]\n",
    "username = your_pypi_username\n",
    "password = your_pypi_password\n",
    "```\n",
    "\n",
    "Another thing you will need is `twine`, so you should run once\n",
    "\n",
    "``` \n",
    "pip install twine\n",
    "```\n",
    "\n",
    "To upload your project to pypi, just type `nbdev_pypi` in your project root directory. Once it's complete, a link to your project on pypi is displayed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Upload to conda\n",
    "\n",
    "Similar to `pip install` support, we have provided an anaconda compliant installer to upload your project to [anaconda](https://anaconda.org). Once uploaded, your package can be installed by typing `conda install -c your_anaconda_username your-project`.\n",
    "\n",
    "You need to register at anaconda (fill out the form to `Sign Up`) which will create a username and password. You will then need to install the following packages\n",
    "\n",
    "```\n",
    "pip install anaconda-client conda-build conda-verify\n",
    "```\n",
    "\n",
    "Before running the anaconda uploader, you need to login to conda using the CLI command (you will be prompted to enter your username and password)\n",
    "\n",
    "```\n",
    "anaconda login\n",
    "```\n",
    "\n",
    "To upload to anaconda, just type `nbdev_conda` in your project root directory."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Upload to pypi and conda\n",
    "\n",
    "The command `nbdev_release_both` from the root of your nbdev repo will upload your project to both conda and pypi."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install collapsible headings and toc2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are two jupyter notebook extensions that I highly recommend when working with projects like this. They are:\n",
    "\n",
    "- [Collapsible headings](https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/nbextensions/collapsible_headings/readme.html): This lets you fold and unfold each section in your notebook, based on its markdown headings. You can also hit <kbd>left</kbd> to go to the start of a section, and <kbd>right</kbd> to go to the end\n",
    "- [TOC2](https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/nbextensions/toc2/README.html): This adds a table of contents to your notebooks, which you can navigate either with the `Navigate` menu item it adds to your notebooks, or the TOC sidebar it adds. These can be modified and/or hidden using its settings."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Math equation support"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "nbdev supports equations (using [Quarto](https://quarto.org/)). You can include math in your notebook's documentation using the following methods."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using `$$`, e.g.:\n",
    "\n",
    "```\n",
    "$$\\sum_{i=1}^{k+1}i$$\n",
    "```\n",
    "\n",
    "Which is rendered as:\n",
    "\n",
    "> $$\\sum_{i=1}^{k+1}i$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using `$`, e.g.:\n",
    "\n",
    "```\n",
    "This version is displayed inline: $\\sum_{i=1}^{k+1}i$ . You can include text before and after.\n",
    "```\n",
    "\n",
    "Which is rendered as:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> This version is displayed inline: $\\sum_{i=1}^{k+1}i$ . You can include text before and after."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For more information, see [the Quarto Docs](https://quarto.org/docs/visual-editor/technical.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Look at nbdev \"source\" for more ideas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Don't forget that nbdev itself is written in nbdev! It's a good place to look to see how fast.ai uses it in practice, and get a few tips. You'll find the nbdev notebooks here in the [nbs folder](https://github.com/fastai/nbdev/tree/master/nbs) on Github."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Quarto Features"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "nbdev supports most Quarto features.  We encourage you to read the [Quarto documentation](https://quarto.org/) to discover all the features available to you. For example, this is how you can [incorporate mermaid charts](https://quarto.org/docs/authoring/diagrams.html):\n",
    "\n",
    "```{mermaid}\n",
    "flowchart LR\n",
    "  A[Hard edge] --> B(Round edge)\n",
    "  B --> C{Decision}\n",
    "  C --> D[Result one]\n",
    "  C --> E[Result two]\n",
    "```\n",
    "\n",
    "Here is another example of using [Graphviz](https://quarto.org/docs/authoring/diagrams.html#graphviz):\n",
    "\n",
    "```{dot}\n",
    "graph G {\n",
    "  layout=neato\n",
    "  run -- intr;\n",
    "  intr -- runbl;\n",
    "  runbl -- run;\n",
    "  run -- kernel;\n",
    "  kernel -- zombie;\n",
    "  kernel -- sleep;\n",
    "  kernel -- runmem;\n",
    "  sleep -- swap;\n",
    "  swap -- runswap;\n",
    "  runswap -- new;\n",
    "  runswap -- runmem;\n",
    "  new -- runmem;\n",
    "  sleep -- runmem;\n",
    "}\n",
    "```\n",
    "\n",
    "It is worth taking a look at the documentation for [figures](https://quarto.org/docs/authoring/figures.html), [callouts](https://quarto.org/docs/authoring/callouts.html), [markdown](https://quarto.org/docs/authoring/markdown-basics.html), [widgets](https://quarto.org/docs/interactive/widgets/jupyter.html), [layouts](https://quarto.org/docs/interactive/layout.html), [conditional content](https://quarto.org/docs/authoring/conditional.html) and [quarto extensions](https://quarto.org/docs/extensions/) to name a few useful things we have encountered."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "jupytext": {
   "split_at_heading": true
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
