chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
Reference in New Issue
Block a user