From 8cdd608b91ed9a9aa49611c5f1f8fa3474670d55 Mon Sep 17 00:00:00 2001 From: ribhuji <56975412+ribhuji@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:42:08 +0530 Subject: [PATCH 1/2] add custom lintrunner to catch indentation issues --- .github/workflows/lintrunner.yml | 3 + .lintrunner.toml | 15 + .../linter/adapters/tutorial_markup_linter.py | 282 ++++++++++++++++++ .../tests/test_tutorial_markup_linter.py | 109 +++++++ 4 files changed, 409 insertions(+) create mode 100644 tools/linter/adapters/tutorial_markup_linter.py create mode 100644 tools/linter/tests/test_tutorial_markup_linter.py diff --git a/.github/workflows/lintrunner.yml b/.github/workflows/lintrunner.yml index fbfc48e9944..11144209f24 100644 --- a/.github/workflows/lintrunner.yml +++ b/.github/workflows/lintrunner.yml @@ -23,6 +23,9 @@ jobs: with: python-version: '3.12' + - name: Test local linter adapters + run: python3 -m unittest discover -s tools/linter/tests -p 'test_*.py' + - name: Install Lintrunner run: | pip install lintrunner==0.12.5 diff --git a/.lintrunner.toml b/.lintrunner.toml index 94dcc437f73..87bb917a86e 100644 --- a/.lintrunner.toml +++ b/.lintrunner.toml @@ -2,6 +2,21 @@ merge_base_with = "origin/main" # 4805a6ead6f1e7f32351056e2602be4e908f69b7 is from pytorch/pytorch main branch 2025-07-16 +[[linter]] +code = 'TUTORIAL_MARKUP' +include_patterns = [ + 'advanced_source/**/*.py', + 'beginner_source/**/*.py', + 'intermediate_source/**/*.py', + 'recipes_source/**/*.py', + 'unstable_source/**/*.py', +] +command = [ + 'python3', + 'tools/linter/adapters/tutorial_markup_linter.py', + '@{{PATHSFILE}}', +] + [[linter]] code = 'SPACES' include_patterns = ['**'] diff --git a/tools/linter/adapters/tutorial_markup_linter.py b/tools/linter/adapters/tutorial_markup_linter.py new file mode 100644 index 00000000000..8bc879e1a51 --- /dev/null +++ b/tools/linter/adapters/tutorial_markup_linter.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +"""Lint tutorial prose for lists that break during notebook conversion. + +Sphinx-Gallery reads prose from module docstrings and from comment blocks that +follow a gallery separator. That prose is parsed as reStructuredText for the +HTML documentation, but is converted to Markdown for generated notebooks. +Some list layouts accepted by reStructuredText are interpreted differently by +Pandoc and produce malformed Markdown cells. This linter detects those +layouts in the source, before a tutorial is built. +""" + +from __future__ import annotations + +import argparse +import ast +import json +import re +from enum import Enum +from pathlib import Path +from typing import Iterable, NamedTuple, Sequence + + +LINTER_CODE = "TUTORIAL_MARKUP" +GALLERY_SEPARATOR = re.compile(r"^#{20,}\s*$") +LIST_MARKER = re.compile( + r"^(?P *)(?P[-+*]|\d+[.)]|[A-Za-z][.)])\s+(?P.*)$" +) +LIST_TABLE_ROW = re.compile(r"^\s*\*\s+-(?:\s|$)") +SECTION_ADORNMENT = re.compile(r"^\s*([!\"#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~])\1{2,}\s*$") + + +class LintSeverity(str, Enum): + ERROR = "error" + WARNING = "warning" + ADVICE = "advice" + DISABLED = "disabled" + + +class LintMessage(NamedTuple): + path: str | None + line: int | None + char: int | None + code: str + severity: LintSeverity + name: str + original: str | None + replacement: str | None + description: str | None + + +class ProseLine(NamedTuple): + text: str + source_line: int + + +def _module_docstring(source: str) -> list[ProseLine]: + """Return a module docstring without normalizing meaningful indentation.""" + try: + module = ast.parse(source) + except SyntaxError: + return [] + + if not module.body: + return [] + expression = module.body[0] + if not ( + isinstance(expression, ast.Expr) + and isinstance(expression.value, ast.Constant) + and isinstance(expression.value.value, str) + ): + return [] + + value = expression.value.value + return [ + ProseLine(text, expression.lineno + offset) + for offset, text in enumerate(value.splitlines()) + ] + + +def _comment_text(line: str) -> str: + if line.startswith("# "): + return line[2:] + return line[1:] + + +def extract_prose_blocks(source: str) -> list[list[ProseLine]]: + """Extract the source regions Sphinx-Gallery treats as narrative prose.""" + source_lines = source.splitlines() + blocks: list[list[ProseLine]] = [] + + docstring = _module_docstring(source) + if docstring: + blocks.append(docstring) + + index = 0 + while index < len(source_lines): + if not GALLERY_SEPARATOR.fullmatch(source_lines[index]): + index += 1 + continue + + index += 1 + block: list[ProseLine] = [] + while index < len(source_lines) and source_lines[index].startswith("#"): + block.append(ProseLine(_comment_text(source_lines[index]), index + 1)) + index += 1 + if block: + blocks.append(block) + + return blocks + + +def _leading_spaces(line: str) -> int: + return len(line) - len(line.lstrip(" ")) + + +def _marker(line: str) -> re.Match[str] | None: + return LIST_MARKER.match(line) + + +def _has_same_indent_marker_since_blank( + lines: Sequence[ProseLine], index: int, indent: int +) -> bool: + for previous in reversed(lines[:index]): + if not previous.text.strip(): + return False + match = _marker(previous.text) + if match and len(match.group("indent")) == indent: + return True + return False + + +def _has_valid_parent_list( + lines: Sequence[ProseLine], index: int, indent: int +) -> bool: + """Recognize a conventionally indented child of an active list item.""" + for previous in reversed(lines[:index]): + if not previous.text.strip(): + return False + match = _marker(previous.text) + if not match: + continue + parent_indent = len(match.group("indent")) + if parent_indent >= indent: + continue + content_column = parent_indent + len(match.group("marker")) + 1 + return indent == content_column + return False + + +def _missing_blank_before_indented_list( + lines: Sequence[ProseLine], index: int +) -> bool: + match = _marker(lines[index].text) + if not match: + return False + + indent = len(match.group("indent")) + marker = match.group("marker") + if index == 0 or indent == 0 or marker.endswith(")"): + return False + + previous = lines[index - 1].text + if not previous.strip() or SECTION_ADORNMENT.fullmatch(previous): + return False + + # Later items in the same list do not need another separating blank line. + if _has_same_indent_marker_since_blank(lines, index, indent): + return False + + # A child list aligned to its parent's content is valid reStructuredText + # and converts cleanly. The broken examples use an arbitrary indent. + if _has_valid_parent_list(lines, index, indent): + return False + + # Do not confuse list-table cells with ordinary list items. + if LIST_TABLE_ROW.match(previous): + return False + + # An item aligned with the preceding directive content (for example an + # ``.. note::`` body) is already separated by the directive structure. + if indent > 0 and _leading_spaces(previous) == indent: + return False + + return True + + +def _bad_list_continuations(lines: Sequence[ProseLine]) -> Iterable[ProseLine]: + """Yield unindented continuation lines from blank-separated top-level lists.""" + index = 0 + while index < len(lines): + first_match = _marker(lines[index].text) + if not first_match or first_match.group("indent"): + index += 1 + continue + if index > 0 and lines[index - 1].text.strip(): + index += 1 + continue + + marker_indent = 0 + cursor = index + 1 + first_bad: ProseLine | None = None + has_next_item = False + while cursor < len(lines) and lines[cursor].text.strip(): + match = _marker(lines[cursor].text) + if match and len(match.group("indent")) == marker_indent: + has_next_item = True + elif not match and _leading_spaces(lines[cursor].text) <= marker_indent: + first_bad = first_bad or lines[cursor] + cursor += 1 + + if has_next_item and first_bad is not None: + yield first_bad + index = max(cursor, index + 1) + + +def lint_source(filename: str, source: str) -> list[LintMessage]: + messages: list[LintMessage] = [] + + for block in extract_prose_blocks(source): + for index, prose_line in enumerate(block): + if _missing_blank_before_indented_list(block, index): + messages.append( + LintMessage( + path=filename, + line=prose_line.source_line, + char=1, + code=LINTER_CODE, + severity=LintSeverity.ERROR, + name="list missing a preceding blank line", + original=None, + replacement=None, + description=( + "This indented list starts immediately after prose. " + "It renders as a list in the HTML tutorial, but Pandoc " + "can merge or mis-indent it in the generated notebook. " + "Add a blank narrative line before the list." + ), + ) + ) + + for prose_line in _bad_list_continuations(block): + messages.append( + LintMessage( + path=filename, + line=prose_line.source_line, + char=1, + code=LINTER_CODE, + severity=LintSeverity.ERROR, + name="unindented list continuation", + original=None, + replacement=None, + description=( + "This list item continues at the same indentation as the " + "list marker. Pandoc treats the continuation as ordinary " + "text in generated notebooks. Indent continuation lines " + "to the list item's content column." + ), + ) + ) + + return messages + + +def lint_file(filename: str) -> list[LintMessage]: + return lint_source(filename, Path(filename).read_text(encoding="utf-8")) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(fromfile_prefix_chars="@") + parser.add_argument("filenames", nargs="+") + return parser.parse_args() + + +def main() -> None: + args = parse_args() + for filename in args.filenames: + for message in lint_file(filename): + print(json.dumps(message._asdict())) + + +if __name__ == "__main__": + main() diff --git a/tools/linter/tests/test_tutorial_markup_linter.py b/tools/linter/tests/test_tutorial_markup_linter.py new file mode 100644 index 00000000000..c04cda34e37 --- /dev/null +++ b/tools/linter/tests/test_tutorial_markup_linter.py @@ -0,0 +1,109 @@ +import textwrap +import unittest + +from tools.linter.adapters.tutorial_markup_linter import lint_source + + +def lint(prose: str): + source = ( + '"""\n' + "Example tutorial\n" + "================\n\n" + f"{textwrap.dedent(prose)}\n" + '"""\n' + ) + return lint_source("example_tutorial.py", source) + + +class TutorialMarkupLinterTest(unittest.TestCase): + def test_reports_indented_list_without_blank_line(self): + messages = lint( + """\ + Parameters: + - first value + - second value""" + ) + + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].name, "list missing a preceding blank line") + + def test_allows_indented_list_after_blank_line(self): + messages = lint( + """\ + Parameters: + + - first value + - second value""" + ) + + self.assertEqual(messages, []) + + def test_allows_conventionally_indented_nested_list(self): + messages = lint( + """\ + - parent item + - nested item + - another nested item""" + ) + + self.assertEqual(messages, []) + + def test_allows_list_table_cells(self): + messages = lint( + """\ + .. list-table:: + + * - Heading + - Value""" + ) + + self.assertEqual(messages, []) + + def test_allows_list_aligned_inside_directive(self): + messages = lint( + """\ + .. note:: + This is directive content. + * first item + * second item""" + ) + + self.assertEqual(messages, []) + + def test_reports_unindented_list_continuation(self): + messages = lint( + """\ + - first item starts here + but its continuation is not indented + - second item""" + ) + + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].name, "unindented list continuation") + + def test_allows_indented_list_continuation(self): + messages = lint( + """\ + - first item starts here + and its continuation is indented + - second item""" + ) + + self.assertEqual(messages, []) + + def test_ignores_comments_outside_gallery_prose(self): + source = textwrap.dedent( + '''\ + """Example tutorial""" + + # Implementation details: + # - this is a code comment, not narrative prose + value = 1 + ''' + ) + + self.assertEqual(lint_source("example_tutorial.py", source), []) + + +if __name__ == "__main__": + unittest.main() From 28337e0a410817f2788983523108b6ed01c68737 Mon Sep 17 00:00:00 2001 From: ribhuji <56975412+ribhuji@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:40:11 +0530 Subject: [PATCH 2/2] fix list indentation issue --- advanced_source/usb_semisup_learn.py | 8 ++-- beginner_source/basics/autogradqs_tutorial.py | 8 ++-- beginner_source/basics/data_tutorial.py | 9 +++-- .../blitz/neural_networks_tutorial.py | 37 ++++++++++--------- beginner_source/nn_tutorial.py | 36 +++++++++--------- .../optimizer_step_in_backward_tutorial.py | 13 ++++--- recipes_source/recipes/profiler_recipe.py | 10 +++-- recipes_source/recipes/timer_quick_start.py | 17 +++++---- .../torch_compiler_set_stance_tutorial.py | 7 ++-- unstable_source/gpu_direct_storage.py | 7 ++-- 10 files changed, 83 insertions(+), 69 deletions(-) diff --git a/advanced_source/usb_semisup_learn.py b/advanced_source/usb_semisup_learn.py index 4ea6f621ab7..f58b5d9b323 100644 --- a/advanced_source/usb_semisup_learn.py +++ b/advanced_source/usb_semisup_learn.py @@ -71,14 +71,14 @@ # # - ``get_dataset`` to load dataset, here we use CIFAR-10 # - ``get_data_loader`` to create train (labeled and unlabeled) and test data -# loaders, the train unlabeled loaders will provide both strong and weak -# augmentation of unlabeled data +# loaders, the train unlabeled loaders will provide both strong and weak +# augmentation of unlabeled data # - ``get_net_builder`` to create a model, here we use pretrained ViT # - ``get_algorithm`` to create the semi-supervised learning algorithm, -# here we use ``FreeMatch`` and ``SoftMatch`` +# here we use ``FreeMatch`` and ``SoftMatch`` # - ``get_config``: to get default configuration of the algorithm # - ``Trainer``: a Trainer class for training and evaluating the -# algorithm on dataset +# algorithm on dataset # # Note that a CUDA-enabled backend is required for training with the ``semilearn`` package. # See `Enabling CUDA in Google Colab `__ for instructions diff --git a/beginner_source/basics/autogradqs_tutorial.py b/beginner_source/basics/autogradqs_tutorial.py index 8eff127ddee..48d42b325e1 100644 --- a/beginner_source/basics/autogradqs_tutorial.py +++ b/beginner_source/basics/autogradqs_tutorial.py @@ -87,6 +87,7 @@ ###################################################################### # .. note:: +# # - We can only obtain the ``grad`` properties for the leaf # nodes of the computational graph, which have ``requires_grad`` property # set to ``True``. For all other nodes in our graph, gradients will not be @@ -130,9 +131,10 @@ ###################################################################### # There are reasons you might want to disable gradient tracking: -# - To mark some parameters in your neural network as **frozen parameters**. -# - To **speed up computations** when you are only doing forward pass, because computations on tensors that do -# not track gradients would be more efficient. +# +# - To mark some parameters in your neural network as **frozen parameters**. +# - To **speed up computations** when you are only doing forward pass, because computations on tensors that do +# not track gradients would be more efficient. ###################################################################### diff --git a/beginner_source/basics/data_tutorial.py b/beginner_source/basics/data_tutorial.py index c49f25a587a..3b3d09b8086 100644 --- a/beginner_source/basics/data_tutorial.py +++ b/beginner_source/basics/data_tutorial.py @@ -39,10 +39,11 @@ # Each example comprises a 28×28 grayscale image and an associated label from one of 10 classes. # # We load the `FashionMNIST Dataset `_ with the following parameters: -# - ``root`` is the path where the train/test data is stored, -# - ``train`` specifies training or test dataset, -# - ``download=True`` downloads the data from the internet if it's not available at ``root``. -# - ``transform`` and ``target_transform`` specify the feature and label transformations +# +# - ``root`` is the path where the train/test data is stored, +# - ``train`` specifies training or test dataset, +# - ``download=True`` downloads the data from the internet if it's not available at ``root``. +# - ``transform`` and ``target_transform`` specify the feature and label transformations import torch diff --git a/beginner_source/blitz/neural_networks_tutorial.py b/beginner_source/blitz/neural_networks_tutorial.py index ae29411becc..262d94c4bf9 100644 --- a/beginner_source/blitz/neural_networks_tutorial.py +++ b/beginner_source/blitz/neural_networks_tutorial.py @@ -130,27 +130,30 @@ def forward(self, input): # Before proceeding further, let's recap all the classes you’ve seen so far. # # **Recap:** -# - ``torch.Tensor`` - A *multi-dimensional array* with support for autograd -# operations like ``backward()``. Also *holds the gradient* w.r.t. the -# tensor. -# - ``nn.Module`` - Neural network module. *Convenient way of -# encapsulating parameters*, with helpers for moving them to GPU, -# exporting, loading, etc. -# - ``nn.Parameter`` - A kind of Tensor, that is *automatically -# registered as a parameter when assigned as an attribute to a* -# ``Module``. -# - ``autograd.Function`` - Implements *forward and backward definitions -# of an autograd operation*. Every ``Tensor`` operation creates at -# least a single ``Function`` node that connects to functions that -# created a ``Tensor`` and *encodes its history*. +# +# - ``torch.Tensor`` - A *multi-dimensional array* with support for autograd +# operations like ``backward()``. Also *holds the gradient* w.r.t. the +# tensor. +# - ``nn.Module`` - Neural network module. *Convenient way of +# encapsulating parameters*, with helpers for moving them to GPU, +# exporting, loading, etc. +# - ``nn.Parameter`` - A kind of Tensor, that is *automatically +# registered as a parameter when assigned as an attribute to a* +# ``Module``. +# - ``autograd.Function`` - Implements *forward and backward definitions +# of an autograd operation*. Every ``Tensor`` operation creates at +# least a single ``Function`` node that connects to functions that +# created a ``Tensor`` and *encodes its history*. # # **At this point, we covered:** -# - Defining a neural network -# - Processing inputs and calling backward +# +# - Defining a neural network +# - Processing inputs and calling backward # # **Still Left:** -# - Computing the loss -# - Updating the weights of the network +# +# - Computing the loss +# - Updating the weights of the network # # Loss Function # ------------- diff --git a/beginner_source/nn_tutorial.py b/beginner_source/nn_tutorial.py index e04815bd27e..9710a0136db 100644 --- a/beginner_source/nn_tutorial.py +++ b/beginner_source/nn_tutorial.py @@ -775,8 +775,9 @@ def preprocess(x): # ----------------------------- # # Our CNN is fairly concise, but it only works with MNIST, because: -# - It assumes the input is a 28\*28 long vector -# - It assumes that the final CNN grid size is 4\*4 (since that's the average pooling kernel size we used) +# +# - It assumes the input is a 28\*28 long vector +# - It assumes that the final CNN grid size is 4\*4 (since that's the average pooling kernel size we used) # # Let's get rid of these two assumptions, so our model works with any 2d # single channel image. First, we can remove the initial Lambda layer by @@ -881,18 +882,19 @@ def preprocess(x, y): # ``torch.nn``, ``torch.optim``, ``Dataset``, and ``DataLoader``. So let's summarize # what we've seen: # -# - ``torch.nn``: -# -# + ``Module``: creates a callable which behaves like a function, but can also -# contain state(such as neural net layer weights). It knows what ``Parameter`` (s) it -# contains and can zero all their gradients, loop through them for weight updates, etc. -# + ``Parameter``: a wrapper for a tensor that tells a ``Module`` that it has weights -# that need updating during backprop. Only tensors with the `requires_grad` attribute set are updated -# + ``functional``: a module(usually imported into the ``F`` namespace by convention) -# which contains activation functions, loss functions, etc, as well as non-stateful -# versions of layers such as convolutional and linear layers. -# - ``torch.optim``: Contains optimizers such as ``SGD``, which update the weights -# of ``Parameter`` during the backward step -# - ``Dataset``: An abstract interface of objects with a ``__len__`` and a ``__getitem__``, -# including classes provided with Pytorch such as ``TensorDataset`` -# - ``DataLoader``: Takes any ``Dataset`` and creates an iterator which returns batches of data. +# - ``torch.nn``: +# +# + ``Module``: creates a callable which behaves like a function, but can also +# contain state(such as neural net layer weights). It knows what ``Parameter`` (s) it +# contains and can zero all their gradients, loop through them for weight updates, etc. +# + ``Parameter``: a wrapper for a tensor that tells a ``Module`` that it has weights +# that need updating during backprop. Only tensors with the `requires_grad` attribute set are updated +# + ``functional``: a module(usually imported into the ``F`` namespace by convention) +# which contains activation functions, loss functions, etc, as well as non-stateful +# versions of layers such as convolutional and linear layers. +# +# - ``torch.optim``: Contains optimizers such as ``SGD``, which update the weights +# of ``Parameter`` during the backward step +# - ``Dataset``: An abstract interface of objects with a ``__len__`` and a ``__getitem__``, +# including classes provided with Pytorch such as ``TensorDataset`` +# - ``DataLoader``: Takes any ``Dataset`` and creates an iterator which returns batches of data. diff --git a/intermediate_source/optimizer_step_in_backward_tutorial.py b/intermediate_source/optimizer_step_in_backward_tutorial.py index fd72f733c50..3c479a1aecc 100644 --- a/intermediate_source/optimizer_step_in_backward_tutorial.py +++ b/intermediate_source/optimizer_step_in_backward_tutorial.py @@ -235,12 +235,13 @@ def train(model): # :alt: snapshot.png loaded into CUDA Memory Visualizer # # Several major observations: -# 1. There is no more optimizer step! Right...we fused that into the backward. -# 2. Likewise, the backward drags longer and there are more random allocations -# for intermediates. This is expected, as the optimizer step requires -# intermediates. -# 3. Most importantly! The peak memory is lower! It is now ~4GB (which I -# hope maps closely to your earlier expectation). +# +# 1. There is no more optimizer step! Right...we fused that into the backward. +# 2. Likewise, the backward drags longer and there are more random allocations +# for intermediates. This is expected, as the optimizer step requires +# intermediates. +# 3. Most importantly! The peak memory is lower! It is now ~4GB (which I +# hope maps closely to your earlier expectation). # # Note that there is no longer any big chunk of memory allocated for the gradients # compared to before, accounting for ~1.2GB of memory savings. Instead, we've freed diff --git a/recipes_source/recipes/profiler_recipe.py b/recipes_source/recipes/profiler_recipe.py index ef0471e75de..4ff671a2433 100644 --- a/recipes_source/recipes/profiler_recipe.py +++ b/recipes_source/recipes/profiler_recipe.py @@ -72,10 +72,12 @@ # a number of parameters, some of the most useful are: # # - ``activities`` - a list of activities to profile: -# - ``ProfilerActivity.CPU`` - PyTorch operators, TorchScript functions and -# user-defined code labels (see ``record_function`` below); -# - ``ProfilerActivity.CUDA`` - on-device CUDA kernels; -# - ``ProfilerActivity.XPU`` - on-device XPU kernels; +# +# - ``ProfilerActivity.CPU`` - PyTorch operators, TorchScript functions and +# user-defined code labels (see ``record_function`` below); +# - ``ProfilerActivity.CUDA`` - on-device CUDA kernels; +# - ``ProfilerActivity.XPU`` - on-device XPU kernels; +# # - ``record_shapes`` - whether to record shapes of the operator inputs; # - ``profile_memory`` - whether to report amount of memory consumed by # model's Tensors; diff --git a/recipes_source/recipes/timer_quick_start.py b/recipes_source/recipes/timer_quick_start.py index d6b79e094c7..0d12567db97 100644 --- a/recipes_source/recipes/timer_quick_start.py +++ b/recipes_source/recipes/timer_quick_start.py @@ -14,14 +14,15 @@ **Contents:** - 1. `Defining a Timer <#defining-a-timer>`__ - 2. `Wall time: Timer.blocked_autorange(...) <#wall-time-timer-blocked-autorange>`__ - 3. `C++ snippets <#c-snippets>`__ - 4. `Instruction counts: Timer.collect_callgrind(...) <#instruction-counts-timer-collect-callgrind>`__ - 5. `Instruction counts: Delving deeper <#instruction-counts-delving-deeper>`__ - 6. `A/B testing with Callgrind <#a-b-testing-with-callgrind>`__ - 7. `Wrapping up <#wrapping-up>`__ - 8. `Footnotes <#footnotes>`__ + +1. `Defining a Timer <#defining-a-timer>`__ +2. `Wall time: Timer.blocked_autorange(...) <#wall-time-timer-blocked-autorange>`__ +3. `C++ snippets <#c-snippets>`__ +4. `Instruction counts: Timer.collect_callgrind(...) <#instruction-counts-timer-collect-callgrind>`__ +5. `Instruction counts: Delving deeper <#instruction-counts-delving-deeper>`__ +6. `A/B testing with Callgrind <#a-b-testing-with-callgrind>`__ +7. `Wrapping up <#wrapping-up>`__ +8. `Footnotes <#footnotes>`__ """ diff --git a/recipes_source/torch_compiler_set_stance_tutorial.py b/recipes_source/torch_compiler_set_stance_tutorial.py index 56b338db801..0fc9e542872 100644 --- a/recipes_source/torch_compiler_set_stance_tutorial.py +++ b/recipes_source/torch_compiler_set_stance_tutorial.py @@ -110,9 +110,10 @@ def outer(x): ###################################################################### # Other stances include: -# - ``"default"``: The default stance, used for normal compilation. -# - ``"eager_on_recompile"``: Run code eagerly when a recompile is necessary. If there is cached compiled code valid for the input, it will still be used. -# - ``"fail_on_recompile"``: Raise an error when recompiling a function. +# +# - ``"default"``: The default stance, used for normal compilation. +# - ``"eager_on_recompile"``: Run code eagerly when a recompile is necessary. If there is cached compiled code valid for the input, it will still be used. +# - ``"fail_on_recompile"``: Raise an error when recompiling a function. # # See the ``torch.compiler.set_stance`` `doc page `__ # for more stances and options. More stances/options may also be added in the future. diff --git a/unstable_source/gpu_direct_storage.py b/unstable_source/gpu_direct_storage.py index 2b06c53bc7f..d9e09cfda9c 100644 --- a/unstable_source/gpu_direct_storage.py +++ b/unstable_source/gpu_direct_storage.py @@ -42,9 +42,10 @@ ################################################################################ # The steps involved in the process are as follows: -# * Write the checkpoint file without any actual data. This reserves the space on disk. -# * Read the offsets for the storage associated with each tensor in the checkpoint using ``FakeTensor``. -# * Use ``GDSFile`` to write the appropriate data at these offsets. +# +# * Write the checkpoint file without any actual data. This reserves the space on disk. +# * Read the offsets for the storage associated with each tensor in the checkpoint using ``FakeTensor``. +# * Use ``GDSFile`` to write the appropriate data at these offsets. # # Given a state dictionary of tensors that are on the GPU, one can use the ``torch.serialization.skip_data`` context # manager to save a checkpoint that contains all relevant metadata except the storage bytes. For each ``torch.Storage``