diff --git a/README.md b/README.md index 26ddab98d..8b7023008 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Getting started +Getting Started ================ diff --git a/nbs/_quarto.yml b/nbs/_quarto.yml index 0f1730556..ba5e59ba5 100644 --- a/nbs/_quarto.yml +++ b/nbs/_quarto.yml @@ -36,7 +36,7 @@ website: - text: "Tutorial" href: tutorials/tutorial.ipynb - text: "Blog" - href: https://www.fast.ai/2022/07/28/nbdev-v2/ + href: blog/index.qmd - text: "Help" menu: - text: "Report an Issue" diff --git a/nbs/api/migrate.ipynb b/nbs/api/migrate.ipynb index 1cc130080..2542f1139 100644 --- a/nbs/api/migrate.ipynb +++ b/nbs/api/migrate.ipynb @@ -280,7 +280,7 @@ "id": "6f768e05-5368-4a3d-be1d-ed40a4bdf8b1", "metadata": {}, "source": [ - "After migrating the notebook, the front matter is moved to a raw cell, and some of the fields are converted to be compliant with Quarto. Furthermore aliases may be added in order to prevent broken links:" + "After migrating the notebook, the front matter is moved to a raw cell, and some of the fields are converted to be compliant with Quarto. Furthermore, aliases may be added in order to prevent broken links:" ] }, { diff --git a/nbs/blog/index.qmd b/nbs/blog/index.qmd new file mode 100644 index 000000000..df63e6268 --- /dev/null +++ b/nbs/blog/index.qmd @@ -0,0 +1,12 @@ +--- +title: nbdev Blog +subtitle: News, tips, and commentary about all things nbdev +listing: + sort: "date desc" + contents: "posts" + sort-ui: false + filter-ui: false + categories: true + feed: true +page-layout: full +--- \ No newline at end of file diff --git a/nbs/blog/posts/2022-07-28-nbdev2/index.qmd b/nbs/blog/posts/2022-07-28-nbdev2/index.qmd new file mode 100644 index 000000000..1c78dc388 --- /dev/null +++ b/nbs/blog/posts/2022-07-28-nbdev2/index.qmd @@ -0,0 +1,161 @@ +--- +author: [Hamel Husain, Jeremy Howard] +date: 2022-07-28 +image: /images/card.png +description: Our favorite tool for software engineering productivity--nbdev, now re-written with Quarto +tags: [technical] +title: 'nbdev+Quarto: A new secret weapon for productivity' +--- + +_[Originally posted](https://www.fast.ai/2022/07/28/nbdev-v2/) on the fast.ai blog_ + +![A single notebook can create a python module, tests, CI, pypi/conda packages, and more.](/images/card.png) + +## Our new secret weapon for productivity + +Today we’re excited to announce that we’ve teamed up with Quarto to give [nbdev](https://nbdev.fast.ai) superpowers. nbdev offers Python programmers a common set of tools for using Jupyter notebooks to: + +* Write & distribute software packages +* Test code, and +* Author documentation and technical articles + +Although notebooks are already widely used for once-off exploratory work, it’s less well-known that they are perfectly capable of writing quality software. In fact, we’ve used nbdev for a wide range of software projects over the last three years, including [deep learning libraries,](https://github.com/fastai/fastai) [API clients](https://github.com/fastai/ghapi), [Python language extensions](https://github.com/fastai/fastcore), [terminal user interfaces](https://github.com/nat/ghtop), and more. We discovered that it is not only capable of writing great software but that it **has also increased our productivity by 300% or more**. With nbdev, developers simply write notebooks with lightweight markup and get high-quality documentation, tests, continuous integration, and packaging for free! Nbdev has allowed us to maintain and scale many[ open source projects](https://github.com/fastai). Pull requests are often accompanied by detailed documentation and tests–contributors simply write notebooks. + +This is why we’re excited to share nbdev v2. It’s rewritten from the ground up, with much-anticipated features including: + +* Interoperation with non-nbdev codebases for tasks like documentation +* Support for any static site generator +* Wide variety of output mediums such as blogs, papers, slides, and websites +* A faster Jupyter kernel, which also means faster tests +* Cleaner and more extensible API, which supports custom directives, custom module exporters, and more + +## nbdev in industry + +We have piloted nbdev at several companies. We were delighted to receive the following feedback, which fits our own experience using and developing nbdev: + + +::: {layout="[70,30]"} +**[David Berg](https://www.linkedin.com/in/david-j-berg/), on using nbdev for internal documentation at Netflix:** +"Prior to using nbdev, documentation was the most cumbersome aspect of our software development process… Using nbdev allows us to spend more time creating rich prose around the many code snippets guaranteeing the whole experience is robust. nbdev has turned what was once a chore into a natural extension of the notebook-based testing we were already doing." + +![](logo_netflix.png){fig-width="200" fig-align="center" fig-pos="center"} +::: + + +::: {layout="[70,30]"} +**[Erik Gaasedelen](https://news.ycombinator.com/item?id=25165306), on using nbdev in production at Lyft:** "I use this in production at my company. It’s an awesome tool… nbdev streamlines everything so I can write docs, tests, and code all in one place… The packaging is also really well thought out. From my point of view it is close to a Pareto improvement over traditional Python library development." + +![](logo_lyft.png){fig-width="200" fig-align="center"} +::: + +::: {layout="[70,30]"} +**[Hugo Bowne-Anderson](https://twitter.com/hugobowne), on using nbdev for [Outerbounds](https://outerbounds.com/docs):** "nbdev has transformed the way we write documentation. Gone are the days of worrying about broken code examples when our API changes or [due to] human errors associated with copying & pasting code into markdown files. The authoring experience of nbdev… [allows] us to write prose and live code in a unified interface, which allows more experimentation… On top of this, nbdev allows us to include unit tests in our documentation which mitigates the burden of maintaining the docs over time." + +![](logo_ob.png){fig-width="200" fig-align="center"} +::: + +::: {layout="[70,30]"} +**[Roxanna Pourzand](https://www.linkedin.com/in/roxanna-pourzand-445a3035/), on using nbdev for [Transform](https://docs.transform.co/):** "We’re so excited about using nbdev. Our product is technical so our resulting documentation includes a lot of code-based examples. Before nbdev, we had no way of maintaining our code examples and ensuring that it was up-to-date for both command inputs and outputs. It was all manual. With nbdev, we now have this under control in a sustainable way. Since we’ve deployed these docs, we also had a situation where we were able to identify a bug in one of our interfaces, which we found by seeing the error that was output in the documentation." + +![](logo_transform.png){width="200"} + +::: + +## What's nbdev? + +Nbdev embraces the dynamic nature of python and REPL-driven development in ways that traditional IDEs and software development workflows cannot. We thoroughly discussed the motivation, history, and goals of nbdev in this [initial launch post](https://www.fast.ai/2019/12/02/nbdev/#software-development-tools) three years ago. The creator of Jupyter, Fernando Pérez, told us: + + +> [Nbdev] should be celebrated and used a lot more - I have kept a tab with your original nbdev blog post open for months in Chrome because of how often I refer to it and point others to this work + +In short, nbdev embraces ideas from [literate programming](https://en.wikipedia.org/wiki/Literate_programming) and [exploratory programming](https://en.wikipedia.org/wiki/Exploratory_programming). These paradigms have been revisited in platforms like XCode [Playgrounds](https://en.wikipedia.org/wiki/Swift_Playgrounds) and languages like Smalltalk, LISP, and Mathematica. With nbdev, we sought to push these paradigms even further by enabling it for one of the most popular dynamic programming languages in the world: Python. + +![State of the Octoverse 2021, GitHub](lang_rank.png) + +Even though nbdev is most widely used in scientific computing communities due to its integration with Jupyter Notebooks, we’ve found that nbdev is well suited for a much wider range of software. We have used nbdev to write [deep learning libraries,](https://github.com/fastai/fastai) [API clients](https://github.com/fastai/ghapi), [python language extensions](https://github.com/fastai/fastcore),[terminal user interfaces](https://github.com/nat/ghtop), and more! + +_Hamel: When I use nbdev, my colleagues are often astounded by how quickly I can create and distribute high-quality python packages. I consider nbdev to be a superpower that allows me to create tests and documentation without any additional friction, which makes all of my projects more maintainable. I also find writing software with nbdev to be more fun and productive as I can iterate very fast on ideas relative to more traditional software engineering workflows. Lastly, with nbdev I can also use traditional text-based IDEs if I want to, so I get the best of both worlds._ + +## What we learned after three years of using nbdev + +While nbdev was originally developed to simplify the software development workflow for various [fast.ai projects](https://github.com/fastai), we found that users wanted to extend nbdev to: + +* Write and publish blog posts, books, papers, and other types of documents with Jupyter Notebooks +* Document existing codebases not written in nbdev +* Accommodate traditional Python conventions–for those constrained in how their code is organized and formatted +* Publish content using _any_ static site generator + +While we created projects such as [fastpages](https://github.com/fastai/fastpages) and [fastdoc](https://github.com/fastai/fastdoc) to accomplish some of these tasks, we realized that it would be better to have a single set of flexible tools to accomplish all of them. To this end, we were extremely excited to discover [Quarto](https://quarto.org/), an open-source technical publishing system built on pandoc. + +_Hamel: The more I used nbdev for creating Python modules, the more I wanted to use it for writing blogs and documenting existing codebases. The ability to customize the way notebooks are rendered (hiding vs. showing cells, stripping output, etc.), along with the facilities for including unit tests, made it my go-to authoring tool for all technical content. I’m excited that nbdev2 unlocks all of these possibilities for everyone!_ + + +## Enter Quarto: A pandoc super-processor + +[Quarto](https://quarto.org/) is a project that enables technical publishing with support for Jupyter Notebook, VSCode, Observable, and plaintext editors. Furthermore, Quarto enables the publishing of high-quality articles, reports, websites, and blogs in HTML, PDF, ePub, PowerPoint slides, and more. Quarto is maintained by [RStudio](https://www.rstudio.com/), a company with a long history of products supporting literate programming, such as RMarkdown and RStudio. + +Quarto is built on top of [Pandoc](https://pandoc.org/), a universal document converter that supports nearly any format you can think of. Pandoc achieves this seemingly magical feat by representing documents in a common abstract syntax tree (AST) that serves as the medium through which different formats can be translated. By extension, Quarto allows you to generate content in almost any format you wish! You can use [pandoc filters](https://pandoc.org/filters%202.html#summary) to modify the AST and the output format, which allows you to use any static site generator you want, and programmatically modify and generate content. + +Quarto allows you to [compose pandoc filters in a processing pipeline](https://quarto.org/docs/extensions/filters.html#activating-filters) and apply them to specific documents or entire projects. You can also [distribute filters as Quarto extensions](https://quarto.org/docs/extensions/filters.html#distribution), which makes Quarto extremely customizable. + +We also find Quarto compelling because user interfaces such as [comment directives](https://quarto.org/docs/reference/cells/cells-jupyter.html#overview) (comments that start with `#|`) correlate with nbdev. In fact, we even learned that nbdev inspired Quarto in this regard! In general, Quarto and nbdev share many goals, and the Quarto team has been incredibly responsive to our suggestions. For example, the ability to create [notebook filters](https://quarto.org/docs/extensions/nbfilter.html) to modify notebooks before rendering. Below is a screenshot of a Jupyter notebook rendered with Quarto and nbdev. + +![Quarto rendering a Jupyter notebook written with nbdev](nb_quarto.png) + +Finally, Quarto supports more programming languages than just Python and has been adding new features and fixing bugs at an impressive speed. This gives us confidence that we will be able to expand nbdev to support more use cases in the future. We discuss some of these future directions in the closing section. + +## A blazing fast notebook kernel: execnb + +A core component of nbdev is executing and testing notebooks programmatically. It is important that this notebook runner executes with minimal overhead to maintain our goal of providing a delightful developer experience. This is why we built [execnb](https://github.com/fastai/execnb), a lightweight notebook runner for Python kernels, which executes notebooks blazingly fast. Furthermore, execnb allows parameterized execution of notebooks. + +_Hamel: I have been an enthusiastic user of tools like papermill that programmatically run notebooks for use-cases like [creating dashboards](https://github.com/github/covid19-dashboard) or enabling [new kinds of machine learning workflows](https://outerbounds.com/blog/notebooks-in-production-with-metaflow). I believe execnb unlocks even more possibilities with its ability to inject arbitrary code at any place in a notebook, as well as the ability to pass callbacks that run before and/or after cells are executed. This opens up possibilities to create new types of workflows with notebooks that I am excited about exploring in the near future._ + +## Towards a dialect of python that embraces its dynamic nature + +One way to understand nbdev is part of an ecosystem that is designed to embrace Python’s dynamic properties for REPL-driven software engineering. [Similar to Clojure](https://clojure.org/guides/repl/enhancing_your_repl_workflow), our goal is to provide tools that remove all friction from using the REPL in your programming workflow. We believe that the REPL enhances developer workflows thanks to context-sensitive auto-completion, signature inspection, and documentation–all based on the actual state of your code, and none of which are available in IDEs that depend solely on static analysis. We have found that for this reason, nbdev, with its Jupyter notebook foundation, makes programming significantly more productive and enjoyable. + +Our efforts to support REPL-driven development and literate programming are not limited to nbdev. We maintain a number of libraries that extend python to bolster this programming experience. The most notable of these libraries is [fastcore](https://github.com/fastai/fastcore), which extends Python in terms of [testing](https://fastcore.fast.ai/test.html), [documenting code](https://fastcore.fast.ai/docments.html), [metaprogramming](https://fastcore.fast.ai/meta.html#Metaprogramming), [attribute helpers](https://fastcore.fast.ai/basics.html#Attribute-Helpers), [enhanced representations of objects](https://fastcore.fast.ai/basics.html#basic_repr), and notebook-friendly [patching](https://fastcore.fast.ai/basics.html#Patching). This [blog post](https://fastpages.fast.ai/fastcore/) offers a gentle introduction to fastcore. In addition to literate programming, fastcore encourages conventions such as brevity and efficient use of vertical space so you can accomplish more with significantly less code. For example, below is a simple decorator that enables notebook-friendly [patching](https://fastcore.fast.ai/basics.html#Patching): + + +![`@patch` decorator from fastcore](patch.png){fig-align="center" width="600"} + + +We believe that this combination of a new developer workflow (nbdev), Python extensions (fastcore), and associated norms form a new dialect of Python that is centered on leveraging its dynamic nature–in contrast to an ever-growing trend toward [static analysis](https://mypy.readthedocs.io/en/stable/). We suspect that this dialect of Python will be more productive for programmers in many scenarios. We are framing this ecosystem as a “dialect” as it is still very much Python and is approachable by anyone who is familiar with the language. Furthermore, despite nbdev’s notebook workflow, our tools generate plaintext modules that can be navigated and edited with text-based IDEs, allowing programmers to experience the best of both worlds, if they desire. + +_Hamel: I believe this framing of a Python dialect is key to properly understanding what nbdev is. While it may be tempting to get stuck on specific features or technical details of nbdev, it is useful to zoom out to understand the overall intent of creating a better workflow rather than conforming too rigidly to existing ones. A good analogy is TypeScript’s relationship with JavaScript: it is an extension of an existing programming language that supports a new way of programming. I encourage you to treat nbdev in a similar fashion: be willing to try new ways of programming and observe which tradeoffs resonate with you. At the very least, I believe nbdev is a fun way to experience a different way of writing software, which will broaden your horizons about programming in general, all without having to learn an entirely new programming language!_ + +## The future of nbdev + +While we are excited about nbdev2, we believe we have only scratched the surface of what’s possible. We are considering the following features: + +* Supporting more languages beyond Python, such as Julia, R and JavaScript +* Offering interfaces for executing parameterized notebooks that mimic Python scripts +* Extensions for more static site generators and filters +* Supporting alternate testing backends, such as pytest +* Supporting a greater number of docstring formats, such as [Google-style](https://google.github.io/styleguide/pyguide.html#381-docstrings) docstrings +* More options to use plain-text or human readable notebook backends other than JSON + +If you have interesting ideas about how nbdev can be extended, please drop and chat with us on [discord](https://discord.com/invite/xnpeRdg) or post a message in the [forums](https://forums.fast.ai/). + +## How you can get started with nbdev + +Our project’s website is at [nbdev.fast.ai](https://nbdev.fast.ai/), where we will be posting tutorials, examples, and more documentation in the coming days. + +## Thank You + +This new version of nbdev was a team effort by many wonderful people. We want to highlight two people who have made outstanding contributions: + +- [Wasim Lorgat](https://twitter.com/wasimlorgat) was instrumental across different areas, including significant contributions to fastcore, execnb, and nbdev, as well as the implementation of the new nbdev [home page](https://nbdev.fast.ai/). With Wasim's help, we were able to push nbdev to a new level of functionality and quality. + +- [JJ Allaire](https://en.wikipedia.org/wiki/Joseph_J._Allaire) is not only the CEO of RStudio but also the steward of Quarto. JJ was incredibly responsive and eager to work with us on nbdev and added many features to Quarto specifically with nbdev in mind, such as [notebook filters](https://quarto.org/docs/extensions/nbfilter.html). We were also astounded by the attention to detail and the pace at which bugs are addressed. This new version of nbdev would not have been possible without JJ's help, and we are excited to continue to work with him. + +We also want to thank the amazing fastai community, notably [Isaac Flath](https://twitter.com/isaac_flath), [Benjamin Warner](https://mobile.twitter.com/benjamin_warner) and [Zach Mueller](https://twitter.com/TheZachMueller) for their tireless work on this project. + +## A conversation with JJ Allaire + +To celebrate the launch of nbdev v2 and Quarto, Jeremy sat down with the CEO of Posit (previously known as RStudio, the company behind Quarto), JJ Allaire, to talk about software development, scientific publishing, R, Python, literate programming, and much more. + +
+ +
+ diff --git a/nbs/blog/posts/2022-07-28-nbdev2/lang_rank.png b/nbs/blog/posts/2022-07-28-nbdev2/lang_rank.png new file mode 100644 index 000000000..28826a51e Binary files /dev/null and b/nbs/blog/posts/2022-07-28-nbdev2/lang_rank.png differ diff --git a/nbs/blog/posts/2022-07-28-nbdev2/logo_lyft.png b/nbs/blog/posts/2022-07-28-nbdev2/logo_lyft.png new file mode 100644 index 000000000..170875541 Binary files /dev/null and b/nbs/blog/posts/2022-07-28-nbdev2/logo_lyft.png differ diff --git a/nbs/blog/posts/2022-07-28-nbdev2/logo_netflix.png b/nbs/blog/posts/2022-07-28-nbdev2/logo_netflix.png new file mode 100644 index 000000000..4659eda8a Binary files /dev/null and b/nbs/blog/posts/2022-07-28-nbdev2/logo_netflix.png differ diff --git a/nbs/blog/posts/2022-07-28-nbdev2/logo_ob.png b/nbs/blog/posts/2022-07-28-nbdev2/logo_ob.png new file mode 100644 index 000000000..c0fd56a6e Binary files /dev/null and b/nbs/blog/posts/2022-07-28-nbdev2/logo_ob.png differ diff --git a/nbs/blog/posts/2022-07-28-nbdev2/logo_transform.png b/nbs/blog/posts/2022-07-28-nbdev2/logo_transform.png new file mode 100644 index 000000000..f61024963 Binary files /dev/null and b/nbs/blog/posts/2022-07-28-nbdev2/logo_transform.png differ diff --git a/nbs/blog/posts/2022-07-28-nbdev2/nb_quarto.png b/nbs/blog/posts/2022-07-28-nbdev2/nb_quarto.png new file mode 100644 index 000000000..f58906959 Binary files /dev/null and b/nbs/blog/posts/2022-07-28-nbdev2/nb_quarto.png differ diff --git a/nbs/blog/posts/2022-07-28-nbdev2/patch.png b/nbs/blog/posts/2022-07-28-nbdev2/patch.png new file mode 100644 index 000000000..444ebce04 Binary files /dev/null and b/nbs/blog/posts/2022-07-28-nbdev2/patch.png differ diff --git a/nbs/blog/posts/2022-08-25-jupyter-git/friendly-conflict.png b/nbs/blog/posts/2022-08-25-jupyter-git/friendly-conflict.png new file mode 100644 index 000000000..8a8ab6ae8 Binary files /dev/null and b/nbs/blog/posts/2022-08-25-jupyter-git/friendly-conflict.png differ diff --git a/nbs/blog/posts/2022-08-25-jupyter-git/index.qmd b/nbs/blog/posts/2022-08-25-jupyter-git/index.qmd new file mode 100644 index 000000000..c4416dfb1 --- /dev/null +++ b/nbs/blog/posts/2022-08-25-jupyter-git/index.qmd @@ -0,0 +1,155 @@ +--- +author: Jeremy Howard +date: 2022-08-25 +image: unreadable-notebook.png +description: Previously, using git with Jupyter could create conflicts and break notebooks. With nbdev2, the problem has been totally solved. +tags: [technical] +title: The Jupyter+git problem is now solved +--- + +_[Originally posted](https://www.fast.ai/2022/08/25/jupyter-git/) on the fast.ai blog_ + +> Jupyter notebooks don’t work with git by default. With [nbdev2](https://nbdev.fast.ai/), the Jupyter+git problem has been totally solved. It provides a set of hooks which provide clean git diffs, solve most git conflicts automatically, and ensure that any remaining conflicts can be resolved entirely within the standard Jupyter notebook environment. To get started, follow the directions on [Git-friendly Jupyter](https://nbdev.fast.ai/01_Tutorials/02_git_friendly_jupyter.html). + +## The Jupyter+git problem + +[Jupyter notebooks](https://Jupyter.org/) are a powerful tool for scientists, engineers, technical writers, students, teachers, and more. They provide an ideal [notebook](https://en.wikipedia.org/wiki/Lab_notebook) environment for interactively exploring data and code, writing programs, and documenting the results as dashboards, books, or blogs. + +But when collaborating with others, this ideal environment goes up in smoke. That's because tools such as git, which are the most popular approaches for asynchronous collaboration, makes notebooks unusable. Literally. Here's what it looks like if you and a colleague both modify a notebook cell (including, in many cases, simply executing a cell withuout changing it), and then try to open that notebook later: + +![What merge conflicts normally do to Jupyter Notebooks](unreadable-notebook.png) + +The reason for this stems from a fundamental incompatibility between the format Jupyter notebooks use (JSON) and the format that git conflict markers assume by default (plain lines of text). This is what it looks like when git adds its conflict markers to a notebook: + +::: {.pt-3 .pb-1 .px-3 .mt-2 .mb-4 .border .rounded .shadow-sm} + +``` + "source": [ +<<<<<< HEAD + "z=3\n", +====== + "z=2\n", +>>>>>> a7ec1b0bfb8e23b05fd0a2e6cafcb41cd0fb1c35 + "z" + ] +``` + +::: + +That's not valid JSON, and therefore Jupyter can't open it. Conflicts are particularly common in notebooks, because Jupyter changes the following every time you run a notebook: + +- Every cell includes a number indicating what order it was run in. If you and a colleague run the cells in different orders, you'll have a conflict in every single cell! This would take a very long time to fix manually +- For every figure, such as a plot, Jupyter includes not only the image itself in the notebook, but also a plain text description that includes the `id` (like a memory address) of the object, such as ``. This changes every time you execute a notebook, and therefore will create a conflict every time two people execute this cell +- Some outputs may be non-deterministic, such as a notebook that uses random numbers, or that interacts with a service that provides different outputs over time (such as a weather service) +- Jupyter adds metadata to the notebook describing the environment it was last run in, such as the name of the kernel. This often varies across installations, and therefore two people saving a notebook (even without and other changes) will often end up with a conflict in the metadata. + +All these changes to notebook files also make git diffs of notebooks very verbose. This can make code reviews a challenge, and make git repos more bulky than necessary. + +The result of these problems is that many Jupyter users feel that collaborating with notebooks is a clunky, error-prone, and frustrating experience. (We've even seen people on social media describe Jupyter's notebook format as "stupid" or "terrible", despite otherwise professing their love for the software!) + +It turns out, however, that Jupyter and git can work together extremely well, with none of the above problems at all. All that's needed is a bit of special software... + +## The solution + +Jupyter and git are both well-designed software systems that provide many powerful extensibility mechanisms. It turns out that we can use these to fully and automatically solve the Jupyter+git problem. We identified two categories of problems in the previous section: + +1. git conflicts lead to broken notebooks +2. Unnecessary conflicts due to metadata and outputs. + +In our newly released [nbdev2](https://nbdev.fast.ai/), an open source Jupyter-based development platform, we've solve each of the problems: + +1. A new *merge driver* for git provides "notebook-native" conflict markers, resulting in notebooks that can be opened directly in Jupyter, even when there are git conflicts +2. A new *save hook* for Jupyter automatically removes all unnecessary metadata and non-deterministic cell output. + +Here’s what a conflict looks like in Jupyter with nbdev’s merge driver: + +::: {.pt-3 .pb-1 .px-3 .mt-2 .mb-4 .border .rounded .shadow-sm} + +![](friendly-conflict.png) + +::: + +As you see, the local and remote change are each clearly displayed as separate cells in the notebook, allowing you to simply delete the version you don't want to keep, or combine the two cells as needed. + +The techniques used to make the merge driver work are quite fascinating -- let's dive into the details! + +### The nbdev2 git merge driver + +We provide here a summary of the git merge driver -- for full details and source code see the [`nbdev.merge` docs](https://nbdev.fast.ai/merge.html). Amazingly enough, the entire implementation is just 58 lines of code! + +The basic idea is to first "undo" the original git merge which created the conflict, and then "redo" it at a cell level (instead of a line level) and looking only at cell source (not outputs or metadata). The "undoing" is straightforward: just create two copies of the conflicted file (representing the local and remove versions of the file), go through each git conflict marker, and replace the conflict section with either the local or remote version of the code. + +Now that we've got the original local and remote notebooks, we can load the json using [`execnb.nbio`](https://fastai.github.io/execnb/nbio.html), which will then give us an array of cells for each notebook. Now we're up to the interesting bit -- creating cell-level diffs based only on the cell source. + +The Python standard library contains a very flexible and effective implementation of a diff algorithm in the `difflib` module. In particular, the [`SequenceMatcher`](https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher) class provides the fundamental building blocks for implementing your own conflict resolution system. We pass the two sets of cells (remote and local) to `SequenceMatcher(...).get_matching_blocks()`, and it returns a list of each section of cells that match (i.e. have no conflicts/differences). We can then go through each matching section and copy them into the final notebook, and through each non-matching section and copy in each of the remote and local cells (add cells between them to mark the conflicts). + +Making `SequenceMatcher` work with notebook cells (represented in nbdev by the `NbCell` class) requires only adding `__hash__` and `__eq__` methods to `NbCell`. In each case, these methods are defined to look only at the actual source code, and not at any metadata or outputs. As a result, `SequenceMatcher` will only show differences in source code, and will ignore differences in everything else. + +With a single line of configuration, we can ask git to call our python script, instead of its default line-based implementation, any time it is merging changes. `nbdev_install_hooks` sets up this configuration automatically, so after running it, git conflicts become much less common, and never result in broken notebooks. + +### The nbdev2 Jupyter save hook + +Solving git merges locally is extremely helpful, but we need to solve them remotely as well. For instance, if a contributor submits a pull request (PR), and then someone else commits to the same notebook before the PR is merged, the PR might now have a conflict like this: + +::: {.pt-3 .pb-1 .px-3 .mt-2 .mb-4 .border .rounded .shadow-sm} + +``` + "outputs": [ + { +<<<<<< HEAD + "execution_count": 7, +====== + "execution_count": 5, +>>>>>> a7ec1b0bfb8e23b05fd0a2e6cafcb41cd0fb1c35 + "metadata": {}, +``` + +::: + +This conflict shows that the two contributors have run cells in different orders (or perhaps one added a couple of cells above in the notebook), so their commits have conflicting execution counts. GitHub will refuse to allow this PR to be merged until this conflict is fixed. + +But of course we don't really care about the conflict at all -- it doesn't matter what, if any, execution count is stored in the notebook. So we'd really prefer to ignore this difference entirely! + +Thankfully, Jupyter provides a "pre-save" hook which allows code to be run every time a notebook is saved. nbdev uses this to set up a hook which removes all unnecessary metadata (including `execution_count`) on saving. That means there's no pointless conflicts like the one above, because no commits will have this information stored in the first place. + +## Background + +Here at fast.ai we use Jupyter for everything. All our tests, documentation, and module source code for all of our many libraries is entirely developed in notebooks (using nbdev, of course!) And we use git for all our libraries too. Some of our repositories have many hundreds of contributors. Therefore solving the Jupyter+git problem has been critical for us. The solution presented here is the result of years of work by many people. + +Our first approach, developed by Stas Bekman and me, was to use git ["smudge" and "clean" filters](https://bignerdranch.com/blog/git-smudge-and-clean-filters-making-changes-so-you-dont-have-to/) that automatically rewrote all notebook json to remove unneeded metadata when committing. This helped a bit, but git quite often ended up in an odd state where it was impossible to merge. + +In nbdev v1 Sylvain Gugger created an amazing tool called `nbdev_fix_merge` which used very clever custom logic to manually fix merge conflicts in notebooks, to ensure that they could opened in Jupyter. For nbdev v2 I did a from-scratch rewrite of every part of the library, and I realised that we could replace the custom logic with the `SequenceMatcher` approach described above. + +None of these steps fully resolved the Jupyter+git problem, since we were getting frequent merge errors caused by the smudge/clean git filters, and conflicts required manually running `nbdev_fix_merge`. Wasim Lorgat realised that we could resolve the smudge/clean issue by moving that logic into an nbdev save hook, and avoid the manual fix step by moving that logic into a git merge driver. This resolved the final remaining issues! (I was actually quite stunned that Wasim went from our first discussion of the outstanding problems, to figuring out how to solve all of them, in the space of about two days...) + +## The result + +The new tools in nbdev2, which we've been using internally for the last few months, have been transformational to our workflow. **The Jupyter+git problem has been totally solved.** I've seen no unnecessary conflicts, cell-level merges have worked like magic, and on the few occassions where I've changed the source in the same cell as a collaborator, fixing the conflict in Jupyter has been straightforward and convenient. + +## Postscript: other Jupyter+git tools + +### ReviewNB + +There is one other tool which we've found very helpful in using Jupyter with git, which is [ReviewNB](https://www.reviewnb.com/). ReviewNB solves the problem of doing pull requests with notebooks. GitHub's code review GUI only works well for line-based file formats, such as plain python scripts. This works fine with the Python modules that nbdev exports, and I often do reviews directly on the Python files, instead of the source notebooks. + +However, much of the time I'd rather do reviews on the source notebooks, because: + +- I want to review the documentation and tests, not just the implementation +- I want to see the changes to cell outputs, such as charts and tables, not just the code. + +For this purpose, ReviewNB is perfect. Just like nbdev makes git merges and commits Jupyter-friendly, ReviewNB makes code reviews Jupyter-friendly. A picture is worth a thousand words, so rather than trying to explain, I'll just show this picture from the ReviewNB website of what PRs look like in their interface: + +![](https://uploads-ssl.webflow.com/5ba4ebe021cb91ae35dbf88c/61f800c83d99ed2f1810b6e4_visual_diff_2.png) + +### An alternative solution: Jupytext + +Another potential solution to the Jupyter+git problem might be to use [Jupytext](https://jupytext.readthedocs.io/en/latest/index.html). Jupytext saves notebooks in a line-based format, instead of in JSON. This means that all the usual git machinery, such as merges and PRs, works fine. Jupytext can even use [Quarto's](https://quarto.org/) format, `qmd`, as a format for saving notebooks, which then can be used to generate a website. + +Jupytext can be a bit tricky to manage when you want to save your cell outputs (which I generally want to do, since many of my notebooks take a long time to run -- e.g training deep learning models.) Whilst Jupytext can save outputs in a linked `ipynb` file, managing this linkage gets complex, and ends up with the Jupyter+git problem all over again! If you don't need to save outputs, then you might find Jupytext sufficient -- although of course you'll miss out on the cell-based code reviews of ReviewNB and your users won't be able to read your notebooks properly when they're browsing GitHub. + +### nbdime + +There's also an interesting project called [nbdime](https://nbdime.readthedocs.io/en/latest/) which has its own git drivers and filters. Since they're not really compatible with nbdev (partly because they tackle some of the same problems in different ways) I haven't used them much, so haven't got an informed opinion about them. However I do use nbdime's Jupyter extension sometimes, which provides a view similar to ReviewNB, but for local changes instead of PRs. + +If you want to try to yourself, follow the directions on [Git-friendly Jupyter](https://nbdev.fast.ai/01_Tutorials/02_git_friendly_jupyter.html) to get started. + diff --git a/nbs/blog/posts/2022-08-25-jupyter-git/unreadable-notebook.png b/nbs/blog/posts/2022-08-25-jupyter-git/unreadable-notebook.png new file mode 100644 index 000000000..c7f9cb0b2 Binary files /dev/null and b/nbs/blog/posts/2022-08-25-jupyter-git/unreadable-notebook.png differ diff --git a/nbs/getting_started.ipynb b/nbs/getting_started.ipynb index 9b2971bb8..d58ef07b2 100644 --- a/nbs/getting_started.ipynb +++ b/nbs/getting_started.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Getting started\n", + "# Getting Started\n", "\n", "> Create delightful software with Jupyter Notebooks" ] diff --git a/nbs/migrating.ipynb b/nbs/migrating.ipynb index 28c16a738..fc39693ff 100644 --- a/nbs/migrating.ipynb +++ b/nbs/migrating.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# nbdev1 migration\n", + "# nbdev1 Migration\n", "\n", "> How to change your nbdev1 repo to work with nbdev2" ] diff --git a/nbs/styles.css b/nbs/styles.css index f11fe5ded..ba17c8d4e 100644 --- a/nbs/styles.css +++ b/nbs/styles.css @@ -46,3 +46,7 @@ blockquote > pre { /* disable striped tables */ --bs-table-striped-bg: var(--bs-table-bg); } + +.quarto-figure-center > figure > figcaption { + text-align: center; +} \ No newline at end of file diff --git a/nbs/tutorials/best_practices.ipynb b/nbs/tutorials/best_practices.ipynb index 6e8578768..a4d2a282e 100644 --- a/nbs/tutorials/best_practices.ipynb +++ b/nbs/tutorials/best_practices.ipynb @@ -5,7 +5,7 @@ "id": "08f83295", "metadata": {}, "source": [ - "# Notebook best practices\n", + "# Notebook Best Practices\n", "\n", "> How to write great nbdev notebooks\n", "\n", diff --git a/nbs/tutorials/blogging.ipynb b/nbs/tutorials/blogging.ipynb new file mode 100644 index 000000000..eaae7cc3c --- /dev/null +++ b/nbs/tutorials/blogging.ipynb @@ -0,0 +1,362 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "331a9e51-3d6b-42c2-940d-fbc44bf81e26", + "metadata": {}, + "source": [ + "# Blogging\n", + "\n", + "> Creating a blog with notebooks\n", + "- order: 5" + ] + }, + { + "cell_type": "markdown", + "id": "a5ab5a63-cab7-4a1a-9858-e51286abde80", + "metadata": {}, + "source": [ + "## Background\n", + "\n", + "Blogging with notebooks can offer a dramatic quality of life improvement over writing in Markdown, especially for blog posts that contain code. Previously, there were no static site generators that supported Jupyter Notebooks as a first-class authoring medium. This previously led us to create [fastpages](https://github.com/fastai/fastpages) (now deprecated), which extended [Jekyll](https://jekyllrb.com/) to enable blogging directly with Jupyter. \n", + "\n", + "## Enter Quarto\n", + "\n", + "However, there now exists [Quarto](https://quarto.org/), a wonderful publishing system with rich support for authoring via Jupyter Notebooks. \n", + "\n", + "Some helpful resources on getting started with Quarto:\n", + "\n", + "- [The Quarto Homepage](https://quarto.org/)\n", + "- [Creating A Blog With Quarto](https://quarto.org/docs/websites/website-blog.html): This page helps you get started with creating a blog.\n", + "- [A Gallery of Quarto Sites](https://quarto.org/docs/gallery/): Good reference examples of using Quarto.\n", + "- [The Quarto Website](https://github.com/quarto-dev/quarto-web): The Quarto website is built with Quarto and they use some of its advanced functionality. It is instructive to look through this project to understand how Quarto works.\n", + "\n", + ":::{.callout-note}\n", + "\n", + "### You don't need nbdev to blog with notebooks\n", + "\n", + "You do not need to use nbdev to create a blog with notebooks. However, you may wish to:\n", + "\n", + "- Incorporate a blog in your nbdev project's website\n", + "- Use some nbdev functionality in your blog (testing, exporting etc)\n", + "\n", + "We will discuss these subjects in this article.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "0b71025a-455d-4cd7-a196-6ebd5cc4f519", + "metadata": {}, + "source": [ + "## Migrating from fastpages\n", + "\n", + "If you previously had a fastpages site, we offer some utilities to help migrate you to Quarto. **The migration is not holistic: you will likely have to manually correct some things that we are not able to automate.**\n", + "\n", + "Instructions:\n", + "\n", + "1. [Install Quarto](https://quarto.org/docs/get-started/)\n", + "2. Create a new repo or directory to migrate your blog to\n", + "\n", + "3. In this new repo, create a quarto blog and install required extensions with the following terminal commands. This will create a minimal project structure for you:\n", + "\n", + "```bash\n", + "quarto create-project --type website:blog .\n", + "quarto install extension quarto-ext/video\n", + "```\n", + "\n", + "5. Your new repo will have a `posts/` directory. This is where you will copy all of your notebook and markdown posts from fastpages. For example, let's say your fastpages blog repo is in a sibling directory located at `../blog/`, you would copy all the relevant posts like this: \n", + "\n", + ":::{.callout-important}\n", + "\n", + "Make sure you are in root of your quarto directory before executing the below commands. Furthermore, change the commands as appropriate depending on the location of your fastpages repo relative to your current directory.\n", + "\n", + ":::\n", + "\n", + "```bash\n", + "cp -r ../blog/_notebooks/* posts\n", + "cp -r ../blog/_posts/* posts\n", + "```\n", + "\n", + "6. Copy all images associated with your posts into the `posts/` directory. We have to get our images from several places (due to the way Jekyll and fastpages work):\n", + "\n", + "```bash\n", + "cp ../blog/images/* posts\n", + "cp -r ../blog/images/copied_from_nb/* posts/\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "c2b5a506-28df-40d0-9fcb-4f523b2c0943", + "metadata": {}, + "source": [ + "7. Make your posts Quarto compatible with the following command:\n", + "\n", + "```bash\n", + "nbdev_migrate --path posts\n", + "```\n", + "\n", + ":::{.callout-note collapse=\"true\"}\n", + "\n", + "### What does `nbdev_migrate` do?\n", + "\n", + "`nbdev_migrate` does the following things:\n", + "\n", + "#### For notebooks\n", + "- Migrates markdown front matter to raw cell front matter [as described here](../api/migrate.ipynb#migrateproc).\n", + "- nbdev v1 directives are automatically converted to [Quarto directives](../explanations/directives.ipynb). Note that we convert everything to Quarto directives (nbdev-specific directives are not relevant for this context)\n", + "- Markdown shortcut for embedding youtube videos and callouts are automatically converted to work with Quarto.\n", + "\n", + "#### For markdown and notebooks\n", + "- Automatically creates [link aliases](https://quarto.org/docs/reference/formats/html.html#website) so that old links will not break. Jekyll automatically generates URLs differently than Quarto, so this ensures that the Jekyll way is aliased.\n", + "- Automatically corrects image paths\n", + "- Makes front matter compatible with Quarto by changing field names and values where necessary\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "79f1e2c2-aa94-4ef5-8249-0eb5c030452a", + "metadata": {}, + "source": [ + "8. Update the following files: \n", + " \n", + "- `./.gitignore`: we suggest adding`_site/` as well as dot files `.*`\n", + "- `./about.qmd`: Add some information about yourself.\n", + "- `./profile.jpg`: optionally change the profile picture.\n", + "\n", + "9. Preview your site with the command `quarto preview`, and make any necessary adjustments and fix for broken links or Jekyll shortcodes (things with `{% ... %}`) that need to be converted to Quarto. Search the [the Quarto documentation](https://quarto.org/) if you need help locating specific Quarto features." + ] + }, + { + "cell_type": "markdown", + "id": "3f00ffc1-295a-4de5-b71f-92cde47d3a84", + "metadata": {}, + "source": [ + "### Configuration options: fastpages vs. Quarto\n", + "\n", + "fastpages (which is based on Jekyll) and Quarto offer different options and configurations for individual posts and at a site level. The tables below enumerate some of the most important features of fastpages and how they map to Quarto. Each link in the last column is specific to the relevant feature.\n", + "\n", + "#### Post-level options\n", + "\n", + "Jekyll Front Matter | Quarto | Notes\n", + "-- | -- | --\n", + "toc: false | Same | There are more options in the [Quarto docs](https://quarto.org/docs/reference/formats/pdf.html#table-of-contents)\n", + "badges: true | n/a | No support yet\n", + "comments: true | see notes | [Quarto docs](https://quarto.org/docs/output-formats/html-basics.html#commenting)\n", + "categories: [fastpages, jupyter] | Same | [Quarto docs](https://quarto.org/docs/websites/website-listings.html#categories)\n", + "image: images/some_folder/your_image.png | Same | [Quarto docs](https://quarto.org/docs/websites/website-listings.html#listing-fields)\n", + "hide: false | draft: true | [Quarto docs](https://quarto.org/docs/websites/website-blog.html#drafts)\n", + "search_exclude: true | see notes | [Quarto docs](https://quarto.org/docs/websites/website-search.html#disabling-search)\n", + "title | Same |  \n", + "description | Same |  \n", + "sticky_rank | Not supported | [Quarto docs](https://quarto.org/docs/websites/website-listings.html#sorting-items)\n", + "\n", + "\n", + "\n", + "\n", + "#### Site-level options\n", + "\n", + "Jekyll site config | Quarto | Notes\n", + "-- | -- | --\n", + "title | see notes | [Quarto docs](https://quarto.org/docs/websites/#getting-started)\n", + "description | see notes | [Quarto docs](https://quarto.org/docs/websites/website-tools.html)\n", + "github_repo | see notes | [Quarto docs](https://quarto.org/docs/websites/website-navigation.html#top-navigation)\n", + "url | n/a | Don't need this\n", + "baseurl | n/a | Don't need this\n", + "twitter_username | search page in notes for \"twitter\" | [Quarto docs](https://quarto.org/docs/websites/website-navigation.html#top-navigation)\n", + "use_math | see notes  | [Quarto docs](https://quarto.org/docs/output-formats/html-basics.html#latex-equations)\n", + "google_analytics | website: google-analytics: \"UA-XXXXXXXX\" | [Quarto docs](https://quarto.org/docs/websites/website-tools.html#google-analytics)\n", + "show_image | n/a | [Quarto docs](https://quarto.org/docs/reference/formats/html.html#layout)\n", + "show_tags | see title-block-categories of page in notes | [Quarto docs](https://quarto.org/docs/reference/formats/html.html#layout)\n", + "pagination | website: page-navigation: true | [Quarto docs](https://quarto.org/docs/websites/website-navigation.html#page-navigation)\n", + "annotations | comments: hypothesis: ... | [Quarto docs](https://quarto.org/docs/output-formats/html-basics.html#commenting)" + ] + }, + { + "cell_type": "markdown", + "id": "8ee5b80e-7931-4d03-8f3e-c8010c36279c", + "metadata": {}, + "source": [ + "## Publishing your blog\n", + "\n", + "::: {.callout-warning}\n", + "_This section is for stand-alone blogs that are not part of a nbdev documentation site. If you want to create a blog within a nbdev documentation site, see [this section](#creating-a-blog-within-a-nbdev-project)._\n", + ":::\n", + "\n", + "You can publish your site with the `quarto publish` command. See [the docs](https://quarto.org/docs/publishing/) for more details. \n", + "\n", + ":::{.callout-note}\n", + "\n", + "### GitHub Pages\n", + "\n", + "If using GitHub Pages, commit your files to GitHub before publish your site. **No GitHub Actions are needed**, you can use `quarto publish` instead. If you want to automate your workflow with GitHub Actions, you can follow [these instructions](https://quarto.org/docs/publishing/quarto-pub.html#github-action) \n", + "\n", + ":::\n", + "\n", + "\n", + "## Creating a blog within a nbdev project\n", + "\n", + "In addition to a stand-alone blog that you might build with Quarto, you might want to include a blogging site in your nbdev project. This website has [a blog](../blog/) as well! The easiest way to implement a blog is to emulate the directory structure of [this folder](https://github.com/fastai/nbdev/tree/master/nbs/blog). The general steps are:\n", + "\n", + "1. **Create a `blog/` directory in your notebooks folder.**\n", + "2. **Create a `index.qmd` file in the root of the `blog/` directory.** Here [is an example](https://github.com/fastai/nbdev/blob/master/nbs/blog/index.qmd).\n", + "\n", + "\n", + "The frontmatter the `index.qmd` file signals to Quarto that you intend to create a blog. For example, here is [our front matter](https://github.com/fastai/nbdev/blob/master/nbs/blog/index.qmd):\n", + "\n", + "```yaml\n", + "---\n", + "title: nbdev Blog\n", + "subtitle: News, tips, and commentary about all things nbdev\n", + "listing:\n", + " sort: \"date desc\"\n", + " contents: \"posts\"\n", + " sort-ui: false\n", + " filter-ui: false\n", + " categories: true\n", + " feed: true\n", + "page-layout: full\n", + "---\n", + "```\n", + "\n", + "The `listing:` field specifies how you want to structure your blog. Feel free to copy ours as-is, but we encourage you to [consult the documentation](https://quarto.org/docs/websites/website-blog.html) for additional options.\n", + "\n", + "3. **Create a link to your blog on your site's navbar so people can find it**: To add a link to your blog on your site's navbar, you must edit the navbar section of `_quarto.yml` to include a link to your blog's listing page, which is `blog/index.qmd` in our example. For nbdev, the relevant part of our [`_quarto.yml`](https://github.com/fastai/nbdev/blob/master/nbs/_quarto.yml) file looks like this:\n", + "[The yaml snippet shown below is an abbreviated version of nbdev's [`_quarto.yml`](# An abbreviated version of https://github.com/fastai/nbdev/blob/master/nbs/_quarto.yml) file.]{.aside}\n", + "```yaml\n", + "website:\n", + " navbar:\n", + " left:\n", + " - text: \"Blog\"\n", + " href: blog/index.qmd\n", + "```\n", + "\n", + "\n", + "You can read more about the navbar in the [Quarto docs](https://quarto.org/docs/websites/website-navigation.html#top-navigation).\n", + "\n", + "4. **Create a folder for each blog post**: This is not strictly required, but we recommend this as a way to keep your blog posts and related assets (pictures, videos etc) organized. \n", + "\n", + "5. **Create your first blog post**: You can emulate our example or create your own. In each folder, create a `index.qmd` or `index.ipynb` file. You can also put images and related assets in the same folder.\n", + "\n", + "\n", + "### Folder structure\n", + "\n", + "Below is an overview of the general folder structure for a blog within a nbdev site:\n", + "\n", + "```\n", + "nbs/blog\n", + "├── index.qmd\n", + "└── posts\n", + " ├── 2022-07-28-nbdev2\n", + " │   ├── cover.png\n", + " │   ├── index.qmd\n", + " │   ├── ...\n", + " └── 2022-08-25-jupyter-git\n", + " ├── friendly-conflict.png\n", + " ├── index.qmd\n", + " └── ...\n", + " ...\n", + "```\n", + "\n", + "- `nbs/blog`: this is the folder inside your notebook folder that contains the blog.\n", + "- `index.qmd`: this is at the root of your blog folder and is the listings page for your blog.\n", + "- `posts/`: this a subdirectory for each blog post.\n", + "- `YYYY-MM-DD-.../index.{qmd,ipynb}`: this is where you author the content of each individual blog post.\n", + "\n", + "### Special considerations for nbdev blogs\n", + "\n", + "In contrast to standalone Quarto blogs, when you embed a blog in a nbdev website there are the following differences:\n", + "\n", + "- You can use all [nbdev directives](../explanations/directives.ipynb) in addition to Quarto ones. All nbdev features will be available in your nbdev blog in the same way they work for other pages.\n", + "- Your site will automatically deploy with GitHub Actions or whatever deployment mechanism you have for your nbdev site, so you do not have to use `quarto publish`. " + ] + }, + { + "cell_type": "markdown", + "id": "700589e2-aca4-44a4-b82d-167f63f55758", + "metadata": {}, + "source": [ + "## Using nbdev features in blogs outside nbdev projects\n", + "\n", + "::: {.callout-warning}\n", + "_This section is for stand-alone blogs that are not part of a nbdev documentation site. If you create a blog within a nbdev documentation site, all nbdev features will automatically work._\n", + ":::\n", + " \n", + "If you create a standalone blog with Quarto, a limited number of nbdev features can still assist you. For example, we recommend installing [Jupyter git hooks](http://localhost:3000/tutorials/pre_commit.html). A list of nbdev features available to you are listed in [this article](modular_nbdev.ipynb)." + ] + }, + { + "cell_type": "markdown", + "id": "53d31ad9-6618-4cdc-bdcf-66506b5ea74f", + "metadata": {}, + "source": [ + "# FAQ\n", + "\n", + "1. **Do I need to migrate from fastpages?** \n", + "\n", + " No you do not. However we will not be actively supporting fastpages going forward.\n", + "\n", + "2. **Will migrating from fastpages to Quarto take lots of manual effort?**\n", + "\n", + " You will have to do some manual work to make sure everything looks and works the same, including converting Jekyll shortcodes to equivalent Quarto commands. However, we have automated the biggest aspects for you. Please see [Migrating from fastpages](#migrating-from-fastpages) for instructions.\n", + "\n", + "3. **I cannot find something in Quarto that does what `#collapse_output` did in fastpages**\n", + "\n", + " Correct, this is a feature that isn't supported in Quarto yet. \n", + "\n", + "4. **Why am I seeing `{% twitter ...%}` and `{% fn_detail ... %}` and other `{%...%}` commands in my Quarto blog posts?**\n", + "\n", + " These are Jekyll shortcodes that you will have to manually migrate to Quarto. For example with the twitter embeds, you can insert the appropriate HTML as [recommended by Twitter](https://help.twitter.com/en/using-twitter/how-to-embed-a-tweet#:~:text=Click%20the%20icon%20located%20within,by%20clicking%20set%20customization%20options). For footnotes, you can see [these docs](https://quarto.org/docs/authoring/footnotes-and-citations.html). For other items, you should search the [Quarto docs](https://quarto.org/docs/websites/website-blog.html) for how to enable your desired result.\n", + " \n", + "5. **I created a new blog post and used `#| hide` but the cell is still showing. What is wrong?**\n", + "\n", + " You must use Quarto directives, not nbdev-specific ones. See the difference between the two [in these docs](../explanations/directives.ipynb). This is because you are blogging with Quarto, not nbdev. The exception to this is you have a blog site in your nbdev project -- in that case, nbdev-specific directives will work. \n", + " \n", + "6. **How do I enable comments on my blog posts?**\n", + "\n", + " In general, Quarto offers all of the same features and much more than fastpages did, including comments. If you search the [Quarto docs](https://quarto.org/) for comments, you will see a [page for enabling comments](https://quarto.org/docs/output-formats/html-basics.html#commenting).\n", + " \n", + "7. **How do I sort and hide posts? What about the publishing date since that's not in the filename?**\n", + "\n", + " See the Quarto docs on [creating a blog](https://quarto.org/docs/websites/website-blog.html) as well as [listing pages](https://quarto.org/docs/websites/website-listings.html).\n", + " \n", + "8. **How do I customize my site?**\n", + "\n", + " See the [Quarto docs](https://quarto.org/docs/websites/website-blog.html)\n", + "\n", + "9. **How do I do set up RSS Feeds and add particular features to my site?**\n", + "\n", + " See the [Quarto docs](https://quarto.org/docs/websites/website-blog.html)\n", + " \n", + "8. **What does nbdev have to do with Quarto?**.\n", + "\n", + " For just blogging, nothing! You can use Quarto for blogging without worrying about nbdev. In the past, we maintained a nbdev related project called [fastpages](https://github.com/fastai/fastpages), and are recommending that people migrate to Quarto if possible. Furthermore, nbdev is built on top of Quarto which means much of the functionality is similar. Finally, you can incorporate blogs into a nbdev project site, which we discuss in [this section](#creating-a-blog-within-a-nbdev-project).\n", + " \n", + "9. **For stand alone blogs, why are you directing us to Quarto, can I just use nbdev?**\n", + "\n", + " nbdev is built on top of Quarto. For the purposes of blogging, adding nbdev is of little additional benefit if all you want to do is to blog. We decided to keep things simple and let people use Quarto directly instead of trying to abstract aspects of Quarto for blogging. Furthermore, having some knowledge of Quarto can be very helpful in using nbdev!. Lastly, you can still use some nbdev features in your blog, which are listed in [this article](modular_nbdev.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1448ebc8-8097-47bd-99ff-cb53eb288d1a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/nbs/tutorials/git_friendly_jupyter.ipynb b/nbs/tutorials/git_friendly_jupyter.ipynb index fb7058437..58aa4cfc1 100644 --- a/nbs/tutorials/git_friendly_jupyter.ipynb +++ b/nbs/tutorials/git_friendly_jupyter.ipynb @@ -5,7 +5,7 @@ "id": "424352af", "metadata": {}, "source": [ - "# Git-friendly Jupyter\n", + "# Git-Friendly Jupyter\n", "\n", "> How to use nbdev hooks for git-friendly Jupyter notebooks\n", "- order: 4" diff --git a/nbs/tutorials/modular_nbdev.ipynb b/nbs/tutorials/modular_nbdev.ipynb index ad8f49849..acac31957 100644 --- a/nbs/tutorials/modular_nbdev.ipynb +++ b/nbs/tutorials/modular_nbdev.ipynb @@ -8,7 +8,8 @@ "# Modular nbdev\n", "\n", "> How to use nbdev's various tools separately\n", - "- order: 9" + "- order: 9\n", + "- skip_showdoc: true" ] }, { @@ -16,7 +17,92 @@ "id": "a97e9b2c", "metadata": {}, "source": [ - "While `nbdev_new` gets you started with everything you need to create a delightful Python package, **you can also use each of nbdev's components listed below on their own**. You might find this useful if you're porting a large system over to nbdev, or if you'd like to customise the nbdev workflow for your own project. Note that all of the commands below work without a settings.ini file." + "While `nbdev_new` gets you started with everything you need to create a delightful Python package, **you can also use each of nbdev's components listed below on their own**. You might find this useful if you're porting a large system over to nbdev, documenting an existing code base, or if you'd like to customize the nbdev workflow for your own project. Note that all of the commands below work without a `settings.ini` file unless otherwise noted." + ] + }, + { + "cell_type": "markdown", + "id": "b838f2c9-c24a-457c-9401-40708c5bfba0", + "metadata": {}, + "source": [ + "## Document existing code: `show_doc`\n", + "\n", + "nbdev allows you to document existing code, even code that is not written in nbdev! `nbdev.showdoc.show_doc` allows you to render beautiful API documentation in notebooks and on Quarto sites. For example, you can render API documentation for `numpy.all` like this: \n", + "\n", + "::: {.py-2 .px-3 .mb-4 fig-align=\"center\" .border .rounded .shadow-sm}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf7cc968-8068-4250-920e-92171bebc17a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "---\n", + "\n", + "### all\n", + "\n", + "> all (a, axis=None, out=None, keepdims=, where=)\n", + "\n", + "Test whether all array elements along a given axis evaluate to True.\n", + "\n", + "| | **Type** | **Default** | **Details** |\n", + "| -- | -------- | ----------- | ----------- |\n", + "| a | array_like | | Input array or object that can be converted to an array. |\n", + "| axis | NoneType | None | Axis or axes along which a logical AND reduction is performed.
The default (``axis=None``) is to perform a logical AND over all
the dimensions of the input array. `axis` may be negative, in
which case it counts from the last to the first axis.

.. versionadded:: 1.7.0

If this is a tuple of ints, a reduction is performed on multiple
axes, instead of a single axis or all the axes as before. |\n", + "| out | NoneType | None | Alternate output array in which to place the result.
It must have the same shape as the expected output and its
type is preserved (e.g., if ``dtype(out)`` is float, the result
will consist of 0.0's and 1.0's). See :ref:`ufuncs-output-type` for more
details. |\n", + "| keepdims | _NoValueType | | If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the input array.

If the default value is passed, then `keepdims` will not be
passed through to the `all` method of sub-classes of
`ndarray`, however any non-default value will be. If the
sub-class' method does not implement `keepdims` any
exceptions will be raised. |\n", + "| where | _NoValueType | | Elements to include in checking for all `True` values.
See `~numpy.ufunc.reduce` for details.

.. versionadded:: 1.20.0 |\n", + "| **Returns** | **ndarray, bool** | | **A new boolean or array is returned unless `out` is specified,
in which case a reference to `out` is returned.** |" + ], + "text/plain": [ + "---\n", + "\n", + "### all\n", + "\n", + "> all (a, axis=None, out=None, keepdims=, where=)\n", + "\n", + "Test whether all array elements along a given axis evaluate to True.\n", + "\n", + "| | **Type** | **Default** | **Details** |\n", + "| -- | -------- | ----------- | ----------- |\n", + "| a | array_like | | Input array or object that can be converted to an array. |\n", + "| axis | NoneType | None | Axis or axes along which a logical AND reduction is performed.
The default (``axis=None``) is to perform a logical AND over all
the dimensions of the input array. `axis` may be negative, in
which case it counts from the last to the first axis.

.. versionadded:: 1.7.0

If this is a tuple of ints, a reduction is performed on multiple
axes, instead of a single axis or all the axes as before. |\n", + "| out | NoneType | None | Alternate output array in which to place the result.
It must have the same shape as the expected output and its
type is preserved (e.g., if ``dtype(out)`` is float, the result
will consist of 0.0's and 1.0's). See :ref:`ufuncs-output-type` for more
details. |\n", + "| keepdims | _NoValueType | | If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the input array.

If the default value is passed, then `keepdims` will not be
passed through to the `all` method of sub-classes of
`ndarray`, however any non-default value will be. If the
sub-class' method does not implement `keepdims` any
exceptions will be raised. |\n", + "| where | _NoValueType | | Elements to include in checking for all `True` values.
See `~numpy.ufunc.reduce` for details.

.. versionadded:: 1.20.0 |\n", + "| **Returns** | **ndarray, bool** | | **A new boolean or array is returned unless `out` is specified,
in which case a reference to `out` is returned.** |" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#|echo: true\n", + "#|eval: false\n", + "from nbdev.showdoc import show_doc\n", + "from numpy import all\n", + "show_doc(all)" + ] + }, + { + "cell_type": "markdown", + "id": "4477baba-96ec-4d5f-aa75-a35fbe99a057", + "metadata": {}, + "source": [ + ":::\n", + "\n", + "\n", + "::: {.callout-note}\n", + "\n", + "`show_doc` automatically parses docstrings that are written in the numpy style. For more information [read here](../api/showdoc.ipynb#numpy-docstrings).\n", + "\n", + ":::" ] }, { @@ -24,7 +110,9 @@ "id": "5de0e130", "metadata": {}, "source": [ - "#### `nbdev_test`: Test notebooks" + "## Testing notebooks: `nbdev_test`\n", + "\n", + "Testing notebooks can be very useful outside of nbdev, especially if you are documenting an existing code base and want to incorporate tests for your docs. The `nbdev_test` CLI utility allows you to accomplish this:" ] }, { @@ -50,7 +138,7 @@ "id": "a2a38c4e", "metadata": {}, "source": [ - "#### `nb_export`: Export notebooks to modules" + "## Export code to modules: `nb_export`" ] }, { @@ -72,7 +160,15 @@ "id": "77673b03", "metadata": {}, "source": [ - "#### `nbdev_install_hooks`: Improve Jupyter/git integration" + "## Jupyter-git integration\n", + "\n", + "Jupyter and Git don't normally play well together, especially for things like merge conflicts. We have outlined all of these problems, and our solutions in [this blog post](../blog/posts/2022-08-25-jupyter-git/index.qmd). You can install our merge driver and hooks with the following command: \n", + "\n", + "```bash\n", + "nbdev_install_hooks\n", + "```\n", + "\n", + "We describe what `nbdev_install_hooks` does in detail on [this page](git_friendly_jupyter.ipynb)." ] }, { @@ -80,13 +176,7 @@ "id": "6f8b49e3", "metadata": {}, "source": [ - "You can install nbdev hooks into any git repo with the terminal command:\n", - "\n", - "```sh\n", - "nbdev_install_hooks\n", - "```\n", - "\n", - "Or directly use any of its underlying commands, for example, to implement your own hooks or extensions:\n", + "You can also directly use any of its underlying commands, for example, to implement your own hooks or extensions:\n", "\n", "- `nbdev_clean`\n", "- `nbdev_fix`\n", @@ -99,15 +189,15 @@ "id": "d12eaaae-04b1-41f9-a162-e8a4d7d3bef4", "metadata": {}, "source": [ - "#### Pre-commit hooks: Automatically check notebooks before commits" + "To configure your own hooks, Check out the [pre-commit hooks tutorial](/tutorials/pre_commit.ipynb)." ] }, { "cell_type": "markdown", - "id": "a5992830-a000-4862-8eb8-d4856d7cdffd", + "id": "7f6c261f-d47f-4d1a-a695-05370f81a9c9", "metadata": {}, "source": [ - "Check out the [pre-commit hooks tutorial](/tutorials/pre_commit.ipynb) for more." + "## Python packaging" ] }, { @@ -115,31 +205,7 @@ "id": "9b9a56e1", "metadata": {}, "source": [ - "#### [`nbdev.release`](/api/release.html): Easy packaging on PyPI, conda, and GitHub" - ] - }, - { - "cell_type": "markdown", - "id": "40810bf2", - "metadata": {}, - "source": [ - "Check out the `nbdev.release` docs for more. Note that this functionality requires a settings.ini file." - ] - }, - { - "cell_type": "markdown", - "id": "22d910d0", - "metadata": {}, - "source": [ - "#### [`nbdev.quarto`](/api/quarto.html): Technical documentation with Quarto" - ] - }, - { - "cell_type": "markdown", - "id": "368989e9", - "metadata": {}, - "source": [ - "Check out the `nbdev.quarto` docs for more. Note that this functionality requires a settings.ini file." + "`nbdev.release`provides utlities for easy packaging on PyPI, conda, and GitHub. Check out the [`nbdev.release`](/api/release.html) docs for more information. Note that this functionality requires a settings.ini file." ] }, { diff --git a/nbs/tutorials/pre_commit.ipynb b/nbs/tutorials/pre_commit.ipynb index 4b43fe0e3..382b14060 100644 --- a/nbs/tutorials/pre_commit.ipynb +++ b/nbs/tutorials/pre_commit.ipynb @@ -5,10 +5,10 @@ "id": "2f2a6752-fa19-4096-8bd2-5ca82e1a9ffb", "metadata": {}, "source": [ - "# Pre-commit hooks\n", + "# Pre-Commit Hooks\n", "\n", "> How to use nbdev's git pre-commit hooks\n", - "- order: 5" + "- order: 6" ] }, { diff --git a/nbs/tutorials/qmd_intro.qmd b/nbs/tutorials/qmd_intro.qmd index 58be7c6bb..4fccb34ff 100644 --- a/nbs/tutorials/qmd_intro.qmd +++ b/nbs/tutorials/qmd_intro.qmd @@ -1,5 +1,5 @@ --- -title: Qmd documents +title: Qmd Documents description: Introduction to qmd -- markdown on steroids order: 3 execute: diff --git a/nbs/tutorials/tutorial.ipynb b/nbs/tutorials/tutorial.ipynb index f678bf822..e052326f3 100644 --- a/nbs/tutorials/tutorial.ipynb +++ b/nbs/tutorials/tutorial.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# End-to-end walkthrough\n", + "# End-To-End Walkthrough\n", "\n", "> A step-by-step guide to using nbdev\n", "- order: 1\n",