--- 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 ```