<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: sphinx-docs</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/sphinx-docs.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2024-09-23T21:45:15+00:00</updated><author><name>Simon Willison</name></author><entry><title>simonw/docs cookiecutter template</title><link href="https://simonwillison.net/2024/Sep/23/docs-cookiecutter/#atom-tag" rel="alternate"/><published>2024-09-23T21:45:15+00:00</published><updated>2024-09-23T21:45:15+00:00</updated><id>https://simonwillison.net/2024/Sep/23/docs-cookiecutter/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonw/docs"&gt;simonw/docs cookiecutter template&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Over the last few years I’ve settled on the combination of &lt;a href="https://www.sphinx-doc.org/"&gt;Sphinx&lt;/a&gt;, the &lt;a href="https://github.com/pradyunsg/furo"&gt;Furo&lt;/a&gt; theme and the &lt;a href="https://myst-parser.readthedocs.io/en/latest/"&gt;myst-parser&lt;/a&gt; extension (enabling Markdown in place of reStructuredText) as my documentation toolkit of choice, maintained in GitHub and hosted using &lt;a href="https://about.readthedocs.com/"&gt;ReadTheDocs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My &lt;a href="https://llm.datasette.io/"&gt;LLM&lt;/a&gt; and &lt;a href="https://shot-scraper.datasette.io/"&gt;shot-scraper&lt;/a&gt; projects are two examples of that stack in action.&lt;/p&gt;
&lt;p&gt;Today I wanted to spin up a new documentation site so I finally took the time to construct a &lt;a href="https://cookiecutter.readthedocs.io/"&gt;cookiecutter&lt;/a&gt; template for my preferred configuration. You can use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pipx install cookiecutter
cookiecutter gh:simonw/docs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or with &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv tool run cookiecutter gh:simonw/docs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Answer a few questions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1/3] project (): shot-scraper
[2/3] author (): Simon Willison
[3/3] docs_directory (docs):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it creates a &lt;code&gt;docs/&lt;/code&gt; directory ready for you to start editing docs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd docs
pip install -r requirements.txt
make livehtml
&lt;/code&gt;&lt;/pre&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/markdown"&gt;markdown&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookiecutter"&gt;cookiecutter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/read-the-docs"&gt;read-the-docs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;



</summary><category term="documentation"/><category term="projects"/><category term="python"/><category term="markdown"/><category term="cookiecutter"/><category term="sphinx-docs"/><category term="read-the-docs"/><category term="uv"/></entry><entry><title>How I’m a Productive Programmer With a Memory of a Fruit Fly</title><link href="https://simonwillison.net/2022/Sep/19/docsets/#atom-tag" rel="alternate"/><published>2022-09-19T16:19:02+00:00</published><updated>2022-09-19T16:19:02+00:00</updated><id>https://simonwillison.net/2022/Sep/19/docsets/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://hynek.me/articles/productive-fruit-fly-programmer/"&gt;How I’m a Productive Programmer With a Memory of a Fruit Fly&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Hynek Schlawack describes the value he gets from searchable offline developer documentation, and advocates for the Documentation Sets format which bundles docs, metadata and a SQLite search index. Hynek’s doc2dash command can convert documentation generated by tools like Sphinx into a docset that’s compatible with several offline documentation browser applications.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/hynek/status/1571875599064481798"&gt;@hynek&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/hynek-schlawack"&gt;hynek-schlawack&lt;/a&gt;&lt;/p&gt;



</summary><category term="documentation"/><category term="sqlite"/><category term="sphinx-docs"/><category term="hynek-schlawack"/></entry><entry><title>Weeknotes: Distracted by Playwright</title><link href="https://simonwillison.net/2022/Mar/12/weeknotes-playwright/#atom-tag" rel="alternate"/><published>2022-03-12T00:30:26+00:00</published><updated>2022-03-12T00:30:26+00:00</updated><id>https://simonwillison.net/2022/Mar/12/weeknotes-playwright/#atom-tag</id><summary type="html">
    &lt;p&gt;My goal for this week was to unblock progress on Datasette by finally finishing the dash encoding implementation I described last week. I was getting close, and then I got very distracted by &lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Dash encoding v2&lt;/h4&gt;
&lt;p&gt;In &lt;a href="https://simonwillison.net/2022/Mar/5/dash-encoding/"&gt;Why I invented “dash encoding”, a new encoding scheme for URL paths&lt;/a&gt; I described a new mechanism I had invented for handling the gnarly problem of including table names with &lt;code&gt;/&lt;/code&gt; characters in the URL path on Datasette. The very short version: you can't use URL encoding in a path, because common proxies (including Apache and Nginx) will decode them before they get to your application.&lt;/p&gt;
&lt;p&gt;Thanks to feedback on that post I actually changed my design: I'm now using a variant of percent encoding that uses the &lt;code&gt;-&lt;/code&gt; instead of the &lt;code&gt;%&lt;/code&gt;. More &lt;a href="https://github.com/simonw/datasette/issues/1439#issuecomment-1059851259"&gt;details in the issue&lt;/a&gt; - and I'll write this up fully once I've finished landing the change.&lt;/p&gt;
&lt;h4&gt;shot-scraper and Playwright&lt;/h4&gt;
&lt;p&gt;I thoroughly &lt;a href="https://xkcd.com/356/"&gt;nerd-sniped&lt;/a&gt; myself with this one. I started investigating possibilities for automatically generating screeshots for documentation, and realized that &lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt; made this substantially easier than it has been in the past.&lt;/p&gt;
&lt;p&gt;The result was &lt;strong&gt;&lt;a href="https://simonwillison.net/2022/Mar/10/shot-scraper/"&gt;shot-scraper&lt;/a&gt;&lt;/strong&gt; - a new command-line utility for taking screenshots of web pages, or portions of web pages - and for running through a set of screenshots defined in a YAML file.&lt;/p&gt;
&lt;p&gt;I still can't quite believe how quickly this came together.&lt;/p&gt;
&lt;p&gt;Every now and then a tool comes along which adds a fundamental new set of capabilities to your toolbox, and can be multiplied against other tools to open up a huge range of possibilities.&lt;/p&gt;
&lt;p&gt;Playwright feels like one of those tools.&lt;/p&gt;
&lt;p&gt;A quick &lt;code&gt;pip install playwright&lt;/code&gt; is all it takes to start writing robust browser automation tools, using dedicated standalone headless instances of multiple browsers that are installed for you using &lt;code&gt;playwright install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It's easy to run in CI - getting it working in GitHub Actions was trivial.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shot-scraper&lt;/code&gt; is my first project built on Playwright, but there will definitely be more.&lt;/p&gt;
&lt;h4&gt;shot-scraper accessibility&lt;/h4&gt;
&lt;p&gt;I started &lt;a href="https://twitter.com/simonw/status/1502044953836503048"&gt;a Twitter conversation&lt;/a&gt; asking for ways to write automated tests that exercise screen readers - not just running audit rules, but actually simulating what happens when a screen reader user attempts to navigate through a specific flow within an application.&lt;/p&gt;
&lt;p&gt;The most interesting answer I had was &lt;a href="https://twitter.com/bmustillrose/status/1502066504401141767"&gt;from Ben Mustill-Rose&lt;/a&gt;, who built a system for automating tests against an Android screen reader while working on BBC iPlayer - &lt;a href="https://youtu.be/-vEHOiIggss?t=253"&gt;demo here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;@fardarter &lt;a href="https://twitter.com/fardarter/status/1502045993667280905"&gt;pointed me&lt;/a&gt; back to Playwright again, which turns out to have an &lt;a href="https://playwright.dev/python/docs/api/class-accessibility"&gt;Accessibility snapshot&lt;/a&gt; mechanism that can dump out the current state of the Chromium accessibility tree.&lt;/p&gt;
&lt;p&gt;I couldn't resist &lt;a href="https://github.com/simonw/shot-scraper/issues/22"&gt;adding that to shot-scraper&lt;/a&gt; - so now you can run the following to see the accessibility tree for a web page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~ % shot-scraper accessibility https://datasette.io
{
    "role": "WebArea",
    "name": "Datasette: An open source multi-tool for exploring and publishing data",
    "children": [
        {
            "role": "link",
            "name": "Uses"
        },
        {
            "role": "link",
            "name": "Documentation"
        },
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/simonw/431e9075441463236850bab042b9d20d"&gt;Full output here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As a really fun bonus trick: since the output is JSON, you can pipe it into &lt;a href="https://sqlite-utils.datasette.io/en/stable/cli.html#inserting-json-data"&gt;sqlite-utils insert&lt;/a&gt; to get a SQLite database:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shot-scraper accessibility https://datasette.io \
    | jq .children | sqlite-utils insert \
    /tmp/accessibility.db nodes - --alter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then open it in &lt;a href="https://datasette.io/desktop"&gt;Datasette Desktop&lt;/a&gt; and start faceting by role and heading level!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/datasette-desktop-accessibility.jpg" alt="Datasette Desktop browsing the nodes table - it has text, link, heading, button and textbox roles and four different heading levels." style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;sqlite-utils documentation improvements&lt;/h4&gt;
&lt;p&gt;I complained on Twitter that the way type information was displayed in the Sphinx &lt;a href="https://sqlite-utils.datasette.io/en/stable/reference.html"&gt;sqlite-utils API reference documentation&lt;/a&gt; was ugly:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/docs-ugly.png" alt="Really long ugly type signatures" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Adam Johnson &lt;a href="https://twitter.com/AdamChainz/status/1502311047612575745"&gt;pointed me&lt;/a&gt; to the &lt;code&gt;autodoc_typehints = "description"&lt;/code&gt; option which fixes this. I spent a while tidying up the documentation to work better with this, mainly by adding a whole bunch of &lt;code&gt;:param name: description&lt;/code&gt; tags that I had previously omitted. That work happenen in &lt;a href="https://github.com/simonw/sqlite-utils/issues/413"&gt;this issue&lt;/a&gt;. I think it looks &lt;em&gt;much&lt;/em&gt; better now:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/docs-pretty.png" alt="Type signatures are much easier to read now, and there's a detailed list of parameters with descriptions." style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/image-diff"&gt;image-diff&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/image-diff/releases/tag/0.2.1"&gt;0.2.1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/image-diff/releases"&gt;3 releases total&lt;/a&gt;) - 2022-03-11
&lt;br /&gt;CLI tool for comparing images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/sqlite-utils/releases/tag/3.25.1"&gt;3.25.1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/sqlite-utils/releases"&gt;98 releases total&lt;/a&gt;) - 2022-03-11
&lt;br /&gt;Python CLI utility and library for manipulating SQLite databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/shot-scraper"&gt;shot-scraper&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/shot-scraper/releases/tag/0.4"&gt;0.4&lt;/a&gt; - (&lt;a href="https://github.com/simonw/shot-scraper/releases"&gt;5 releases total&lt;/a&gt;) - 2022-03-10
&lt;br /&gt;Automated website screenshots using GitHub Actions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/django-sql-dashboard/releases/tag/1.0.2"&gt;1.0.2&lt;/a&gt; - (&lt;a href="https://github.com/simonw/django-sql-dashboard/releases"&gt;34 releases total&lt;/a&gt;) - 2022-03-08
&lt;br /&gt;Django app for building dashboards using raw SQL queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/geojson-to-sqlite"&gt;geojson-to-sqlite&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/geojson-to-sqlite/releases/tag/1.0"&gt;1.0&lt;/a&gt; - (&lt;a href="https://github.com/simonw/geojson-to-sqlite/releases"&gt;8 releases total&lt;/a&gt;) - 2022-03-04
&lt;br /&gt;CLI tool for converting GeoJSON files to SQLite (with SpatiaLite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/xml-analyser"&gt;xml-analyser&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/xml-analyser/releases/tag/1.3"&gt;1.3&lt;/a&gt; - (&lt;a href="https://github.com/simonw/xml-analyser/releases"&gt;4 releases total&lt;/a&gt;) - 2022-03-01
&lt;br /&gt;Simple command line tool for quickly analysing the structure of an arbitrary XML file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-dateutil"&gt;datasette-dateutil&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-dateutil/releases/tag/0.3"&gt;0.3&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-dateutil/releases"&gt;4 releases total&lt;/a&gt;) - 2022-03-01
&lt;br /&gt;dateutil functions for Datasette&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/datasette/crawling-datasette-with-datasette"&gt;Crawling Datasette with Datasette&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/homebrew/latest-sqlite"&gt;Running the latest SQLite in Datasette using Homebrew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/macos/python-installer-macos"&gt;Installing Python on macOS with the official Python installer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/gis/natural-earth-in-spatialite-and-datasette"&gt;Natural Earth in SpatiaLite and Datasette&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/pytest/coverage-with-context"&gt;pytest coverage with context&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/playwright"&gt;playwright&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shot-scraper"&gt;shot-scraper&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="accessibility"/><category term="documentation"/><category term="datasette"/><category term="weeknotes"/><category term="sphinx-docs"/><category term="playwright"/><category term="shot-scraper"/></entry><entry><title>Adding Sphinx autodoc to a project, and configuring Read The Docs to build it</title><link href="https://simonwillison.net/2021/Aug/11/sphinx-autodoc/#atom-tag" rel="alternate"/><published>2021-08-11T01:21:28+00:00</published><updated>2021-08-11T01:21:28+00:00</updated><id>https://simonwillison.net/2021/Aug/11/sphinx-autodoc/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://til.simonwillison.net/sphinx/sphinx-autodoc"&gt;Adding Sphinx autodoc to a project, and configuring Read The Docs to build it&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
My TIL notes from figuring out how to use sphinx-autodoc for the sqlite-utils reference documentation today.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/read-the-docs"&gt;read-the-docs&lt;/a&gt;&lt;/p&gt;



</summary><category term="documentation"/><category term="sqlite-utils"/><category term="sphinx-docs"/><category term="read-the-docs"/></entry><entry><title>sqlite-utils API reference</title><link href="https://simonwillison.net/2021/Aug/11/sqlite-utils-api-reference/#atom-tag" rel="alternate"/><published>2021-08-11T01:03:33+00:00</published><updated>2021-08-11T01:03:33+00:00</updated><id>https://simonwillison.net/2021/Aug/11/sqlite-utils-api-reference/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://sqlite-utils.datasette.io/en/stable/reference.html"&gt;sqlite-utils API reference&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I released sqlite-utils 3.15.1 today with just one change, but it’s a big one: I’ve added docstrings and type annotations to nearly every method in the library, and I’ve started using sphinx-autodoc to generate an API reference page in the documentation directly from those docstrings. I’ve deliberately avoided building this kind of documentation in the past because I so often see projects where the class reference is the ONLY documentation, which I find makes it really hard to figure out how to actually use it. sqlite-utils already has extensive narrative prose documentation so in this case I think it’s a useful enhancement—especially since the docstrings and type hints can help improve the usability of the library in IDEs and Jupyter notebooks.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://sqlite-utils.datasette.io/en/stable/changelog.html#v3-15-1"&gt;sqlite-utils 3.15.1 release notes&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;&lt;/p&gt;



</summary><category term="documentation"/><category term="python"/><category term="sqlite-utils"/><category term="sphinx-docs"/></entry><entry><title>Weeknotes: django-sql-dashboard widgets</title><link href="https://simonwillison.net/2021/Mar/21/django-sql-dashboard-widgets/#atom-tag" rel="alternate"/><published>2021-03-21T05:50:25+00:00</published><updated>2021-03-21T05:50:25+00:00</updated><id>https://simonwillison.net/2021/Mar/21/django-sql-dashboard-widgets/#atom-tag</id><summary type="html">
    &lt;p&gt;A few small releases this week, for &lt;code&gt;django-sql-dashboard&lt;/code&gt;, &lt;code&gt;datasette-auth-passwords&lt;/code&gt; and &lt;code&gt;datasette-publish-vercel&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;django-sql-dashboard widgets and permissions&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;, my subset-of-Datasette-for-Django-and-PostgreSQL continues to come together.&lt;/p&gt;
&lt;p&gt;New this week: widgets and permissions.&lt;/p&gt;
&lt;p&gt;To recap: this Django app borrows some ideas from Datasette: it encourages you to create a read-only PostgreSQL user and grant authenticated users the ability to run one or more raw SQL queries directly against your database.&lt;/p&gt;
&lt;p&gt;You can execute more than one SQL query and combine them into a saved dashboard, which will then show multiple tables containing the results.&lt;/p&gt;
&lt;p&gt;This week I added support for dashboard widgets. You can construct SQL queries to return specific column patterns which will then be rendered on the page in different ways.&lt;/p&gt;
&lt;p&gt;There are four widgets at the moment: "big number", bar chart, HTML and Markdown.&lt;/p&gt;
&lt;p&gt;Big number is the simplest: define a SQL query that returns two columns called &lt;code&gt;label&lt;/code&gt; and &lt;code&gt;big_number&lt;/code&gt; and the dashboard will display that result as a big number:&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;select&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Entries&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; label, &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;as&lt;/span&gt; big_number &lt;span class="pl-k"&gt;from&lt;/span&gt; blog_entry;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt="Entries: 2804 - an example of a big number display" src="https://static.simonwillison.net/static/2021/dashboard-big-number.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Bar chart is more sophisticated: return columns named &lt;code&gt;bar_label&lt;/code&gt; and &lt;code&gt;bar_quantity&lt;/code&gt; to display a bar chart of the results:&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;select&lt;/span&gt;
  to_char(date_trunc(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;month&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;, created), &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;YYYY-MM&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="pl-k"&gt;as&lt;/span&gt; bar_label,
  &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;as&lt;/span&gt; bar_quantity
&lt;span class="pl-k"&gt;from&lt;/span&gt;
  blog_entry
&lt;span class="pl-k"&gt;group by&lt;/span&gt;
  bar_label
&lt;span class="pl-k"&gt;order by&lt;/span&gt;
  &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;desc&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt="A bar chart showing the result of that query" src="https://static.simonwillison.net/static/2021/dashboard-bar-chart.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;HTML and Markdown are simpler: they display the rendered HTML or Markdown, after filtering it through the Bleach library to strip any harmful elements or scripts.&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;select&lt;/span&gt;
  &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;## Ten most recent blogmarks (of &lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; 
  &lt;span class="pl-k"&gt;||&lt;/span&gt; &lt;span class="pl-c1"&gt;count&lt;/span&gt;(&lt;span class="pl-k"&gt;*&lt;/span&gt;) &lt;span class="pl-k"&gt;||&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt; total)&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;as&lt;/span&gt; markdown &lt;span class="pl-k"&gt;from&lt;/span&gt; blog_blogmark;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I'm running the dashboard application on this blog, and I've set up &lt;a href="https://simonwillison.net/dashboard/example-dashboard/"&gt;an example dashboard&lt;/a&gt; here that illustrates the different types of widget.&lt;/p&gt;
&lt;p&gt;&lt;img alt="An example dashboard with several different widgets" src="https://static.simonwillison.net/static/2021/dashboard-example.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Defining custom widgets is easy: take the column names you would like to respond to, sort them alphabetically, join them with hyphens and create a custom widget in a template file with that name.&lt;/p&gt;
&lt;p&gt;So if you wanted to build a widget that looks for &lt;code&gt;label&lt;/code&gt; and &lt;code&gt;geojson&lt;/code&gt; columns and renders that data on a &lt;a href="https://leafletjs.com/"&gt;Leaflet map&lt;/a&gt;, you would create a &lt;code&gt;geojson-label.html&lt;/code&gt; template and drop it into your Django &lt;code&gt;templates/django-sql-dashboard/widgets&lt;/code&gt; folder. See &lt;a href="https://django-sql-dashboard.readthedocs.io/en/latest/widgets.html#custom-widgets"&gt;the custom widgets documentation&lt;/a&gt; for details.&lt;/p&gt;
&lt;p&gt;Which reminds me: I decided a README wasn't quite enough space for documentation here, so I started a &lt;a href="https://django-sql-dashboard.readthedocs.io/"&gt;Read The Docs documentation site&lt;/a&gt; for the project.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.datasette.io/"&gt;Datasette&lt;/a&gt; and &lt;a href="https://sqlite-utils.datasette.io/"&gt;sqlite-utils&lt;/a&gt; both use &lt;a href="https://www.sphinx-doc.org/"&gt;Sphinx&lt;/a&gt; and &lt;a href="https://docutils.sourceforge.io/rst.html"&gt;reStructuredText&lt;/a&gt; for their documentation.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;django-sql-dashboard&lt;/code&gt; I've decided to try out Sphinx and Markdown instead, using &lt;a href="https://myst-parser.readthedocs.io/"&gt;MyST&lt;/a&gt; - a Markdown flavour and parser for Sphinx.&lt;/p&gt;
&lt;p&gt;I picked this because I want to add inline help to &lt;code&gt;django-sql-dashboard&lt;/code&gt;, and since it ships with Markdown as a dependency already (to power the Markdown widget) my hope is that using Markdown for the documentation will allow me to ship some of the user-facing docs as part of the application itself. But it's also a fun excuse to try out MyST, which so far is working exactly as advertised.&lt;/p&gt;
&lt;p&gt;I've seen people in the past avoid Sphinx entirely because they preferred Markdown to reStructuredText, so MyST feels like an important addition to the Python documentation ecosystem.&lt;/p&gt;
&lt;h4&gt;HTTP Basic authentication&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://datasette.io/plugins/datasette-auth-passwords"&gt;datasette-auth-passwords&lt;/a&gt; implements password-based authentication to Datasette. The plugin defaults to providing a username and password login form which sets a signed cookie identifying the current user.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/datasette-auth-passwords/releases/tag/0.4"&gt;Version 0.4&lt;/a&gt; introduces &lt;a href="https://github.com/simonw/datasette-auth-passwords/issues/15"&gt;optional support&lt;/a&gt; for HTTP Basic authentication instead - where the user's browser handles the authentication prompt.&lt;/p&gt;
&lt;p&gt;Basic auth has some disadvantages - most notably that it doesn't support logout without the user entirely closing down their browser. But it's useful for a number of reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It's easy to protect every resource on a website with it - including static assets. Adding &lt;code&gt;"http_basic_auth": true&lt;/code&gt; to your plugin configuration adds this protection, covering all of Datasette's resources.&lt;/li&gt;
&lt;li&gt;It's much easier to authenticate with from automated scripts. &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;roquests&lt;/code&gt; and &lt;code&gt;httpx&lt;/code&gt; all have simple built-in support for passing basic authentication usernames and passwords, which makes it a useful target for scripting - without having to install an additional authentication plugin such as &lt;a href="https://datasette.io/plugins/datasette-auth-tokens"&gt;datasette-auth-tokens&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'm continuing to flesh out authentication options for Datasette, and adding this to &lt;code&gt;datasette-auth-passwords&lt;/code&gt; is one of those small improvements that should pay off long into the future.&lt;/p&gt;
&lt;h4&gt;A fix for datasette-publish-vercel&lt;/h4&gt;
&lt;p&gt;Datasette instances published to &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt; using the &lt;a href="https://datasette.io/plugins/datasette-publish-vercel"&gt;datasette-publish-vercel&lt;/a&gt; have previously been affected by an obscure Vercel bug: &lt;a href="https://github.com/vercel/vercel/issues/5575"&gt;characters such as + in the query string&lt;/a&gt; were being lost due to Vercel unescaping encoded characters before the request got to the Python application server.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://vercel.com/changelog/correcting-request-urls-with-python-serverless-functions"&gt;Vercel fixed this&lt;/a&gt; earlier this month, and the latest release of &lt;code&gt;datasette-publish-vercel&lt;/code&gt; includes their fix by switching to the new &lt;code&gt;@vercel/python&lt;/code&gt; builder. Thanks &lt;a href="https://twitter.com/styfle"&gt;@styfle&lt;/a&gt; from Vercel for shepherding this fix through!&lt;/p&gt;
&lt;h4&gt;New photos on Niche Museums&lt;/h4&gt;
&lt;p&gt;My Niche Museums project has been in hiberation since the start of the pandemic. Now that vaccines are rolling out it feels like there might be an end to this thing, so I've started thinking about my museum hobby again.&lt;/p&gt;
&lt;p&gt;I added some new photos to the site today - on the entries for &lt;a href="https://www.niche-museums.com/17"&gt;Novelty Automation&lt;/a&gt;, &lt;a href="https://www.niche-museums.com/21"&gt;DEVIL-ish Little Things&lt;/a&gt;, &lt;a href="https://www.niche-museums.com/24"&gt;Evergreen Aviation &amp;amp; Space Museum&lt;/a&gt; and &lt;a href="https://www.niche-museums.com/33"&gt;California State Capitol Dioramas&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully someday soon I'll get to visit and add an entirely new museum!&lt;/p&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/django-sql-dashboard/releases/tag/0.4a1"&gt;0.4a1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/django-sql-dashboard/releases"&gt;10 releases total&lt;/a&gt;) - 2021-03-21
&lt;br /&gt;Django app for building dashboards using raw SQL queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-publish-vercel"&gt;datasette-publish-vercel&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-publish-vercel/releases/tag/0.9.2"&gt;0.9.2&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-publish-vercel/releases"&gt;14 releases total&lt;/a&gt;) - 2021-03-20
&lt;br /&gt;Datasette plugin for publishing data using Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-auth-passwords"&gt;datasette-auth-passwords&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-auth-passwords/releases/tag/0.4"&gt;0.4&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-auth-passwords/releases"&gt;9 releases total&lt;/a&gt;) - 2021-03-19
&lt;br /&gt;Datasette plugin for authentication using passwords&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/authentication"&gt;authentication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dashboard"&gt;dashboard&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/zeit-now"&gt;zeit-now&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="authentication"/><category term="dashboard"/><category term="django"/><category term="postgresql"/><category term="projects"/><category term="zeit-now"/><category term="weeknotes"/><category term="django-sql-dashboard"/><category term="sphinx-docs"/></entry><entry><title>The subset of reStructuredText worth committing to memory</title><link href="https://simonwillison.net/2018/Aug/25/restructuredtext/#atom-tag" rel="alternate"/><published>2018-08-25T18:44:29+00:00</published><updated>2018-08-25T18:44:29+00:00</updated><id>https://simonwillison.net/2018/Aug/25/restructuredtext/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/ReStructuredText"&gt;reStructuredText&lt;/a&gt; is the standard for documentation in the Python world.&lt;/p&gt;
&lt;p&gt;It’s a bit weird. It’s like &lt;a href="https://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; but older, more feature-filled and in my experience significantly harder to remember.&lt;/p&gt;
&lt;p&gt;There are plenty of guides and cheatsheets out there, but when writing simple documentation for software projects I think there’s a subset that is worth committing to memory. I’ll describe that subset here.&lt;/p&gt;
&lt;p&gt;First though: when writing reStructuredText having a live preview render is extremely useful. I use &lt;a href="http://rst.ninjs.org/"&gt;rst.ninjs.org&lt;/a&gt; for this. If you don’t trust that hosted version (it round-trips your documentation through the server in order to render it) you can run a local copy instead using the &lt;a href="https://github.com/anru/rsted"&gt;underlying source code&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;a id="Paragraphs_10"&gt;&lt;/a&gt;Paragraphs&lt;/h3&gt;
&lt;p&gt;Paragraphs work the same way as Markdown and plain text. They are nice and easy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;This is the first paragraph. No need to wrap the text (though you can wrap at e.g. 80 characters without affecting rendering).

This is the second paragraph.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a id="Headings_20"&gt;&lt;/a&gt;Headings&lt;/h3&gt;
&lt;p&gt;reStructuredText section headings are a little surprising.&lt;/p&gt;
&lt;p&gt;Markdown has multiple levels of heading, each with a different number of prefix hashes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Markdown heading level 1
## Markdown heading level 2
..
###### Markdown heading fevel 6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In reStructuredText there is no single format for these different levels. Instead, the format you use first will be treated as an H1, the next format as an H2 and so on. Here’s the &lt;a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#sections"&gt;description from the official documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sections are identified through their titles, which are marked up with adornment: “underlines” below the title text, or underlines and matching “overlines” above the title. An underline/overline is a single repeated punctuation character that begins in column 1 and forms a line extending at least as far as the right edge of the title text. Specifically, an underline/overline character may be any non-alphanumeric printable 7-bit ASCII character. […] There may be any number of levels of section titles, although some output formats may have limits (HTML has 6 levels).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is deeply confusing. I suggest instead standardizing on the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;=====================
 This is a heading 1
=====================
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This heading has = signs both above and below, and they extend past the text by a single character in each direction.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;This is a heading 2
===================

This is a heading 3
-------------------

This is a heading 4
~~~~~~~~~~~~~~~~~~~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need more levels, you can invent them using whatever character you like - but try to stay consistent within your project.&lt;/p&gt;
&lt;h3&gt;&lt;a id="Bulleted_lists_54"&gt;&lt;/a&gt;Bulleted lists&lt;/h3&gt;
&lt;p&gt;As with headings, you can use a variety of characters for these. I suggest sticking with asterisks.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A blank line is required before starting a bulleted list.

* A bullet point
* Another bullet point
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you decide to wrap your text (I tend not to) you must maintain the indentation on the wrapped lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* A bulleted list item. Since the text is wrapped each subsequent
  line of text must be indented by two spaces.
* Second list item.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nested lists are supported, but you MUST leave a blank line above the first  inner list bullet point or they won't work:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* This is the first bullet list item. Here comes a sub-list:

  * Hello sublist
  * Sublist two

* Back to the parent list.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a id="Inline_markup_78"&gt;&lt;/a&gt;Inline markup&lt;/h3&gt;
&lt;p&gt;I only use three inline markup features: bold, italic and code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;**Bold text** is surrounded by two asterisks.

*Italic text* is one asterisk.

``inline code`` uses two backticks at either side of the code.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a id="Links_90"&gt;&lt;/a&gt;Links&lt;/h3&gt;
&lt;p&gt;Links are my least favorite feature of reStructuredText. There are several different ways of including them, but the one I use most often (and hence have committed to memory) is this one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`a link, note the trailing underscores &amp;lt;http://example.com&amp;gt;`__
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So that’s a backtick at the start, then the link text, then the URL contained in greater than / less than symbols, then another backtick and then TWO underscores to finish it off.&lt;/p&gt;
&lt;p&gt;Why two underscores? Because if you only use one, the text part of the link is remembered and can be used to duplicate your link later on - see example below. In my experience this is more trouble than it’s worth.&lt;/p&gt;
&lt;p&gt;A more complex link syntax example (&lt;a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#embedded-uris-and-aliases"&gt;documented here&lt;/a&gt;) looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;See the `Python home page`_ for info.

This link_ is an alias to the link above.

.. _Python home page: http://www.python.org
.. _link: `Python home page`_
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can’t remember this at all, so I stick with the anonymous hyperlink syntax instead.&lt;/p&gt;
&lt;h3&gt;&lt;a id="Code_blocks_111"&gt;&lt;/a&gt;Code blocks&lt;/h3&gt;
&lt;p&gt;The easiest way to embed a block of code is like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;::

    # This is a code example
    print(&amp;quot;It needs to be indented&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;::&lt;/code&gt; indicates that a code block is coming up. The blank line after the &lt;code&gt;::&lt;/code&gt; before the indentation starts is required.&lt;/p&gt;
&lt;p&gt;Most renderers have the ability to apply syntax highlighting. To specify that a block should have syntax highlighting for a specific language, replace the &lt;code&gt;::&lt;/code&gt; in the above example with one of the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.. code-block:: sql

.. code-block:: javascript

.. code-block:: python
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a id="Images_130"&gt;&lt;/a&gt;Images&lt;/h3&gt;
&lt;p&gt;There are &lt;a href="http://docutils.sourceforge.net/docs/ref/rst/directives.html#images"&gt;plenty of options&lt;/a&gt; for embedding images, but the most basic syntax (worth remembering) looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.. image:: full_text_search.png
   :alt: alternate text
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will embed an image of that filename that sits in the same directory as the document itself.&lt;/p&gt;
&lt;h3&gt;&lt;a id="Internal_references_138"&gt;&lt;/a&gt;Internal references&lt;/h3&gt;
&lt;p&gt;In my opinion this is the key feature that makes reStructuredText more powerful than Markdown for larger documentation projects.&lt;/p&gt;
&lt;p&gt;Again, there is a vast and complex array of options around this, but the key thing to remember is how to add a reference name to a specific section and how to link to that section later on.&lt;/p&gt;
&lt;p&gt;Names are applied to section headings, by adding some magic text before the heading itself. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.. _full_text_search:

Full-text search
================
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the format: two periods, then a space, then an underscore, then the label, then a colon at the end.&lt;/p&gt;
&lt;p&gt;The label &lt;code&gt;full_text_search&lt;/code&gt; is now associated with that heading. I can link to it from any page in my documentation project like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:ref:`full_text_search`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the leading underscore isn’t included in this reference.&lt;/p&gt;
&lt;p&gt;The link text displayed will be the text of the heading, in this case “Full-text search”. If I want to replace that link text with something custom, I can do so like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Learn about the :ref:`search feature &amp;lt;full_text_search&amp;gt;`.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This syntax is similar to the inline hyperlink syntax described above.&lt;/p&gt;
&lt;h3&gt;&lt;a id="Learning_more_165"&gt;&lt;/a&gt;Learning more&lt;/h3&gt;
&lt;p&gt;I extracted the patterns I describe in this post from the &lt;a href="https://datasette.readthedocs.io/"&gt;Datasette documentation&lt;/a&gt; - I encourage you to &lt;a href="https://github.com/simonw/datasette/tree/master/docs"&gt;dig around in the source code&lt;/a&gt; to see how it all works.&lt;/p&gt;
&lt;p&gt;The definitive guide to reStructuredText is &lt;a href="http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html"&gt;the reStructuredText Markup Specification&lt;/a&gt;. My favourite of the various quick references is the &lt;a href="https://thomas-cokelaer.info/tutorials/sphinx/rest_syntax.html"&gt;Restructured Text (reST) and Sphinx CheatSheet&lt;/a&gt; by Thomas Cokelaer.&lt;/p&gt;

&lt;p&gt;I'm a huge fan of &lt;a href="https://readthedocs.org/"&gt;Read the Docs&lt;/a&gt; for hosting documentation - it's the key reason I use reStructuredText in my projects. Unsurprisingly, they offer &lt;a href="https://docs.readthedocs.io/en/latest/"&gt;extensive documentation&lt;/a&gt; to help you make the most of their platform.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/restructuredtext"&gt;restructuredtext&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/read-the-docs"&gt;read-the-docs&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="documentation"/><category term="python"/><category term="restructuredtext"/><category term="sphinx-docs"/><category term="read-the-docs"/></entry><entry><title>What's New in Python 2.6</title><link href="https://simonwillison.net/2008/Oct/2/whatus/#atom-tag" rel="alternate"/><published>2008-10-02T11:47:21+00:00</published><updated>2008-10-02T11:47:21+00:00</updated><id>https://simonwillison.net/2008/Oct/2/whatus/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://docs.python.org/whatsnew/2.6.html"&gt;What&amp;#x27;s New in Python 2.6&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Python 2.6 final has been released (the last 2.x version before 3.0). multiprocessing and simplejson (as json) are now in the standard library, any backwards compatible 3.0 features have been added and the official docs are now powered by Sphinx (used by Django 1.0 as well). There’s plenty more.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://www.python.org/download/releases/2.6/"&gt;Python 2.6 Release&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/json"&gt;json&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/multiprocessing"&gt;multiprocessing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/releases"&gt;releases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/simplejson"&gt;simplejson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="json"/><category term="multiprocessing"/><category term="python"/><category term="releases"/><category term="simplejson"/><category term="sphinx-docs"/></entry><entry><title>Django documentation (for 1.0)</title><link href="https://simonwillison.net/2008/Aug/24/django/#atom-tag" rel="alternate"/><published>2008-08-24T10:49:46+00:00</published><updated>2008-08-24T10:49:46+00:00</updated><id>https://simonwillison.net/2008/Aug/24/django/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://docs.djangoproject.com/"&gt;Django documentation (for 1.0)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The documentation refactor is in: the docs for the upcoming 1.0 release have been tidied up, rearranged and ported to a new documentation system based on Sphinx (the Python documentation toolkit, NOT the full-text search engine). The URL has also changed to docs.djangoproject.com.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="documentation"/><category term="python"/><category term="sphinx-docs"/></entry></feed>