<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: restructuredtext</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/restructuredtext.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2018-08-25T18:44:29+00:00</updated><author><name>Simon Willison</name></author><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>Documentation unit tests</title><link href="https://simonwillison.net/2018/Jul/28/documentation-unit-tests/#atom-tag" rel="alternate"/><published>2018-07-28T15:59:55+00:00</published><updated>2018-07-28T15:59:55+00:00</updated><id>https://simonwillison.net/2018/Jul/28/documentation-unit-tests/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;Or: Test-driven documentation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Keeping documentation synchronized with an evolving codebase is difficult. Without extreme discipline, it’s easy for documentation to get out-of-date as new features are added.&lt;/p&gt;
&lt;p&gt;One thing that can help is keeping the documentation for a project in the same repository as the code itself. This allows you to construct the ideal commit: one that includes the code change, the updated unit tests AND the accompanying documentation all in the same unit of work.&lt;/p&gt;
&lt;p&gt;When combined with a code review system (like &lt;a href="https://www.phacility.com/phabricator/"&gt;Phabricator&lt;/a&gt; or &lt;a href="https://help.github.com/articles/about-pull-requests/"&gt;GitHub pull requests&lt;/a&gt;) this pattern lets you enforce documentation updates as part of the review process: if a change doesn’t update the relevant documentation, point that out in your review!&lt;/p&gt;
&lt;p&gt;Good code review systems also execute unit tests automatically and attach the results to the review. This provides an opportunity to have the tests enforce other aspects of the codebase: for example, running a linter so that no-one has to waste their time arguing over standardize coding style.&lt;/p&gt;
&lt;p&gt;I’ve been experimenting with using unit tests to ensure that aspects of a project are covered by the documentation. I think it’s a very promising technique.&lt;/p&gt;
&lt;h4 id="Introspect_the_code_introspect_the_docs_12"&gt;Introspect the code, introspect the docs&lt;/h4&gt;
&lt;p&gt;The key to this trick is introspection: interogating the code to figure out what needs to be documented, then parsing the documentation to see if each item has been covered.&lt;/p&gt;
&lt;p&gt;I’ll use my &lt;a href="https://github.com/simonw/datasette"&gt;Datasette&lt;/a&gt; project as an example. Datasette’s &lt;a href="https://github.com/simonw/datasette/blob/295d005ca48747faf046ed30c3c61e7563c61ed2/tests/test_docs.py"&gt;test_docs.py&lt;/a&gt; module contains three relevant tests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;test_config_options_are_documented&lt;/code&gt; checks that every one of Datasette’s &lt;a href="http://datasette.readthedocs.io/en/latest/config.html"&gt;configuration options&lt;/a&gt; are documented.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;test_plugin_hooks_are_documented&lt;/code&gt; ensures all of the plugin hooks (powered by &lt;a href="https://pluggy.readthedocs.io/en/latest/"&gt;pluggy&lt;/a&gt;) are covered in the &lt;a href="http://datasette.readthedocs.io/en/latest/plugins.html#plugin-hooks"&gt;plugin documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;test_view_classes_are_documented&lt;/code&gt; iterates through all of the &lt;code&gt;*View&lt;/code&gt; classes (corresponding to pages in the Datasette user interface) and makes sure &lt;a href="http://datasette.readthedocs.io/en/latest/pages.html"&gt;they are covered&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each case, the test uses introspection against the relevant code areas to figure out what needs to be documented, then runs a regular expression against the documentation to make sure it is mentioned in the correct place.&lt;/p&gt;
&lt;p&gt;Obviously the tests can’t confirm the quality of the documentation, so they are easy to cheat: but they do at least protect against adding a new option but forgetting to document it.&lt;/p&gt;
&lt;h4 id="Testing_that_Datasettes_view_classes_are_covered_26"&gt;Testing that Datasette’s view classes are covered&lt;/h4&gt;
&lt;p&gt;Datasette’s view classes use a naming convention: they all end in &lt;code&gt;View&lt;/code&gt;. The current list of view classes is &lt;code&gt;DatabaseView&lt;/code&gt;, &lt;code&gt;TableView&lt;/code&gt;, &lt;code&gt;RowView&lt;/code&gt;, &lt;code&gt;IndexView&lt;/code&gt; and &lt;code&gt;JsonDataView&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since these classes are all imported into the &lt;a href="https://github.com/simonw/datasette/blob/295d005ca48747faf046ed30c3c61e7563c61ed2/datasette/app.py"&gt;datasette.app&lt;/a&gt; module (in order to be hooked up to URL routes) the easiest way to introspect them is to import that module, then run &lt;code&gt;dir(app)&lt;/code&gt; and grab any class names that end in &lt;code&gt;View&lt;/code&gt;. We can do that with a Python list comprehension:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from datasette import app
views = [v for v in dir(app) if v.endswith(&amp;quot;View&amp;quot;)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m using reStructuredText labels to mark the place in the documentation that addresses each of these classes. This also ensures that each documentation section can be linked to, for example:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://datasette.readthedocs.io/en/latest/pages.html#tableview"&gt;http://datasette.readthedocs.io/en/latest/pages.html#tableview&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The reStructuredText syntax for that label looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.. _TableView:

Table
=====

The table page is the heart of Datasette...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can extract these labels using a regular expression:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path
import re

docs_path = Path(__file__).parent.parent / 'docs'
label_re = re.compile(r'\.\. _([^\s:]+):')

def get_labels(filename):
    contents = (docs_path / filename).open().read()
    return set(label_re.findall(contents))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since Datasette’s documentation is spread across multiple &lt;code&gt;*.rst&lt;/code&gt; files, and I want the freedom to document a view class in any one of them, I iterate through every file to find the labels and pull out the ones ending in &lt;code&gt;View&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def documented_views():
    view_labels = set()
    for filename in docs_path.glob(&amp;quot;*.rst&amp;quot;):
        for label in get_labels(filename):
            first_word = label.split(&amp;quot;_&amp;quot;)[0]
            if first_word.endswith(&amp;quot;View&amp;quot;):
                view_labels.add(first_word)
    return view_labels
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have a list of class names and a list of labels across all of our documentation. Writing a basic unit test comparing the two lists is trivial:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def test_view_documentation():
    view_labels = documented_views()
    view_classes = set(v for v in dir(app) if v.endswith(&amp;quot;View&amp;quot;))
    assert view_labels == view_classes
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="Taking_advantage_of_pytest_78"&gt;Taking advantage of pytest&lt;/h4&gt;
&lt;p&gt;Datasette uses &lt;a href="https://pytest.org/"&gt;pytest&lt;/a&gt; for its unit tests, and documentation unit tests are a great opportunity to take advantage of some advanced pytest features.&lt;/p&gt;
&lt;h5 id="Parametrization_82"&gt;Parametrization&lt;/h5&gt;
&lt;p&gt;The first of these is &lt;a href="https://docs.pytest.org/en/6.2.x/parametrize.html"&gt;parametrization&lt;/a&gt;: pytest provides a decorator which can be used to execute a single test function multiple times, each time with different arguments.&lt;/p&gt;
&lt;p&gt;This example from the pytest documentation shows how parametrization works:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pytest
@pytest.mark.parametrize(&amp;quot;test_input,expected&amp;quot;, [
    (&amp;quot;3+5&amp;quot;, 8),
    (&amp;quot;2+4&amp;quot;, 6),
    (&amp;quot;6*9&amp;quot;, 42),
])
def test_eval(test_input, expected):
    assert eval(test_input) == expected
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pytest treats this as three separate unit tests, even though they share a single function definition.&lt;/p&gt;
&lt;p&gt;We can combine this pattern with our introspection to execute an independent unit test for each of our view classes. Here’s what that looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@pytest.mark.parametrize(&amp;quot;view&amp;quot;, [v for v in dir(app) if v.endswith(&amp;quot;View&amp;quot;)])
def test_view_classes_are_documented(view):
    assert view in documented_views()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s the output from pytest if we execute just this unit test (and one of our classes is undocumented):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pytest -k test_view_classes_are_documented -v
=== test session starts ===
collected 249 items / 244 deselected

tests/test_docs.py::test_view_classes_are_documented[DatabaseView] PASSED [ 20%]
tests/test_docs.py::test_view_classes_are_documented[IndexView] PASSED [ 40%]
tests/test_docs.py::test_view_classes_are_documented[JsonDataView] PASSED [ 60%]
tests/test_docs.py::test_view_classes_are_documented[RowView] PASSED [ 80%]
tests/test_docs.py::test_view_classes_are_documented[TableView] FAILED [100%]

=== FAILURES ===

view = 'TableView'

    @pytest.mark.parametrize(&amp;quot;view&amp;quot;, [v for v in dir(app) if v.endswith(&amp;quot;View&amp;quot;)])
    def test_view_classes_are_documented(view):
&amp;gt;       assert view in documented_views()
E       AssertionError: assert 'TableView' in {'DatabaseView', 'IndexView', 'JsonDataView', 'RowView', 'Table2View'}
E        +  where {'DatabaseView', 'IndexView', 'JsonDataView', 'RowView', 'Table2View'} = documented_views()

tests/test_docs.py:77: AssertionError
=== 1 failed, 4 passed, 244 deselected in 1.13 seconds ===
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id="Fixtures_130"&gt;Fixtures&lt;/h5&gt;
&lt;p&gt;There’s a subtle inefficiency in the above test: for every view class, it calls the &lt;code&gt;documented_views()&lt;/code&gt; function - and that function then iterates through every &lt;code&gt;*.rst&lt;/code&gt; file in the &lt;code&gt;docs/&lt;/code&gt; directory and uses a regular expression to extract the labels. With 5 view classes and 17 documentation files that’s 85 executions of &lt;code&gt;get_labels()&lt;/code&gt;, and that number will only increase as Datasette’s code and documentation grow larger.&lt;/p&gt;
&lt;p&gt;We can use pytest’s neat &lt;a href="https://docs.pytest.org/en/6.2.x/fixture.html"&gt;fixtures&lt;/a&gt; to reduce this to a single call to &lt;code&gt;documented_views()&lt;/code&gt; that is shared across all of the tests. Here’s what that looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@pytest.fixture(scope=&amp;quot;session&amp;quot;)
def documented_views():
    view_labels = set()
    for filename in docs_path.glob(&amp;quot;*.rst&amp;quot;):
        for label in get_labels(filename):
            first_word = label.split(&amp;quot;_&amp;quot;)[0]
            if first_word.endswith(&amp;quot;View&amp;quot;):
                view_labels.add(first_word)
    return view_labels

@pytest.mark.parametrize(&amp;quot;view_class&amp;quot;, [
    v for v in dir(app) if v.endswith(&amp;quot;View&amp;quot;)
])
def test_view_classes_are_documented(documented_views, view_class):
    assert view_class in documented_views
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fixtures in pytest are an example of dependency injection: pytest introspects every &lt;code&gt;test_*&lt;/code&gt; function and checks if it has a function argument with a name matching something that has been annotated with the &lt;code&gt;@pytest.fixture&lt;/code&gt; decorator. If it finds any matching arguments, it executes the matching fixture function and passes its return value in to the test function.&lt;/p&gt;
&lt;p&gt;By default, pytest will execute the fixture function once for every test execution. In the above code we use the &lt;code&gt;scope=&amp;quot;session&amp;quot;&lt;/code&gt; argument to tell pytest that this particular fixture should be executed only once for every &lt;code&gt;pytest&lt;/code&gt; command-line execution of the tests, and that single return value should be passed to every matching test.&lt;/p&gt;
&lt;h4 id="What_if_you_havent_documented_everything_yet_157"&gt;What if you haven’t documented everything yet?&lt;/h4&gt;
&lt;p&gt;Adding unit tests to your documentation in this way faces an obvious problem: when you first add the tests, you may have to write a whole lot of documentation before they can all pass.&lt;/p&gt;
&lt;p&gt;Having tests that protect against future code being added without documentation is only useful once you’ve added them to the codebase - but blocking that on documenting your existing features could prevent that benefit from ever manifesting itself.&lt;/p&gt;
&lt;p&gt;Once again, pytest to the rescue. The &lt;code&gt;@pytest.mark.xfail&lt;/code&gt; decorator allows you to mark a test as “expected to fail” - if it fails, pytest will take note but will not fail the entire test suite.&lt;/p&gt;
&lt;p&gt;This means you can add deliberately failing tests to your codebase without breaking the build for everyone - perfect for tests that look for documentation that hasn’t yet been written!&lt;/p&gt;
&lt;p&gt;I used &lt;code&gt;xfail&lt;/code&gt; when I &lt;a href="https://github.com/simonw/datasette/commit/e8625695a3b7938f37b64dff09c14e47d9428fe5"&gt;first added view documentation tests&lt;/a&gt; to Datasette, then removed it once the documentation was all in place. Any future code in pull requests without documentation will cause a hard test failure.&lt;/p&gt;
&lt;p&gt;Here’s what the test output looks like when some of those tests are marked as “expected to fail”:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pytest tests/test_docs.py
collected 31 items

tests/test_docs.py ..........................XXXxx.                [100%]

============ 26 passed, 2 xfailed, 3 xpassed in 1.06 seconds ============
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this reports both the xfailed &lt;em&gt;and&lt;/em&gt; the xpassed counts, it shows how much work is still left to be done before the &lt;code&gt;xfail&lt;/code&gt; decorator can be safely removed.&lt;/p&gt;
&lt;h4 id="Structuring_code_for_testable_documentation_180"&gt;Structuring code for testable documentation&lt;/h4&gt;
&lt;p&gt;A benefit of comprehensive unit testing is that it encourages you to design your code in a way that is easy to test. In my experience this leads to much higher code quality in general: it encourages separation of concerns and cleanly decoupled components.&lt;/p&gt;
&lt;p&gt;My hope is that documentation unit tests will have a similar effect. I’m already starting to think about ways of restructuring my code such that I can cleanly introspect it for the areas that need to be documented. I’m looking forward to discovering code design patterns that help support this goal.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/design-patterns"&gt;design-patterns&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/restructuredtext"&gt;restructuredtext&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/testing"&gt;testing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pytest"&gt;pytest&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="design-patterns"/><category term="documentation"/><category term="restructuredtext"/><category term="testing"/><category term="datasette"/><category term="pytest"/></entry><entry><title>Restructured Text to Anything</title><link href="https://simonwillison.net/2007/Sep/13/rsta/#atom-tag" rel="alternate"/><published>2007-09-13T15:54:44+00:00</published><updated>2007-09-13T15:54:44+00:00</updated><id>https://simonwillison.net/2007/Sep/13/rsta/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://rst2a.com/"&gt;Restructured Text to Anything&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Slick set of online tools for converting Restructured Text (one of the more mature wiki-style markup languages) to HTML or PDF. Includes a nice looking API. Powered by Django.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdf"&gt;pdf&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;/p&gt;



</summary><category term="django"/><category term="html"/><category term="pdf"/><category term="python"/><category term="restructuredtext"/></entry><entry><title>A myriad of markup systems</title><link href="https://simonwillison.net/2004/Apr/13/myriadOfMarkupSystems/#atom-tag" rel="alternate"/><published>2004-04-13T04:58:54+00:00</published><updated>2004-04-13T04:58:54+00:00</updated><id>https://simonwillison.net/2004/Apr/13/myriadOfMarkupSystems/#atom-tag</id><summary type="html">
    &lt;p&gt;It's hard to avoid the legions of custom markup systems out there these days. Every Wiki has it's own syntactical quirks, while packages like &lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt;, &lt;a href="http://www.textism.com/tools/textile/"&gt;Textile&lt;/a&gt;, &lt;a href="http://pear.php.net/pepr/pepr-bbcode-help.php"&gt;BBCode&lt;/a&gt; (in dozens of variants), &lt;a href="http://docutils.sourceforge.net/rst.html"&gt;reStructuredText&lt;/a&gt; offer easy ways of hooking markup conversion in to existing applications. When it comes to being totally over-implemented and infuratingly inconsistent, markup systems are rapidly catching up with template packages. Never one to miss out on an opportunity to reinvent the wheel, I've worked on several of each ;)&lt;/p&gt;

&lt;p&gt;My most recent markup handling attempt has just been published as part of my &lt;a href="http://www.sitepoint.com/article/bookmarklets" title="Better Living Through Bookmarklets"&gt;SitePoint article on Bookmarklets&lt;/a&gt; (&lt;a href="http://www.google.com/search?q=%22better+living+through+*%22"&gt;cliché&lt;/a&gt;). It's a structured markup language in a bookmarklet: activate the bookmarklet to convert the text in any textarea on a page to &lt;acronym title="eXtensible HyperText Markup Language"&gt;XHTML&lt;/acronym&gt;. The syntax is ridiculously simple, and serves my limited needs just fine:&lt;/p&gt;

&lt;pre&gt;&lt;samp&gt;
= This is a header

Here is a paragraph.

* This is a list of items
* Another item in the list
&lt;/samp&gt;&lt;/pre&gt;

&lt;p&gt;Converts to:&lt;/p&gt;

&lt;pre&gt;&lt;code class="xhtml"&gt;
&amp;lt;h4&amp;gt;This is a header&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;Here is a paragraph.&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
 &amp;lt;li&amp;gt;This is a list of items&amp;lt;/li&amp;gt;
 &amp;lt;li&amp;gt;Another item in the list&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The algorithm is simple, and easily portable to any language you care to mention:&lt;/p&gt;

&lt;ol&gt;
 &lt;li&gt;Normalise newlines to \n, for cross-platform consistency.&lt;/li&gt;
 &lt;li&gt;Split the text up on double newlines, to create a list of blocks.&lt;/li&gt;
 &lt;li&gt;For each block:
 &lt;ol&gt;
  &lt;li&gt;If it starts with an equals sign, wrap it in header tags.&lt;/li&gt;
  &lt;li&gt;If it starts with an asterisk, split it in to lines, make each a list item (stripping off the asterisk at the start of the line if required) and glue them all together inside a &lt;code class="xhtml"&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Otherwise, wrap it in a &lt;code class="xhtml"&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag &lt;em&gt;provided it doesn't have one already&lt;/em&gt;.&lt;/li&gt;
 &lt;/ol&gt;&lt;/li&gt;
 &lt;li&gt;Glue everything back together again with a couple of newlines, to make the underlying &lt;acronym title="eXtensible HyperText Markup Language"&gt;XHTML&lt;/acronym&gt; look pretty.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The bookmarklet comes in two flavours: &lt;a href="javascript:(function() { var tas = document.getElementsByTagName(&amp;apos;textarea&amp;apos;); for (var i = 0; i &amp;lt; tas.length; i++) { var ta = tas[i]; var text = ta.value.replace(/(\r\n|\r|\n)/g, &amp;apos;\n&amp;apos;); var paras = text.split(/\n{2,}/); for (var i = 0; i &amp;lt; paras.length; i++) { if (/^\* /.test(paras[i])) { var lines = paras[i].split(&amp;apos;\n&amp;apos;); for (var j = 0; j &amp;lt; lines.length; j++) { lines[j] = &amp;apos; &amp;lt;li&amp;gt;&amp;apos; + lines[j].replace(/^\* /, &amp;apos;&amp;apos;) + &amp;apos;&amp;lt;/li&amp;gt;&amp;apos;; } paras[i] = &amp;apos;&amp;lt;ul&amp;gt;\n&amp;apos; + lines.join(&amp;apos;\n&amp;apos;) + &amp;apos;\n&amp;lt;/ul&amp;gt;&amp;apos;; } if (/^= /.test(paras[i])) { paras[i] = &amp;apos;&amp;lt;h4&amp;gt;&amp;apos; + paras[i].replace(/^= /, &amp;apos;&amp;apos;) + &amp;apos;&amp;lt;/h4&amp;gt;&amp;apos;; } if (!/^&amp;lt;(p|ul|li|h4)&amp;gt;/.test(paras[i])) { paras[i] = &amp;apos;&amp;lt;p&amp;gt;&amp;apos; + paras[i]; } if (!/&amp;lt;\/(p|ul|li|h4)&amp;gt;$/.test(paras[i])) { paras[i] += &amp;apos;&amp;lt;/p&amp;gt;&amp;apos;; } } ta.value = paras.join(&amp;apos;\n\n&amp;apos;); } })();"&gt;Expand HTML Shorthand&lt;/a&gt; (the full version) and &lt;a href="javascript:(function(){var tas=document.getElementsByTagName(&amp;apos;textarea&amp;apos;),ta,t,ps,i,l,j;for(i=0;i&amp;lt;tas.length;i++){ta=tas[i]; t=ta.value.replace(/(\r\n|\r|\n)/g,&amp;apos;\n&amp;apos;);ps=t.split(/\n{2,}/);for(i=0;i&amp;lt;ps.length;i++){if(/^\* /.test(ps[i])){l=ps[i].split(&amp;apos;\n&amp;apos;);for(j=0;j&amp;lt;l.length;j++){l[j]=&amp;apos; &amp;lt;li&amp;gt;&amp;apos;+l[j].replace(/^\* /,&amp;apos;&amp;apos;)+&amp;apos;&amp;lt;/li&amp;gt;&amp;apos;;}ps[i]=&amp;apos;&amp;lt;ul&amp;gt;\n&amp;apos;+l.join(&amp;apos;\n&amp;apos;)+&amp;apos;\n&amp;lt;/ul&amp;gt;&amp;apos;;}if(!/^&amp;lt;(p|ul|li|h4)&amp;gt;/.test(ps[i])){ps[i]=&amp;apos; &amp;lt;p&amp;gt;&amp;apos;+ps[i];}if(!/&amp;lt;\/(p|ul|li|h4)&amp;gt;$/.test(ps[i])){ps[i]+=&amp;apos;&amp;lt;/p&amp;gt;&amp;apos;;}}ta.value=ps.join(&amp;apos;\n\n&amp;apos;);}})();"&gt;Expand HTML Shorthand IE&lt;/a&gt;, which loses header support in order to fit within &lt;acronym title="Internet Explorer"&gt;IE&lt;/acronym&gt;'s crippling 508 character limit. A more capable bookmarklet could be built using the import-script-stub method &lt;a href="http://www.sitepoint.com/article/bookmarklets/2"&gt;described in my article&lt;/a&gt;, but the implementation of such a thing is left as an exercise for the reader (I've &lt;em&gt;always&lt;/em&gt; wanted to say that).&lt;/p&gt;

&lt;p&gt;Incidentally, there's a very common bug in markup systems that allow inline styles that proves extremely difficult to fix: that of improperly nested tags. Say you have a system where *&lt;strong&gt;text&lt;/strong&gt;* is bold and _&lt;em&gt;text&lt;/em&gt;_ is italic; what happens when the user enters _&lt;em&gt;italic&lt;/em&gt;*&lt;strong&gt;&lt;em&gt;italic-bold&lt;/em&gt;&lt;/strong&gt;_&lt;strong&gt;bold&lt;/strong&gt;*? Most systems (and that includes Markdown, Textile and my home-rolled Python solution) use naive regular expressions for inline markup processing and will output vadly formed &lt;acronym title="eXtensible HyperText Markup Language"&gt;XHTML&lt;/acronym&gt;: &lt;code class="xhtml"&gt;&amp;lt;em&amp;gt;italic&amp;lt;strong&amp;gt;italic-bold&amp;lt;/em&amp;gt;bold&amp;lt;/strong&amp;gt;&lt;/code&gt;. To truly solve this problem requires a context-sensitive parser, which involves an unpleasantly large amount of effort to solve what looks like a simple bug.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/bookmarklets"&gt;bookmarklets&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/restructuredtext"&gt;restructuredtext&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/markdown"&gt;markdown&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="bookmarklets"/><category term="restructuredtext"/><category term="markdown"/></entry></feed>