350 lines
10 KiB
Markdown
350 lines
10 KiB
Markdown
---
|
|
myst:
|
|
html_meta:
|
|
description: "Guide to writing runnable, CI-tested code examples for Ray docs using doctest-style, code-output-style, and literalinclude formats. Read this when adding code snippets to docstrings or user guides so they keep working for users."
|
|
---
|
|
|
|
(writing-code-snippets_ref)=
|
|
|
|
# How to write code snippets
|
|
|
|
Users learn from example. So, whether you're writing a docstring or a user guide, include examples that illustrate the relevant APIs. Your examples should run out-of-the-box so that users can copy them and adapt them to their own needs.
|
|
|
|
This page describes how to write code snippets so that they're tested in CI.
|
|
|
|
:::{note}
|
|
The examples in this guide use reStructuredText. If you're writing Markdown, use MyST syntax. To learn more, read the [MyST documentation](https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html#directives-a-block-level-extension-point).
|
|
:::
|
|
|
|
## Types of examples
|
|
|
|
There are three types of examples: *doctest-style*, *code-output-style*, and *literalinclude*.
|
|
|
|
### *doctest-style* examples
|
|
|
|
*doctest-style* examples mimic interactive Python sessions.
|
|
|
|
```
|
|
.. doctest::
|
|
|
|
>>> def is_even(x):
|
|
... return (x % 2) == 0
|
|
>>> is_even(0)
|
|
True
|
|
>>> is_even(1)
|
|
False
|
|
```
|
|
|
|
They're rendered like this:
|
|
|
|
```{doctest}
|
|
>>> def is_even(x):
|
|
... return (x % 2) == 0
|
|
>>> is_even(0)
|
|
True
|
|
>>> is_even(1)
|
|
False
|
|
```
|
|
|
|
:::{tip}
|
|
If you're writing docstrings, exclude `.. doctest::` to simplify your code:
|
|
|
|
```
|
|
Example:
|
|
>>> def is_even(x):
|
|
... return (x % 2) == 0
|
|
>>> is_even(0)
|
|
True
|
|
>>> is_even(1)
|
|
False
|
|
```
|
|
:::
|
|
|
|
### *code-output-style* examples
|
|
|
|
*code-output-style* examples contain ordinary Python code.
|
|
|
|
```
|
|
.. testcode::
|
|
|
|
def is_even(x):
|
|
return (x % 2) == 0
|
|
|
|
print(is_even(0))
|
|
print(is_even(1))
|
|
|
|
.. testoutput::
|
|
|
|
True
|
|
False
|
|
```
|
|
|
|
They're rendered like this:
|
|
|
|
```{testcode}
|
|
def is_even(x):
|
|
return (x % 2) == 0
|
|
|
|
print(is_even(0))
|
|
print(is_even(1))
|
|
```
|
|
|
|
```{testoutput}
|
|
True
|
|
False
|
|
```
|
|
|
|
### *literalinclude* examples
|
|
|
|
*literalinclude* examples display Python modules.
|
|
|
|
```
|
|
.. literalinclude:: ./doc_code/example_module.py
|
|
:language: python
|
|
:start-after: __is_even_begin__
|
|
:end-before: __is_even_end__
|
|
```
|
|
|
|
```{literalinclude} ./doc_code/example_module.py
|
|
:language: python
|
|
```
|
|
|
|
They're rendered like this:
|
|
|
|
```{literalinclude} ./doc_code/example_module.py
|
|
:language: python
|
|
:start-after: __is_even_begin__
|
|
:end-before: __is_even_end__
|
|
```
|
|
|
|
## Which type of example should you write?
|
|
|
|
There's no hard rule about which style you should use. Choose the style that best illustrates your API.
|
|
|
|
:::{tip}
|
|
If you're not sure which style to use, use *code-output-style*.
|
|
:::
|
|
|
|
### When to use *doctest-style*
|
|
|
|
If you're writing a small example that emphasizes object representations, or if you want to print intermediate objects, use *doctest-style*.
|
|
|
|
```
|
|
.. doctest::
|
|
|
|
>>> import ray
|
|
>>> ds = ray.data.range(100)
|
|
>>> ds.schema()
|
|
Column Type
|
|
------ ----
|
|
id int64
|
|
>>> ds.take(5)
|
|
[{'id': 0}, {'id': 1}, {'id': 2}, {'id': 3}, {'id': 4}]
|
|
```
|
|
|
|
### When to use *code-output-style*
|
|
|
|
If you're writing a longer example, or if object representations aren't relevant to your example, use *code-output-style*.
|
|
|
|
```
|
|
.. testcode::
|
|
|
|
from typing import Dict
|
|
import numpy as np
|
|
import ray
|
|
|
|
ds = ray.data.read_csv("s3://anonymous@air-example-data/iris.csv")
|
|
|
|
# Compute a "petal area" attribute.
|
|
def transform_batch(batch: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:
|
|
vec_a = batch["petal length (cm)"]
|
|
vec_b = batch["petal width (cm)"]
|
|
batch["petal area (cm^2)"] = np.round(vec_a * vec_b, 2)
|
|
return batch
|
|
|
|
transformed_ds = ds.map_batches(transform_batch)
|
|
print(transformed_ds.materialize())
|
|
|
|
.. testoutput::
|
|
|
|
shape: (150, 6)
|
|
╭───────────────────┬──────────────────┬───────────────────┬──────────────────┬────────┬───────────────────╮
|
|
│ sepal length (cm) ┆ sepal width (cm) ┆ petal length (cm) ┆ petal width (cm) ┆ target ┆ petal area (cm^2) │
|
|
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
│ double ┆ double ┆ double ┆ double ┆ int64 ┆ double │
|
|
╞═══════════════════╪══════════════════╪═══════════════════╪══════════════════╪════════╪═══════════════════╡
|
|
│ 5.1 ┆ 3.5 ┆ 1.4 ┆ 0.2 ┆ 0 ┆ 0.28 │
|
|
│ 4.9 ┆ 3.0 ┆ 1.4 ┆ 0.2 ┆ 0 ┆ 0.28 │
|
|
│ 4.7 ┆ 3.2 ┆ 1.3 ┆ 0.2 ┆ 0 ┆ 0.26 │
|
|
│ 4.6 ┆ 3.1 ┆ 1.5 ┆ 0.2 ┆ 0 ┆ 0.3 │
|
|
│ 5.0 ┆ 3.6 ┆ 1.4 ┆ 0.2 ┆ 0 ┆ 0.28 │
|
|
│ … ┆ … ┆ … ┆ … ┆ … ┆ … │
|
|
│ 6.7 ┆ 3.0 ┆ 5.2 ┆ 2.3 ┆ 2 ┆ 11.96 │
|
|
│ 6.3 ┆ 2.5 ┆ 5.0 ┆ 1.9 ┆ 2 ┆ 9.5 │
|
|
│ 6.5 ┆ 3.0 ┆ 5.2 ┆ 2.0 ┆ 2 ┆ 10.4 │
|
|
│ 6.2 ┆ 3.4 ┆ 5.4 ┆ 2.3 ┆ 2 ┆ 12.42 │
|
|
│ 5.9 ┆ 3.0 ┆ 5.1 ┆ 1.8 ┆ 2 ┆ 9.18 │
|
|
╰───────────────────┴──────────────────┴───────────────────┴──────────────────┴────────┴───────────────────╯
|
|
(Showing 10 of 150 rows)
|
|
```
|
|
|
|
### When to use *literalinclude*
|
|
|
|
If you're writing an end-to-end example and your example doesn't contain outputs, use *literalinclude*.
|
|
|
|
## How to handle hard-to-test examples
|
|
|
|
### When is it okay to not test an example?
|
|
|
|
You don't need to test examples that depend on external systems such as Weights and Biases.
|
|
|
|
### Skipping *doctest-style* examples
|
|
|
|
To skip a *doctest-style* example, append `# doctest: +SKIP` to your Python code.
|
|
|
|
```
|
|
.. doctest::
|
|
|
|
>>> import ray
|
|
>>> ray.data.read_images("s3://private-bucket") # doctest: +SKIP
|
|
```
|
|
|
|
### Skipping *code-output-style* examples
|
|
|
|
To skip a *code-output-style* example, add `:skipif: True` to the `testcode` block.
|
|
|
|
```
|
|
.. testcode::
|
|
:skipif: True
|
|
|
|
from ray.air.integrations.wandb import WandbLoggerCallback
|
|
callback = WandbLoggerCallback(
|
|
project="Optimization_Project",
|
|
api_key_file=...,
|
|
log_config=True
|
|
)
|
|
```
|
|
|
|
## How to handle long or non-deterministic outputs
|
|
|
|
If your Python code is non-deterministic, or if your output is excessively long, you can skip all or part of the output.
|
|
|
|
### Ignoring *doctest-style* outputs
|
|
|
|
To ignore parts of a *doctest-style* output, replace problematic sections with ellipses.
|
|
|
|
```
|
|
>>> import ray
|
|
>>> ray.data.read_images("s3://anonymous@ray-example-data/image-datasets/simple")
|
|
Dataset(num_rows=..., schema=...)
|
|
```
|
|
|
|
To ignore an output altogether, write a *code-output-style* snippet. Don't use `# doctest: +SKIP`.
|
|
|
|
### Ignoring *code-output-style* outputs
|
|
|
|
If parts of your output are long or non-deterministic, replace problematic sections with ellipses.
|
|
|
|
```
|
|
.. testcode::
|
|
|
|
import ray
|
|
ds = ray.data.read_images("s3://anonymous@ray-example-data/image-datasets/simple")
|
|
print(ds)
|
|
|
|
.. testoutput::
|
|
|
|
Dataset(num_rows=..., schema=...)
|
|
```
|
|
|
|
If your output is non-deterministic and you want to display a sample output, add `:options: +MOCK`.
|
|
|
|
```
|
|
.. testcode::
|
|
|
|
import random
|
|
print(random.random())
|
|
|
|
.. testoutput::
|
|
:options: +MOCK
|
|
|
|
0.969461416250246
|
|
```
|
|
|
|
If your output is hard to test and you don't want to display a sample output, exclude the `testoutput`.
|
|
|
|
```
|
|
.. testcode::
|
|
|
|
print("This output is hidden and untested")
|
|
```
|
|
|
|
## How to test examples with GPUs
|
|
|
|
To configure Bazel to run an example with GPUs, complete the following steps:
|
|
|
|
1. Open the corresponding `BUILD` file. If your example is in the `doc/` folder, open `doc/BUILD`. If your example is in the `python/` folder, open a file such as `python/ray/train/BUILD`.
|
|
|
|
2. Locate the `doctest` rule. It looks like this:
|
|
|
|
```
|
|
doctest(
|
|
files = glob(
|
|
include=["source/**/*.rst"],
|
|
),
|
|
size = "large",
|
|
tags = ["team:none"]
|
|
)
|
|
```
|
|
|
|
3. Add the file that contains your example to the list of excluded files.
|
|
|
|
```
|
|
doctest(
|
|
files = glob(
|
|
include=["source/**/*.rst"],
|
|
exclude=["source/data/requires-gpus.rst"]
|
|
),
|
|
tags = ["team:none"]
|
|
)
|
|
```
|
|
|
|
4. If it doesn't already exist, create a `doctest` rule with `gpu` set to `True`.
|
|
|
|
```
|
|
doctest(
|
|
files = [],
|
|
tags = ["team:none"],
|
|
gpu = True
|
|
)
|
|
```
|
|
|
|
5. Add the file that contains your example to the GPU rule.
|
|
|
|
```
|
|
doctest(
|
|
files = ["source/data/requires-gpus.rst"]
|
|
size = "large",
|
|
tags = ["team:none"],
|
|
gpu = True
|
|
)
|
|
```
|
|
|
|
For a practical example, see `doc/BUILD` or `python/ray/train/BUILD`.
|
|
|
|
## How to locally test examples
|
|
|
|
To locally test examples, install the Ray fork of `pytest-sphinx`.
|
|
|
|
```bash
|
|
pip install git+https://github.com/ray-project/pytest-sphinx
|
|
```
|
|
|
|
Then, run pytest on a module, docstring, or user guide.
|
|
|
|
```bash
|
|
pytest --doctest-modules python/ray/data/read_api.py
|
|
pytest --doctest-modules python/ray/data/read_api.py::ray.data.read_api.range
|
|
pytest --doctest-modules doc/source/data/getting-started.rst
|
|
```
|