Open-Source Wikis

/

PyTorch

/

How to contribute

/

Patterns and conventions

pytorch/pytorch

Patterns and conventions

The repo's CLAUDE.md / AGENTS.md is the authoritative short list of conventions; this page expands on them and adds the codebase-wide patterns you'll see when reading PyTorch source.

Coding style (from CLAUDE.md)

  • Minimize comments. Code should be self-documenting. Comments are for non-obvious global context — not for restating what the next line does.
  • No trivial 1-2-LOC helpers. If it's used once, inline it.
  • Clear abstractions. State management explicit. No dynamic setattr/getattr in lieu of a class definition.
  • Match existing style. Look at the file you're editing.
  • Assume PyTorch familiarity. Don't explain what Tensor is.
  • When uncertain, choose the simpler implementation.

C++

  • C++20. PyTorch builds with -std=c++20.
  • Headers under c10 are header-only. Don't pull in heavy ATen headers from c10.
  • Use c10::intrusive_ptr for refcounted shared types. Inherit c10::intrusive_ptr_target.
  • Use c10::SmallVector/c10::ArrayRef/c10::optional. They are PyTorch's idiom; std::vector/std::span/std::optional are also accepted now (PyTorch is C++20) but the existing code uses the c10 versions.
  • TORCH_CHECK(cond, msg) for runtime checks. Throws a c10::Error on failure.
  • TORCH_INTERNAL_ASSERT(cond) for invariants that should never fail.
  • c10::optional is now std::optional. Both work; new code can use either.
  • Don't catch and swallow exceptions. Errors should surface.

Python

  • Type hints are expected on new code. Old code is being incrementally annotated.
  • Use torch.Tensor types in signatures, not Any.
  • @torch.jit.unused for code that shouldn't be scripted.
  • torch.fx.wrap to mark a function as opaque to FX tracing.
  • Use torch._dynamo.config.patch for temporary config changes (per CLAUDE.md's "Dynamo Config" section). Don't manually save/restore.

Error handling

  • C++ ops use TORCH_CHECK with a clear message and the values involved.
  • Python user-facing errors should be RuntimeError (not Exception) and reference the offending input where possible.
  • Don't assert in production code paths. assert is stripped by python -O.
  • For impossible-to-reach code, TORCH_INTERNAL_ASSERT (C++) or raise RuntimeError("internal: ...") (Python).

Op authoring

The canonical guide is aten/src/ATen/native/README.md. The patterns:

  • Schemas live in native_functions.yaml. One source of truth.
  • Three variants per op: op (functional), op_ (in-place), op.out (out-variant taking explicit output).
  • Structured kernels for new pointwise/reduction ops. The shape rule (meta function) and the loop are separated.
  • Backwards in derivatives.yaml. Closed-form when possible; helper functions for complex cases live in tools/autograd/templates/Functions.cpp.
  • OpInfo for testing. torch/testing/_internal/common_methods_invocations.py.
  • Tagging. Mark pointwise, inplace_view, data_dependent_output, etc., in native_functions.yaml.

Logging

Per CLAUDE.md:

  • Two personas: local development and production. Code should support both.
  • Production-friendly: torch._logging.trace_structured. Artifact-shaped logs that are visible to tlparse. No runtime cost when tracing is disabled.
  • Local development: optional file dumps. OK for true internal compiler exceptions where reproducibility matters.
  • Error messages should mention both options to the user when relevant: "FX graph dump: foo.txt" + "Use tlparse to extract artifacts" (only if tracing enabled).
  • Use _get_unique_path() to avoid overwriting debug files.

Concurrency / GIL

  • The GIL is held in most C++ paths. Released only via pybind11::gil_scoped_release in long-running native sections (training step, dataloader fetch).
  • record_function spans are GIL-friendly; they don't allocate.
  • Don't acquire the GIL twice. Use pybind11::gil_scoped_acquire carefully in callbacks.

Tensor-author idioms

  • Prefer c10::SmallVector<int64_t, N> over std::vector<int64_t> for sizes/strides (typical N is 5).
  • Use IntArrayRef for size/stride arguments. Cheap pass-by-reference.
  • Use Tensor::contiguous() once at the top of a kernel if you need a contiguous view.
  • Use TensorIterator for elementwise/reduction ops instead of hand-rolling loops.

Multiline string in B950 lint failures

Per CLAUDE.md's explicit guidance: if B950 line too long triggers on a multiline string, you can't add # noqa: B950 inside the string (it would change the content) or break the string (it would change the content). Instead put # noqa: B950 on the line with the closing triple-quote:

self.assertExpectedInline(
    foo(),
    """
this line is too long...
""",  # noqa: B950
)

ghstack hygiene

Per CLAUDE.md:

  • Don't amend ghstack commits unless asked. Leave changes uncommitted for review with git diff.
  • Preserve ghstack-source-id and Pull-Request: trailers.
  • Submit with ghstack (full stack) or ghstack --no-stack (current commit only).
  • ghstack -u to push only an updated commit message.
  • Never git push to gh/USERNAME/N branches.
  • CONTRIBUTING.md — the canonical rulebook.
  • aten/src/ATen/native/README.md — op-author guide.
  • Testing — test idioms.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – PyTorch wiki | Factory