From 0e21cf42d0c52a1cff79ef8bf9ed5d8322694f21 Mon Sep 17 00:00:00 2001 From: Dan Allan Date: Tue, 23 May 2023 14:01:26 -0400 Subject: [PATCH 1/3] Add some pages from the NSLS-II guide. --- .../docs/refraction.py | 32 ++ .../docs/test_examples.py | 26 ++ .../docs/the-code-itself.rst | 374 ++++++++++++++++++ .../docs/writing-docs.rst | 297 ++++++++++++++ 4 files changed, 729 insertions(+) create mode 100644 {{cookiecutter.project_name}}/docs/refraction.py create mode 100644 {{cookiecutter.project_name}}/docs/test_examples.py create mode 100644 {{cookiecutter.project_name}}/docs/the-code-itself.rst create mode 100644 {{cookiecutter.project_name}}/docs/writing-docs.rst diff --git a/{{cookiecutter.project_name}}/docs/refraction.py b/{{cookiecutter.project_name}}/docs/refraction.py new file mode 100644 index 00000000..2e2c4e96 --- /dev/null +++ b/{{cookiecutter.project_name}}/docs/refraction.py @@ -0,0 +1,32 @@ +# example/refraction.py + +import numpy as np + + +def snell(theta_inc, n1, n2): + """ + Compute the refraction angle using Snell's Law. + + See https://en.wikipedia.org/wiki/Snell%27s_law + + Parameters + ---------- + theta_inc : float + Incident angle in radians. + n1, n2 : float + The refractive index of medium of origin and destination medium. + + Returns + ------- + theta : float + refraction angle + + Examples + -------- + A ray enters an air--water boundary at pi/4 radians (45 degrees). + Compute exit angle. + + >>> snell(np.pi/4, 1.00, 1.33) + 0.5605584137424605 + """ + return np.arcsin(n1 / n2 * np.sin(theta_inc)) diff --git a/{{cookiecutter.project_name}}/docs/test_examples.py b/{{cookiecutter.project_name}}/docs/test_examples.py new file mode 100644 index 00000000..7c925101 --- /dev/null +++ b/{{cookiecutter.project_name}}/docs/test_examples.py @@ -0,0 +1,26 @@ +# example/tests/test_examples.py + +import numpy as np +from ..refraction import snell +# (The above is equivalent to `from example.refraction import snell`. +# Read on for why.) + + +def test_perpendicular(): + # For any indexes, a ray normal to the surface should not bend. + # We'll try a couple different combinations of indexes.... + + actual = snell(0, 2.00, 3.00) + expected = 0 + assert actual == expected + + actual = snell(0, 3.00, 2.00) + expected = 0 + assert actual == expected + + +def test_air_water(): + n_air, n_water = 1.00, 1.33 + actual = snell(np.pi/4, n_air, n_water) + expected = 0.5605584137424605 + assert np.allclose(actual, expected) diff --git a/{{cookiecutter.project_name}}/docs/the-code-itself.rst b/{{cookiecutter.project_name}}/docs/the-code-itself.rst new file mode 100644 index 00000000..b5f340da --- /dev/null +++ b/{{cookiecutter.project_name}}/docs/the-code-itself.rst @@ -0,0 +1,374 @@ +=============== +The Code Itself +=============== + +In this section you will: + +* Put some scientific code in your new Python package. +* Update your package's list of dependencies in ``requirements.txt``. +* Write a test and run the test suite. +* Use a "linter" and style-checker. +* Commit your changes to git and sync your changes with GitHub. + +A simple function with inline documentation +------------------------------------------- + +Let's write a simple function that encodes +`Snell's Law `_ and include it in +our Python package. + +Look again at the directory structure. + +.. code-block:: none + + example/ + ├── .flake8 + ├── .gitattributes + ├── .gitignore + ├── .travis.yml + ├── AUTHORS.rst + ├── CONTRIBUTING.rst + ├── LICENSE + ├── MANIFEST.in + ├── README.rst + ├── docs + │   ├── Makefile + │   ├── build + │   ├── make.bat + │   └── source + │   ├── _static + │   │   └── .placeholder + │   ├── _templates + │   ├── conf.py + │   ├── index.rst + │   ├── installation.rst + │   ├── release-history.rst + │   └── usage.rst + ├── example + │   ├── __init__.py + │   ├── _version.py + │   └── tests + │   └── test_examples.py + ├── requirements-dev.txt + ├── requirements.txt + ├── setup.cfg + ├── setup.py + └── versioneer.py + +Our scientific code should go in the ``example/`` subdirectory, next to +``__init__.py``. Let's make a new file in that directory named +``refraction.py``, meaning our new layout will be: + +.. code-block:: none + + ├── example + │   ├── __init__.py + │   ├── _version.py + │   ├── refraction.py + │   └── tests + │   └── test_examples.py + +This is our new file. You may follow along exactly or, instead, make a file +with a different name and your own scientific function. + +.. literalinclude:: refraction.py + +Notice that this example includes inline documentation --- a "docstring". This +is extremely useful for collaborators, and the most common collaborator is +Future You! + +Further, by following the +`numpydoc standard `_, +we will be able to automatically generate nice-looking HTML documentation +later. Notable features: + +* There is a succinct, one-line summary of the function's purpose. It must one + line. +* (Optional) There is an paragraph elaborating on that summary. +* There is a section listing input parameters, with the structure + + .. code-block :: none + + parameter_name : parameter_type + optional description + + Note that space before the ``:``. That is part of the standard. +* Similar parameters may be combined into one entry for brevity's sake, as we + have done for ``n1, n2`` here. +* There is a section describing what the function returns. +* (Optional) There is a section of one or more examples. + +We will revisit docstrings in the section on :doc:`writing-docs`. + +Update Requirements +------------------- + +Notice that our package has a third-party dependency, numpy. We should +update our package's ``requirements.txt``. + +.. code-block:: text + + # requirements.txt + + # List required packages in this file, one per line. + numpy + +Our cookiecutter configured ``setup.py`` to read this file. It will ensure that +numpy is installed when our package is installed. + +We can test it by reinstalling the package. + +.. code-block:: bash + + python3 -m pip install -e . + +Try it +------ + +Try importing and using the function. + + +.. code-block:: python + + >>> from example.refraction import snell + >>> import numpy as np + >>> snell(np.pi/4, 1.00, 1.33) + 1.2239576240104186 + +The docstring can be viewed with :func:`help`. + +.. code-block:: python + + >>> help(snell) + +Or, as a shortcut, use ``?`` in IPython/Jupyter. + +.. ipython:: python + :verbatim: + + snell? + +Run the Tests +------------- + +You should add a test right away while the details are still fresh in mind. +Writing tests encourages you to write modular, reusable code, which is easier +to test. + +The cookiecutter template included an example test suite with one test: + +.. code-block:: python + + # example/tests/test_examples.py + + def test_one_plus_one_is_two(): + assert 1 + 1 == 2 + +Before writing our own test, let's practice running that test to check that +everything is working. + +.. important:: + + We assume you have installed the "development requirements," as covered + in :doc:`preliminaries`. If you are not sure whether you have, there is no + harm in running this a second time: + + .. code-block:: bash + + python3 -m pip install --upgrade -r requirements-dev.txt + +.. code-block:: bash + + python3 -m pytest + +This walks through all the directories and files in our package that start with +the word 'test' and collects all the functions whose name also starts with +``test``. Currently, there is just one, ``test_one_plus_one_is_two``. +``pytest`` runs that function. If no exceptions are raised, the test passes. + +The output should look something like this: + +.. code-block:: bash + + ======================================== test session starts ======================================== + platform darwin -- Python 3.6.4, pytest-3.6.2, py-1.5.4, pluggy-0.6.0 + benchmark: 3.1.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) + rootdir: /private/tmp/test11/example, inifile: + plugins: xdist-1.22.2, timeout-1.2.1, rerunfailures-4.0, pep8-1.0.6, lazy-fixture-0.3.0, forked-0.2, benchmark-3.1.1 + collected 1 item + + example/tests/test_examples.py . [100%] + + ===================================== 1 passed in 0.02 seconds ====================================== + +.. note:: + + The output of ``pytest`` is customizable. Commonly useful command-line + arguments include: + + * ``-v`` verbose + * ``-s`` Do not capture stdout/err per test. + * ``-k EXPRESSION`` Filter tests by pattern-matching test name. + + Consult the `pytest documentation `_ + for more. + +Write a Test +------------ + +Let's add a test to ``test_examples.py`` that exercises our ``snell`` function. +We can delete ``test_one_plus_one_is_two`` now. + +.. literalinclude:: test_examples.py + +Things to notice: + +* It is sometime useful to put multiple ``assert`` statements in one test. You + should make a separate test for each *behavior* that you are checking. When a + monolithic, multi-step tests fails, it's difficult to figure out why. +* When comparing floating-point numbers (as opposed to integers) you should not + test for exact equality. Use :func:`numpy.allclose`, which checks for + equality within a (configurable) tolerance. Numpy provides several + `testing utilities `_, + which should always be used when testing numpy arrays. +* Remember that the names of all test modules and functions must begin with + ``test`` or they will not be picked up by pytest! + +See :doc:`advanced-testing` for more. + +"Lint": Check for suspicious-looking code +----------------------------------------- + +A `linter `_ is a tool that +analyzes code to flag potential errors. For example, it can catch variables you +defined by never used, which is likely a spelling error. + +The cookiecutter configured ``flake8`` for this purpose. Flake8 checks for +"lint" and also enforces the standard Python coding style, +`PEP8 `_. Enforcing +consistent style helps projects stay easy to read and maintain as they grow. +While not all projects strictly enfore PEP8, we generally recommend it. + +.. important:: + + We assume you have installed the "development requirements," as covered + in :doc:`preliminaries`. If you are not sure whether you have, there is no + harm in running this a second time: + + .. code-block:: bash + + python3 -m pip install --upgrade -r requirements-dev.txt + +.. code-block:: bash + + python3 -m flake8 + +This will list linting or stylistic errors. If there is no output, all is well. +See the `flake8 documentation `_ for more. + +Commit and Push Changes +----------------------- + +Remember to commit your changes to version control and push them up to GitHub. + +.. important:: + + The following is a quick reference that makes some assumptions about your + local configuration and workflow. + + This usage is part of a workflow named *GitHub flow*. See + `this guide `_ for more. + +Remember that at any time you may use ``git status`` to check which branch +you are currently on and which files have uncommitted changes. Use ``git diff`` +to review the content of those changes. + +1. If you have not already done so, create a new "feature branch" for this work + with some descriptive name. + + .. code-block:: bash + + git checkout master # Starting from the master branch... + git checkout -b add-snell-function # ...make a new branch. + +2. Stage changes to be committed. In our example, we have created one new file + and changed an existing one. We ``git add`` both. + + .. code-block:: bash + + git add example/refraction.py + git add example/tests/test_examples.py + +3. Commit changes. + + .. code-block:: bash + + git commit -m "Add snell function and tests." + +4. Push changes to remote repository on GitHub. + + .. code-block:: bash + + git push origin add-snell-function + +5. Repeat steps 2-4 until you are happy with this feature. + +6. Create a Pull Request --- or merge to master. + + When you are ready for collaborators to review your work and consider merging + the ``add-snell-function`` branch into the ``master`` branch, + `create a pull request `_. + Even if you presently have no collaborators, going through this process is a + useful way to document the history of changes to the project for any *future* + collaborators (and Future You). + + However, if you are in the early stages of just getting a project up and you + are the only developer, you might skip the pull request step and merge the + changes yourself. + + .. code-block:: bash + + git checkout master + # Ensure local master branch is up to date with remote master branch. + git pull --ff-only origin master + # Merge the commits from add-snell-function into master. + git merge add-snell-function + # Update the remote master branch. + git push origin master + +Multiple modules +---------------- + +We created just one module, ``example.refraction``. We might eventually grow a +second module --- say, ``example.utils``. Some brief advice: + +* When in doubt, resist the temptation to grow deep taxonomies of modules and + sub-packages, lest it become difficult for users and collaborators to + remember where everything is. The Python built-in libraries are generally + flat. + +* When making intra-package imports, we recommend relative imports. + + This works: + + .. code-block:: bash + + # example/refraction.py + + from example import utils + from example.utils import some_function + + but this is equivalent, and preferred: + + .. code-block:: bash + + # example/refraction.py + + from . import utils + from .utils import some_function + + For one thing, if you change the name of the package in the future, you won't + need to update this file. + +* Take care to avoid circular imports, wherein two modules each import the + other. diff --git a/{{cookiecutter.project_name}}/docs/writing-docs.rst b/{{cookiecutter.project_name}}/docs/writing-docs.rst new file mode 100644 index 00000000..73123ff2 --- /dev/null +++ b/{{cookiecutter.project_name}}/docs/writing-docs.rst @@ -0,0 +1,297 @@ +===================== +Writing Documentation +===================== + +In this section you will: + +* Generate HTML documentation using Sphinx, starting from a working example + provided by the cookiecutter template. +* Edit ``usage.rst`` to add API documentation and narrative documentation. +* Learn how to incorporate code examples, IPython examples, matplotlib plots, + and typeset math. + +Build the docs +-------------- + +Almost all scientific Python projects use the +`Sphinx documentation generator `_. +The cookiecutter template provided a working example with some popular +extensions installed and some sample pages. + +.. code-block:: none + + example/ + (...) + ├── docs + │   ├── Makefile + │   ├── build + │   ├── make.bat + │   └── source + │   ├── _static + │   │   └── .placeholder + │   ├── _templates + │   ├── conf.py + │   ├── index.rst + │   ├── installation.rst + │   ├── release-history.rst + │   └── usage.rst + (...) + +The ``.rst`` files are source code for our documentation. To build HTML pages +from this source, run: + +.. code-block:: bash + + make -C docs html + +You should see some log message ending in ``build succeeded.`` + +This output HTML will be located in ``docs/build/html``. In your Internet +browser, open ``file://.../docs/build/html/index.html``, where ``...`` is the +path to your project directory. If you aren't sure sure where that is, type +``pwd``. + +Update the docs +--------------- + +The source code for the documentation is located in ``docs/source/``. +Sphinx uses a markup language called ReStructured Text (.rst). We refer you to +`this primer `_ +to learn how to denote headings, links, lists, cross-references, etc. + +Sphinx formatting is sensitive to whitespace and generally quite picky. We +recommend running ``make -C docs html`` often to check that the documentation +builds successfully. Remember to commit your changes to git periodically. + +Good documentation includes both: + +* API (Application Programming Interface) documentation, listing every public + object in the library and its usage +* Narrative documentation interleaving prose and code examples to explain how + and why a library is meant to be used + +API Documentation +----------------- + +Most the work of writing good API documentation goes into writing good, +accurate docstrings. Sphinx can scrape that content and generate HTML from it. +Again, most scientific Python libraries use the +`numpydoc standard `_, +which looks like this: + +.. literalinclude:: refraction.py + +Autodoc +^^^^^^^ + +In an rst file, such as ``docs/source/usage.rst``, we can write: + +.. code-block:: rst + + .. autofunction:: example.refraction.snell + +which renders in HTML like so: + +.. autofunction:: example.refraction.snell + :noindex: + +From here we refer you to the +`sphinx autodoc documentation `_. + +Autosummary +^^^^^^^^^^^ + +If you have many related objects to document, it may be better to display them +in a table. Each row will include the name, the signature (optional), and the +one-line description from the docstring. + +In rst we can write: + +.. code-block:: rst + + .. autosummary:: + :toctree: generated/ + + example.refraction.snell + +which renders in HTML like so: + +.. autosummary:: + :toctree: generated/ + + example.refraction.snell + +It links to the full rendered docstring on a separate page that is +automatically generated. + +From here we refer you to the +`sphinx autosummary documentation `_. + +Narrative Documentation +----------------------- + +Code Blocks +^^^^^^^^^^^ + +Code blocks can be interspersed with narrative text like this: + +.. code-block:: rst + + Scientific libraries conventionally use radians. Numpy provides convenience + functions for converting between radians and degrees. + + .. code-block:: python + + import numpy as np + + + np.deg2rad(90) # pi / 2 + np.rad2deg(np.pi / 2) # 90.0 + +which renders in HTML as: + +Scientific libraries conventionally use radians. Numpy provides convenience +functions for converting between radians and degrees. + +.. code-block:: python + + import numpy as np + + + np.deg2rad(90) # pi / 2 + np.rad2deg(np.pi / 2) # 90.0 + +To render short code expressions inline, surround them with back-ticks. This: + +.. code-block:: rst + + Try ``snell(0, 1, 1.33)``. + +renders in HTML as: + + +Try ``snell(0, 1, 1.33)``. + +Embedded Scripts +^^^^^^^^^^^^^^^^ + +For lengthy examples with tens of lines or more, it can be convenient to embed +the content of a .py file rather than writing it directly into the +documentation. + +This can be done using the directive + +.. code-block:: rest + + .. literalinclude:: examples/some_example.py + +where the path is given relative to the current file's path. Thus, relative to +the repository's root directory, the path to this example script would be +``docs/source/examples/some_example.py``. + +From here we refer you to the +`sphinx code example documentation `_. + +To go beyond embedded scripts to a more richly-featured example gallery that +shows scripts and their outputs, we encourage you to look at +`sphinx-gallery `_. + +IPython Examples +^^^^^^^^^^^^^^^^ + +IPython's sphinx extension, which is included by the cookiecutter template, +makes it possible to execute example code and capture its output when the +documentation is built. This rst code: + +.. code-block:: rst + + .. ipython:: python + + 1 + 1 + +renders in HTML as: + +.. ipython:: python + + 1 + 1 + +From here we refer you to the +`IPython sphinx directive documentation `_. + +Plots +^^^^^ + +Matplotlib's sphinx extension, which is included by the cookiecutter template, +makes it possible to display matplotlib figures in line. This rst code: + +.. code-block:: rst + + .. plot:: + + import matplotlib.pyplot as plt + fig, ax = plt.subplots() + ax.plot([1, 1, 2, 3, 5, 8]) + +renders in HTML as: + +.. plot:: + + import matplotlib.pyplot as plt + fig, ax = plt.subplots() + ax.plot([1, 1, 2, 3, 5, 8]) + +From here we refer you to the +`matplotlib plot directive documentation `_. + +Math (LaTeX) +^^^^^^^^^^^^ + +Sphinx can render LaTeX typeset math in the browser (using +`MathJax `_). This rst code: + +.. code-block:: rst + + .. math:: + + \int_0^a x\,dx = \frac{1}{2}a^2 + +renders in HTML as: + +.. math:: + + \int_0^a x\,dx = \frac{1}{2}a^2 + +This notation can also be used in docstrings. For example, we could add +the equation of Snell's Law to the docstring of +:func:`~example.refraction.snell`. + +Math can also be written inline. This rst code: + +.. code-block:: rst + + The value of :math:`\pi` is 3.141592653.... + +renders in HTML as: + + The value of :math:`\pi` is 3.141592653.... + +Referencing Documented Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can create links to documented functions like so: + +.. code-block:: rst + + The :func:`example.refraction.snell` function encodes Snell's Law. + +The :func:`example.refraction.snell` function encodes Snell's Law. + +Adding a ``~`` omits the module path from the link text. + +.. code-block:: rst + + The :func:`~example.refraction.snell` function encodes Snell's Law. + +The :func:`~example.refraction.snell` function encodes Snell's Law. + +See `the Sphinx documentation `_ for more. From b24d68ea5f4a6708ac83cac5c5596fff5678901d Mon Sep 17 00:00:00 2001 From: Dan Allan Date: Tue, 23 May 2023 14:25:05 -0400 Subject: [PATCH 2/3] Docs go in the guide, not in the cookie. --- .../docs => docs/pages/developers}/conf.py | 0 .../docs => docs/pages/developers}/refraction.py | 0 .../docs => docs/pages/developers}/test_examples.py | 0 .../docs => docs/pages/developers}/the-code-itself.rst | 0 .../docs => docs/pages/developers}/writing-docs.rst | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {{{cookiecutter.project_name}}/docs => docs/pages/developers}/conf.py (100%) rename {{{cookiecutter.project_name}}/docs => docs/pages/developers}/refraction.py (100%) rename {{{cookiecutter.project_name}}/docs => docs/pages/developers}/test_examples.py (100%) rename {{{cookiecutter.project_name}}/docs => docs/pages/developers}/the-code-itself.rst (100%) rename {{{cookiecutter.project_name}}/docs => docs/pages/developers}/writing-docs.rst (100%) diff --git a/{{cookiecutter.project_name}}/docs/conf.py b/docs/pages/developers/conf.py similarity index 100% rename from {{cookiecutter.project_name}}/docs/conf.py rename to docs/pages/developers/conf.py diff --git a/{{cookiecutter.project_name}}/docs/refraction.py b/docs/pages/developers/refraction.py similarity index 100% rename from {{cookiecutter.project_name}}/docs/refraction.py rename to docs/pages/developers/refraction.py diff --git a/{{cookiecutter.project_name}}/docs/test_examples.py b/docs/pages/developers/test_examples.py similarity index 100% rename from {{cookiecutter.project_name}}/docs/test_examples.py rename to docs/pages/developers/test_examples.py diff --git a/{{cookiecutter.project_name}}/docs/the-code-itself.rst b/docs/pages/developers/the-code-itself.rst similarity index 100% rename from {{cookiecutter.project_name}}/docs/the-code-itself.rst rename to docs/pages/developers/the-code-itself.rst diff --git a/{{cookiecutter.project_name}}/docs/writing-docs.rst b/docs/pages/developers/writing-docs.rst similarity index 100% rename from {{cookiecutter.project_name}}/docs/writing-docs.rst rename to docs/pages/developers/writing-docs.rst From 3f74e46141c164a26990a8dd91420fa7cde2717a Mon Sep 17 00:00:00 2001 From: Dan Allan Date: Tue, 23 May 2023 14:30:23 -0400 Subject: [PATCH 3/3] Convert rst to MyST Markdown with rst-to-myst. --- docs/pages/developers/the-code-itself.md | 372 +++++++++++++++++ docs/pages/developers/the-code-itself.rst | 374 ------------------ docs/pages/developers/writing-docs.md | 291 ++++++++++++++ docs/pages/developers/writing-docs.rst | 297 -------------- .../docs}/conf.py | 0 5 files changed, 663 insertions(+), 671 deletions(-) create mode 100644 docs/pages/developers/the-code-itself.md delete mode 100644 docs/pages/developers/the-code-itself.rst create mode 100644 docs/pages/developers/writing-docs.md delete mode 100644 docs/pages/developers/writing-docs.rst rename {docs/pages/developers => {{cookiecutter.project_name}}/docs}/conf.py (100%) diff --git a/docs/pages/developers/the-code-itself.md b/docs/pages/developers/the-code-itself.md new file mode 100644 index 00000000..c197f042 --- /dev/null +++ b/docs/pages/developers/the-code-itself.md @@ -0,0 +1,372 @@ +# The Code Itself + +In this section you will: + +- Put some scientific code in your new Python package. +- Update your package's list of dependencies in `requirements.txt`. +- Write a test and run the test suite. +- Use a "linter" and style-checker. +- Commit your changes to git and sync your changes with GitHub. + +## A simple function with inline documentation + +Let's write a simple function that encodes +[Snell's Law](https://en.wikipedia.org/wiki/Snell%27s_law) and include it in +our Python package. + +Look again at the directory structure. + +```none +example/ +├── .flake8 +├── .gitattributes +├── .gitignore +├── .travis.yml +├── AUTHORS.rst +├── CONTRIBUTING.rst +├── LICENSE +├── MANIFEST.in +├── README.rst +├── docs +│   ├── Makefile +│   ├── build +│   ├── make.bat +│   └── source +│   ├── _static +│   │   └── .placeholder +│   ├── _templates +│   ├── conf.py +│   ├── index.rst +│   ├── installation.rst +│   ├── release-history.rst +│   └── usage.rst +├── example +│   ├── __init__.py +│   ├── _version.py +│   └── tests +│   └── test_examples.py +├── requirements-dev.txt +├── requirements.txt +├── setup.cfg +├── setup.py +└── versioneer.py +``` + +Our scientific code should go in the `example/` subdirectory, next to +`__init__.py`. Let's make a new file in that directory named +`refraction.py`, meaning our new layout will be: + +```none +├── example +│   ├── __init__.py +│   ├── _version.py +│   ├── refraction.py +│   └── tests +│   └── test_examples.py +``` + +This is our new file. You may follow along exactly or, instead, make a file +with a different name and your own scientific function. + +```{literalinclude} refraction.py +``` + +Notice that this example includes inline documentation --- a "docstring". This +is extremely useful for collaborators, and the most common collaborator is +Future You! + +Further, by following the +[numpydoc standard](https://numpydoc.readthedocs.io/en/latest/format.html), +we will be able to automatically generate nice-looking HTML documentation +later. Notable features: + +- There is a succinct, one-line summary of the function's purpose. It must one + line. + +- (Optional) There is an paragraph elaborating on that summary. + +- There is a section listing input parameters, with the structure + + ```none + parameter_name : parameter_type + optional description + ``` + + Note that space before the `:`. That is part of the standard. + +- Similar parameters may be combined into one entry for brevity's sake, as we + have done for `n1, n2` here. + +- There is a section describing what the function returns. + +- (Optional) There is a section of one or more examples. + +We will revisit docstrings in the section on {doc}`writing-docs`. + +## Update Requirements + +Notice that our package has a third-party dependency, numpy. We should +update our package's `requirements.txt`. + +```text +# requirements.txt + +# List required packages in this file, one per line. +numpy +``` + +Our cookiecutter configured `setup.py` to read this file. It will ensure that +numpy is installed when our package is installed. + +We can test it by reinstalling the package. + +```bash +python3 -m pip install -e . +``` + +## Try it + +Try importing and using the function. + +```python +>>> from example.refraction import snell +>>> import numpy as np +>>> snell(np.pi/4, 1.00, 1.33) +1.2239576240104186 +``` + +The docstring can be viewed with {func}`help`. + +```python +>>> help(snell) +``` + +Or, as a shortcut, use `?` in IPython/Jupyter. + +```{eval-rst} +.. ipython:: python + :verbatim: + + snell? +``` + +## Run the Tests + +You should add a test right away while the details are still fresh in mind. +Writing tests encourages you to write modular, reusable code, which is easier +to test. + +The cookiecutter template included an example test suite with one test: + +```python +# example/tests/test_examples.py + +def test_one_plus_one_is_two(): + assert 1 + 1 == 2 +``` + +Before writing our own test, let's practice running that test to check that +everything is working. + +:::{important} +We assume you have installed the "development requirements," as covered +in {doc}`preliminaries`. If you are not sure whether you have, there is no +harm in running this a second time: + +```bash +python3 -m pip install --upgrade -r requirements-dev.txt +``` +::: + +```bash +python3 -m pytest +``` + +This walks through all the directories and files in our package that start with +the word 'test' and collects all the functions whose name also starts with +`test`. Currently, there is just one, `test_one_plus_one_is_two`. +`pytest` runs that function. If no exceptions are raised, the test passes. + +The output should look something like this: + +```bash +======================================== test session starts ======================================== +platform darwin -- Python 3.6.4, pytest-3.6.2, py-1.5.4, pluggy-0.6.0 +benchmark: 3.1.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) +rootdir: /private/tmp/test11/example, inifile: +plugins: xdist-1.22.2, timeout-1.2.1, rerunfailures-4.0, pep8-1.0.6, lazy-fixture-0.3.0, forked-0.2, benchmark-3.1.1 +collected 1 item + +example/tests/test_examples.py . [100%] + +===================================== 1 passed in 0.02 seconds ====================================== +``` + +:::{note} +The output of `pytest` is customizable. Commonly useful command-line +arguments include: + +- `-v` verbose +- `-s` Do not capture stdout/err per test. +- `-k EXPRESSION` Filter tests by pattern-matching test name. + +Consult the [pytest documentation](https://docs.pytest.org/en/latest/) +for more. +::: + +## Write a Test + +Let's add a test to `test_examples.py` that exercises our `snell` function. +We can delete `test_one_plus_one_is_two` now. + +```{literalinclude} test_examples.py +``` + +Things to notice: + +- It is sometime useful to put multiple `assert` statements in one test. You + should make a separate test for each *behavior* that you are checking. When a + monolithic, multi-step tests fails, it's difficult to figure out why. +- When comparing floating-point numbers (as opposed to integers) you should not + test for exact equality. Use {func}`numpy.allclose`, which checks for + equality within a (configurable) tolerance. Numpy provides several + [testing utilities](https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.testing.html), + which should always be used when testing numpy arrays. +- Remember that the names of all test modules and functions must begin with + `test` or they will not be picked up by pytest! + +See {doc}`advanced-testing` for more. + +## "Lint": Check for suspicious-looking code + +A [linter]() is a tool that +analyzes code to flag potential errors. For example, it can catch variables you +defined by never used, which is likely a spelling error. + +The cookiecutter configured `flake8` for this purpose. Flake8 checks for +"lint" and also enforces the standard Python coding style, +[PEP8](https://www.python.org/dev/peps/pep-0008/?#introduction). Enforcing +consistent style helps projects stay easy to read and maintain as they grow. +While not all projects strictly enfore PEP8, we generally recommend it. + +:::{important} +We assume you have installed the "development requirements," as covered +in {doc}`preliminaries`. If you are not sure whether you have, there is no +harm in running this a second time: + +```bash +python3 -m pip install --upgrade -r requirements-dev.txt +``` +::: + +```bash +python3 -m flake8 +``` + +This will list linting or stylistic errors. If there is no output, all is well. +See the [flake8 documentation](http://flake8.pycqa.org/en/latest/) for more. + +## Commit and Push Changes + +Remember to commit your changes to version control and push them up to GitHub. + +:::{important} +The following is a quick reference that makes some assumptions about your +local configuration and workflow. + +This usage is part of a workflow named *GitHub flow*. See +[this guide](https://guides.github.com/introduction/flow/) for more. +::: + +Remember that at any time you may use `git status` to check which branch +you are currently on and which files have uncommitted changes. Use `git diff` +to review the content of those changes. + +1. If you have not already done so, create a new "feature branch" for this work + with some descriptive name. + + ```bash + git checkout master # Starting from the master branch... + git checkout -b add-snell-function # ...make a new branch. + ``` + +2. Stage changes to be committed. In our example, we have created one new file + and changed an existing one. We `git add` both. + + ```bash + git add example/refraction.py + git add example/tests/test_examples.py + ``` + +3. Commit changes. + + ```bash + git commit -m "Add snell function and tests." + ``` + +4. Push changes to remote repository on GitHub. + + ```bash + git push origin add-snell-function + ``` + +5. Repeat steps 2-4 until you are happy with this feature. + +6. Create a Pull Request --- or merge to master. + + When you are ready for collaborators to review your work and consider merging + the `add-snell-function` branch into the `master` branch, + [create a pull request](https://help.github.com/articles/creating-a-pull-request). + Even if you presently have no collaborators, going through this process is a + useful way to document the history of changes to the project for any *future* + collaborators (and Future You). + + However, if you are in the early stages of just getting a project up and you + are the only developer, you might skip the pull request step and merge the + changes yourself. + + ```bash + git checkout master + # Ensure local master branch is up to date with remote master branch. + git pull --ff-only origin master + # Merge the commits from add-snell-function into master. + git merge add-snell-function + # Update the remote master branch. + git push origin master + ``` + +## Multiple modules + +We created just one module, `example.refraction`. We might eventually grow a +second module --- say, `example.utils`. Some brief advice: + +- When in doubt, resist the temptation to grow deep taxonomies of modules and + sub-packages, lest it become difficult for users and collaborators to + remember where everything is. The Python built-in libraries are generally + flat. + +- When making intra-package imports, we recommend relative imports. + + This works: + + ```bash + # example/refraction.py + + from example import utils + from example.utils import some_function + ``` + + but this is equivalent, and preferred: + + ```bash + # example/refraction.py + + from . import utils + from .utils import some_function + ``` + + For one thing, if you change the name of the package in the future, you won't + need to update this file. + +- Take care to avoid circular imports, wherein two modules each import the + other. diff --git a/docs/pages/developers/the-code-itself.rst b/docs/pages/developers/the-code-itself.rst deleted file mode 100644 index b5f340da..00000000 --- a/docs/pages/developers/the-code-itself.rst +++ /dev/null @@ -1,374 +0,0 @@ -=============== -The Code Itself -=============== - -In this section you will: - -* Put some scientific code in your new Python package. -* Update your package's list of dependencies in ``requirements.txt``. -* Write a test and run the test suite. -* Use a "linter" and style-checker. -* Commit your changes to git and sync your changes with GitHub. - -A simple function with inline documentation -------------------------------------------- - -Let's write a simple function that encodes -`Snell's Law `_ and include it in -our Python package. - -Look again at the directory structure. - -.. code-block:: none - - example/ - ├── .flake8 - ├── .gitattributes - ├── .gitignore - ├── .travis.yml - ├── AUTHORS.rst - ├── CONTRIBUTING.rst - ├── LICENSE - ├── MANIFEST.in - ├── README.rst - ├── docs - │   ├── Makefile - │   ├── build - │   ├── make.bat - │   └── source - │   ├── _static - │   │   └── .placeholder - │   ├── _templates - │   ├── conf.py - │   ├── index.rst - │   ├── installation.rst - │   ├── release-history.rst - │   └── usage.rst - ├── example - │   ├── __init__.py - │   ├── _version.py - │   └── tests - │   └── test_examples.py - ├── requirements-dev.txt - ├── requirements.txt - ├── setup.cfg - ├── setup.py - └── versioneer.py - -Our scientific code should go in the ``example/`` subdirectory, next to -``__init__.py``. Let's make a new file in that directory named -``refraction.py``, meaning our new layout will be: - -.. code-block:: none - - ├── example - │   ├── __init__.py - │   ├── _version.py - │   ├── refraction.py - │   └── tests - │   └── test_examples.py - -This is our new file. You may follow along exactly or, instead, make a file -with a different name and your own scientific function. - -.. literalinclude:: refraction.py - -Notice that this example includes inline documentation --- a "docstring". This -is extremely useful for collaborators, and the most common collaborator is -Future You! - -Further, by following the -`numpydoc standard `_, -we will be able to automatically generate nice-looking HTML documentation -later. Notable features: - -* There is a succinct, one-line summary of the function's purpose. It must one - line. -* (Optional) There is an paragraph elaborating on that summary. -* There is a section listing input parameters, with the structure - - .. code-block :: none - - parameter_name : parameter_type - optional description - - Note that space before the ``:``. That is part of the standard. -* Similar parameters may be combined into one entry for brevity's sake, as we - have done for ``n1, n2`` here. -* There is a section describing what the function returns. -* (Optional) There is a section of one or more examples. - -We will revisit docstrings in the section on :doc:`writing-docs`. - -Update Requirements -------------------- - -Notice that our package has a third-party dependency, numpy. We should -update our package's ``requirements.txt``. - -.. code-block:: text - - # requirements.txt - - # List required packages in this file, one per line. - numpy - -Our cookiecutter configured ``setup.py`` to read this file. It will ensure that -numpy is installed when our package is installed. - -We can test it by reinstalling the package. - -.. code-block:: bash - - python3 -m pip install -e . - -Try it ------- - -Try importing and using the function. - - -.. code-block:: python - - >>> from example.refraction import snell - >>> import numpy as np - >>> snell(np.pi/4, 1.00, 1.33) - 1.2239576240104186 - -The docstring can be viewed with :func:`help`. - -.. code-block:: python - - >>> help(snell) - -Or, as a shortcut, use ``?`` in IPython/Jupyter. - -.. ipython:: python - :verbatim: - - snell? - -Run the Tests -------------- - -You should add a test right away while the details are still fresh in mind. -Writing tests encourages you to write modular, reusable code, which is easier -to test. - -The cookiecutter template included an example test suite with one test: - -.. code-block:: python - - # example/tests/test_examples.py - - def test_one_plus_one_is_two(): - assert 1 + 1 == 2 - -Before writing our own test, let's practice running that test to check that -everything is working. - -.. important:: - - We assume you have installed the "development requirements," as covered - in :doc:`preliminaries`. If you are not sure whether you have, there is no - harm in running this a second time: - - .. code-block:: bash - - python3 -m pip install --upgrade -r requirements-dev.txt - -.. code-block:: bash - - python3 -m pytest - -This walks through all the directories and files in our package that start with -the word 'test' and collects all the functions whose name also starts with -``test``. Currently, there is just one, ``test_one_plus_one_is_two``. -``pytest`` runs that function. If no exceptions are raised, the test passes. - -The output should look something like this: - -.. code-block:: bash - - ======================================== test session starts ======================================== - platform darwin -- Python 3.6.4, pytest-3.6.2, py-1.5.4, pluggy-0.6.0 - benchmark: 3.1.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) - rootdir: /private/tmp/test11/example, inifile: - plugins: xdist-1.22.2, timeout-1.2.1, rerunfailures-4.0, pep8-1.0.6, lazy-fixture-0.3.0, forked-0.2, benchmark-3.1.1 - collected 1 item - - example/tests/test_examples.py . [100%] - - ===================================== 1 passed in 0.02 seconds ====================================== - -.. note:: - - The output of ``pytest`` is customizable. Commonly useful command-line - arguments include: - - * ``-v`` verbose - * ``-s`` Do not capture stdout/err per test. - * ``-k EXPRESSION`` Filter tests by pattern-matching test name. - - Consult the `pytest documentation `_ - for more. - -Write a Test ------------- - -Let's add a test to ``test_examples.py`` that exercises our ``snell`` function. -We can delete ``test_one_plus_one_is_two`` now. - -.. literalinclude:: test_examples.py - -Things to notice: - -* It is sometime useful to put multiple ``assert`` statements in one test. You - should make a separate test for each *behavior* that you are checking. When a - monolithic, multi-step tests fails, it's difficult to figure out why. -* When comparing floating-point numbers (as opposed to integers) you should not - test for exact equality. Use :func:`numpy.allclose`, which checks for - equality within a (configurable) tolerance. Numpy provides several - `testing utilities `_, - which should always be used when testing numpy arrays. -* Remember that the names of all test modules and functions must begin with - ``test`` or they will not be picked up by pytest! - -See :doc:`advanced-testing` for more. - -"Lint": Check for suspicious-looking code ------------------------------------------ - -A `linter `_ is a tool that -analyzes code to flag potential errors. For example, it can catch variables you -defined by never used, which is likely a spelling error. - -The cookiecutter configured ``flake8`` for this purpose. Flake8 checks for -"lint" and also enforces the standard Python coding style, -`PEP8 `_. Enforcing -consistent style helps projects stay easy to read and maintain as they grow. -While not all projects strictly enfore PEP8, we generally recommend it. - -.. important:: - - We assume you have installed the "development requirements," as covered - in :doc:`preliminaries`. If you are not sure whether you have, there is no - harm in running this a second time: - - .. code-block:: bash - - python3 -m pip install --upgrade -r requirements-dev.txt - -.. code-block:: bash - - python3 -m flake8 - -This will list linting or stylistic errors. If there is no output, all is well. -See the `flake8 documentation `_ for more. - -Commit and Push Changes ------------------------ - -Remember to commit your changes to version control and push them up to GitHub. - -.. important:: - - The following is a quick reference that makes some assumptions about your - local configuration and workflow. - - This usage is part of a workflow named *GitHub flow*. See - `this guide `_ for more. - -Remember that at any time you may use ``git status`` to check which branch -you are currently on and which files have uncommitted changes. Use ``git diff`` -to review the content of those changes. - -1. If you have not already done so, create a new "feature branch" for this work - with some descriptive name. - - .. code-block:: bash - - git checkout master # Starting from the master branch... - git checkout -b add-snell-function # ...make a new branch. - -2. Stage changes to be committed. In our example, we have created one new file - and changed an existing one. We ``git add`` both. - - .. code-block:: bash - - git add example/refraction.py - git add example/tests/test_examples.py - -3. Commit changes. - - .. code-block:: bash - - git commit -m "Add snell function and tests." - -4. Push changes to remote repository on GitHub. - - .. code-block:: bash - - git push origin add-snell-function - -5. Repeat steps 2-4 until you are happy with this feature. - -6. Create a Pull Request --- or merge to master. - - When you are ready for collaborators to review your work and consider merging - the ``add-snell-function`` branch into the ``master`` branch, - `create a pull request `_. - Even if you presently have no collaborators, going through this process is a - useful way to document the history of changes to the project for any *future* - collaborators (and Future You). - - However, if you are in the early stages of just getting a project up and you - are the only developer, you might skip the pull request step and merge the - changes yourself. - - .. code-block:: bash - - git checkout master - # Ensure local master branch is up to date with remote master branch. - git pull --ff-only origin master - # Merge the commits from add-snell-function into master. - git merge add-snell-function - # Update the remote master branch. - git push origin master - -Multiple modules ----------------- - -We created just one module, ``example.refraction``. We might eventually grow a -second module --- say, ``example.utils``. Some brief advice: - -* When in doubt, resist the temptation to grow deep taxonomies of modules and - sub-packages, lest it become difficult for users and collaborators to - remember where everything is. The Python built-in libraries are generally - flat. - -* When making intra-package imports, we recommend relative imports. - - This works: - - .. code-block:: bash - - # example/refraction.py - - from example import utils - from example.utils import some_function - - but this is equivalent, and preferred: - - .. code-block:: bash - - # example/refraction.py - - from . import utils - from .utils import some_function - - For one thing, if you change the name of the package in the future, you won't - need to update this file. - -* Take care to avoid circular imports, wherein two modules each import the - other. diff --git a/docs/pages/developers/writing-docs.md b/docs/pages/developers/writing-docs.md new file mode 100644 index 00000000..6e660ef0 --- /dev/null +++ b/docs/pages/developers/writing-docs.md @@ -0,0 +1,291 @@ +# Writing Documentation + +In this section you will: + +- Generate HTML documentation using Sphinx, starting from a working example + provided by the cookiecutter template. +- Edit `usage.rst` to add API documentation and narrative documentation. +- Learn how to incorporate code examples, IPython examples, matplotlib plots, + and typeset math. + +## Build the docs + +Almost all scientific Python projects use the +[Sphinx documentation generator](http://www.sphinx-doc.org/). +The cookiecutter template provided a working example with some popular +extensions installed and some sample pages. + +```none +example/ +(...) +├── docs +│   ├── Makefile +│   ├── build +│   ├── make.bat +│   └── source +│   ├── _static +│   │   └── .placeholder +│   ├── _templates +│   ├── conf.py +│   ├── index.rst +│   ├── installation.rst +│   ├── release-history.rst +│   └── usage.rst +(...) +``` + +The `.rst` files are source code for our documentation. To build HTML pages +from this source, run: + +```bash +make -C docs html +``` + +You should see some log message ending in `build succeeded.` + +This output HTML will be located in `docs/build/html`. In your Internet +browser, open `file://.../docs/build/html/index.html`, where `...` is the +path to your project directory. If you aren't sure sure where that is, type +`pwd`. + +## Update the docs + +The source code for the documentation is located in `docs/source/`. +Sphinx uses a markup language called ReStructured Text (.rst). We refer you to +[this primer](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) +to learn how to denote headings, links, lists, cross-references, etc. + +Sphinx formatting is sensitive to whitespace and generally quite picky. We +recommend running `make -C docs html` often to check that the documentation +builds successfully. Remember to commit your changes to git periodically. + +Good documentation includes both: + +- API (Application Programming Interface) documentation, listing every public + object in the library and its usage +- Narrative documentation interleaving prose and code examples to explain how + and why a library is meant to be used + +## API Documentation + +Most the work of writing good API documentation goes into writing good, +accurate docstrings. Sphinx can scrape that content and generate HTML from it. +Again, most scientific Python libraries use the +[numpydoc standard](https://numpydoc.readthedocs.io/en/latest/format.html), +which looks like this: + +```{literalinclude} refraction.py +``` + +### Autodoc + +In an rst file, such as `docs/source/usage.rst`, we can write: + +```rst +.. autofunction:: example.refraction.snell +``` + +which renders in HTML like so: + +```{eval-rst} +.. autofunction:: example.refraction.snell + :noindex: +``` + +From here we refer you to the +[sphinx autodoc documentation](http://www.sphinx-doc.org/en/stable/ext/autodoc.html). + +### Autosummary + +If you have many related objects to document, it may be better to display them +in a table. Each row will include the name, the signature (optional), and the +one-line description from the docstring. + +In rst we can write: + +```rst +.. autosummary:: + :toctree: generated/ + + example.refraction.snell +``` + +which renders in HTML like so: + +```{eval-rst} +.. autosummary:: + :toctree: generated/ + + example.refraction.snell +``` + +It links to the full rendered docstring on a separate page that is +automatically generated. + +From here we refer you to the +[sphinx autosummary documentation](http://www.sphinx-doc.org/en/stable/ext/autosummary.html). + +## Narrative Documentation + +### Code Blocks + +Code blocks can be interspersed with narrative text like this: + +```rst +Scientific libraries conventionally use radians. Numpy provides convenience +functions for converting between radians and degrees. + +.. code-block:: python + + import numpy as np + + + np.deg2rad(90) # pi / 2 + np.rad2deg(np.pi / 2) # 90.0 +``` + +which renders in HTML as: + +Scientific libraries conventionally use radians. Numpy provides convenience +functions for converting between radians and degrees. + +```python +import numpy as np + + +np.deg2rad(90) # pi / 2 +np.rad2deg(np.pi / 2) # 90.0 +``` + +To render short code expressions inline, surround them with back-ticks. This: + +```rst +Try ``snell(0, 1, 1.33)``. +``` + +renders in HTML as: + +Try `snell(0, 1, 1.33)`. + +### Embedded Scripts + +For lengthy examples with tens of lines or more, it can be convenient to embed +the content of a .py file rather than writing it directly into the +documentation. + +This can be done using the directive + +```rest +.. literalinclude:: examples/some_example.py +``` + +where the path is given relative to the current file's path. Thus, relative to +the repository's root directory, the path to this example script would be +`docs/source/examples/some_example.py`. + +From here we refer you to the +[sphinx code example documentation](http://www.sphinx-doc.org/en/stable/markup/code.html). + +To go beyond embedded scripts to a more richly-featured example gallery that +shows scripts and their outputs, we encourage you to look at +[sphinx-gallery](https://sphinx-gallery.github.io/). + +### IPython Examples + +IPython's sphinx extension, which is included by the cookiecutter template, +makes it possible to execute example code and capture its output when the +documentation is built. This rst code: + +```rst +.. ipython:: python + + 1 + 1 +``` + +renders in HTML as: + +```{eval-rst} +.. ipython:: python + + 1 + 1 +``` + +From here we refer you to the +[IPython sphinx directive documentation](https://ipython.org/ipython-doc/rel-0.13.2/development/ipython_directive.html). + +### Plots + +Matplotlib's sphinx extension, which is included by the cookiecutter template, +makes it possible to display matplotlib figures in line. This rst code: + +```rst +.. plot:: + + import matplotlib.pyplot as plt + fig, ax = plt.subplots() + ax.plot([1, 1, 2, 3, 5, 8]) +``` + +renders in HTML as: + +```{eval-rst} +.. plot:: + + import matplotlib.pyplot as plt + fig, ax = plt.subplots() + ax.plot([1, 1, 2, 3, 5, 8]) +``` + +From here we refer you to the +[matplotlib plot directive documentation](https://matplotlib.org/devel/plot_directive.html). + +### Math (LaTeX) + +Sphinx can render LaTeX typeset math in the browser (using +[MathJax](https://www.mathjax.org/)). This rst code: + +```rst +.. math:: + + \int_0^a x\,dx = \frac{1}{2}a^2 +``` + +renders in HTML as: + +$$ +\int_0^a x\,dx = \frac{1}{2}a^2 +$$ + +This notation can also be used in docstrings. For example, we could add +the equation of Snell's Law to the docstring of +{func}`~example.refraction.snell`. + +Math can also be written inline. This rst code: + +```rst +The value of :math:`\pi` is 3.141592653.... +``` + +renders in HTML as: + +> The value of $\pi$ is 3.141592653.... + +### Referencing Documented Objects + +You can create links to documented functions like so: + +```rst +The :func:`example.refraction.snell` function encodes Snell's Law. +``` + +The {func}`example.refraction.snell` function encodes Snell's Law. + +Adding a `~` omits the module path from the link text. + +```rst +The :func:`~example.refraction.snell` function encodes Snell's Law. +``` + +The {func}`~example.refraction.snell` function encodes Snell's Law. + +See [the Sphinx documentation](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) for more. diff --git a/docs/pages/developers/writing-docs.rst b/docs/pages/developers/writing-docs.rst deleted file mode 100644 index 73123ff2..00000000 --- a/docs/pages/developers/writing-docs.rst +++ /dev/null @@ -1,297 +0,0 @@ -===================== -Writing Documentation -===================== - -In this section you will: - -* Generate HTML documentation using Sphinx, starting from a working example - provided by the cookiecutter template. -* Edit ``usage.rst`` to add API documentation and narrative documentation. -* Learn how to incorporate code examples, IPython examples, matplotlib plots, - and typeset math. - -Build the docs --------------- - -Almost all scientific Python projects use the -`Sphinx documentation generator `_. -The cookiecutter template provided a working example with some popular -extensions installed and some sample pages. - -.. code-block:: none - - example/ - (...) - ├── docs - │   ├── Makefile - │   ├── build - │   ├── make.bat - │   └── source - │   ├── _static - │   │   └── .placeholder - │   ├── _templates - │   ├── conf.py - │   ├── index.rst - │   ├── installation.rst - │   ├── release-history.rst - │   └── usage.rst - (...) - -The ``.rst`` files are source code for our documentation. To build HTML pages -from this source, run: - -.. code-block:: bash - - make -C docs html - -You should see some log message ending in ``build succeeded.`` - -This output HTML will be located in ``docs/build/html``. In your Internet -browser, open ``file://.../docs/build/html/index.html``, where ``...`` is the -path to your project directory. If you aren't sure sure where that is, type -``pwd``. - -Update the docs ---------------- - -The source code for the documentation is located in ``docs/source/``. -Sphinx uses a markup language called ReStructured Text (.rst). We refer you to -`this primer `_ -to learn how to denote headings, links, lists, cross-references, etc. - -Sphinx formatting is sensitive to whitespace and generally quite picky. We -recommend running ``make -C docs html`` often to check that the documentation -builds successfully. Remember to commit your changes to git periodically. - -Good documentation includes both: - -* API (Application Programming Interface) documentation, listing every public - object in the library and its usage -* Narrative documentation interleaving prose and code examples to explain how - and why a library is meant to be used - -API Documentation ------------------ - -Most the work of writing good API documentation goes into writing good, -accurate docstrings. Sphinx can scrape that content and generate HTML from it. -Again, most scientific Python libraries use the -`numpydoc standard `_, -which looks like this: - -.. literalinclude:: refraction.py - -Autodoc -^^^^^^^ - -In an rst file, such as ``docs/source/usage.rst``, we can write: - -.. code-block:: rst - - .. autofunction:: example.refraction.snell - -which renders in HTML like so: - -.. autofunction:: example.refraction.snell - :noindex: - -From here we refer you to the -`sphinx autodoc documentation `_. - -Autosummary -^^^^^^^^^^^ - -If you have many related objects to document, it may be better to display them -in a table. Each row will include the name, the signature (optional), and the -one-line description from the docstring. - -In rst we can write: - -.. code-block:: rst - - .. autosummary:: - :toctree: generated/ - - example.refraction.snell - -which renders in HTML like so: - -.. autosummary:: - :toctree: generated/ - - example.refraction.snell - -It links to the full rendered docstring on a separate page that is -automatically generated. - -From here we refer you to the -`sphinx autosummary documentation `_. - -Narrative Documentation ------------------------ - -Code Blocks -^^^^^^^^^^^ - -Code blocks can be interspersed with narrative text like this: - -.. code-block:: rst - - Scientific libraries conventionally use radians. Numpy provides convenience - functions for converting between radians and degrees. - - .. code-block:: python - - import numpy as np - - - np.deg2rad(90) # pi / 2 - np.rad2deg(np.pi / 2) # 90.0 - -which renders in HTML as: - -Scientific libraries conventionally use radians. Numpy provides convenience -functions for converting between radians and degrees. - -.. code-block:: python - - import numpy as np - - - np.deg2rad(90) # pi / 2 - np.rad2deg(np.pi / 2) # 90.0 - -To render short code expressions inline, surround them with back-ticks. This: - -.. code-block:: rst - - Try ``snell(0, 1, 1.33)``. - -renders in HTML as: - - -Try ``snell(0, 1, 1.33)``. - -Embedded Scripts -^^^^^^^^^^^^^^^^ - -For lengthy examples with tens of lines or more, it can be convenient to embed -the content of a .py file rather than writing it directly into the -documentation. - -This can be done using the directive - -.. code-block:: rest - - .. literalinclude:: examples/some_example.py - -where the path is given relative to the current file's path. Thus, relative to -the repository's root directory, the path to this example script would be -``docs/source/examples/some_example.py``. - -From here we refer you to the -`sphinx code example documentation `_. - -To go beyond embedded scripts to a more richly-featured example gallery that -shows scripts and their outputs, we encourage you to look at -`sphinx-gallery `_. - -IPython Examples -^^^^^^^^^^^^^^^^ - -IPython's sphinx extension, which is included by the cookiecutter template, -makes it possible to execute example code and capture its output when the -documentation is built. This rst code: - -.. code-block:: rst - - .. ipython:: python - - 1 + 1 - -renders in HTML as: - -.. ipython:: python - - 1 + 1 - -From here we refer you to the -`IPython sphinx directive documentation `_. - -Plots -^^^^^ - -Matplotlib's sphinx extension, which is included by the cookiecutter template, -makes it possible to display matplotlib figures in line. This rst code: - -.. code-block:: rst - - .. plot:: - - import matplotlib.pyplot as plt - fig, ax = plt.subplots() - ax.plot([1, 1, 2, 3, 5, 8]) - -renders in HTML as: - -.. plot:: - - import matplotlib.pyplot as plt - fig, ax = plt.subplots() - ax.plot([1, 1, 2, 3, 5, 8]) - -From here we refer you to the -`matplotlib plot directive documentation `_. - -Math (LaTeX) -^^^^^^^^^^^^ - -Sphinx can render LaTeX typeset math in the browser (using -`MathJax `_). This rst code: - -.. code-block:: rst - - .. math:: - - \int_0^a x\,dx = \frac{1}{2}a^2 - -renders in HTML as: - -.. math:: - - \int_0^a x\,dx = \frac{1}{2}a^2 - -This notation can also be used in docstrings. For example, we could add -the equation of Snell's Law to the docstring of -:func:`~example.refraction.snell`. - -Math can also be written inline. This rst code: - -.. code-block:: rst - - The value of :math:`\pi` is 3.141592653.... - -renders in HTML as: - - The value of :math:`\pi` is 3.141592653.... - -Referencing Documented Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can create links to documented functions like so: - -.. code-block:: rst - - The :func:`example.refraction.snell` function encodes Snell's Law. - -The :func:`example.refraction.snell` function encodes Snell's Law. - -Adding a ``~`` omits the module path from the link text. - -.. code-block:: rst - - The :func:`~example.refraction.snell` function encodes Snell's Law. - -The :func:`~example.refraction.snell` function encodes Snell's Law. - -See `the Sphinx documentation `_ for more. diff --git a/docs/pages/developers/conf.py b/{{cookiecutter.project_name}}/docs/conf.py similarity index 100% rename from docs/pages/developers/conf.py rename to {{cookiecutter.project_name}}/docs/conf.py