# Linting And Formatting

> Code style guidelines for NeMo-RL (Python and shell). Covers naming, indentation, comments, docstrings, reflection avoidance, and uv usage.

- Skill: `nvidia-nemo-rl/linting-and-formatting` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nvidia-nemo-rl/linting-and-formatting`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nvidia-nemo-rl/linting-and-formatting/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: NVIDIA-NeMo (https://skillmd.com/u/nvidia-nemo-rl)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/nvidia-nemo-rl/linting-and-formatting

---


# Code Style

## Style Guides

- Python: [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)
- Shell: [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html)

This repository is Python-first.

## uv

Use `uv run` to execute scripts. Do not activate a virtual environment and call `python` directly.

**Do:**
```bash
uv run examples/run_grpo.py
```

**Don't:**
```bash
source .venv/bin/activate
python examples/run_grpo.py
```

Exception: `Dockerfile.ngc_pytorch` is exempt from this rule.

## Python Standard

Code must conform to Python 3.13.13+.

## Indentation

Indent with 4 spaces. Do not use tabs.

## Naming

| Kind | Convention | Example |
|------|-----------|---------|
| Files | snake_case | `some_file.py` |
| Classes | PascalCase | `class SomeClass` |
| Functions/methods | snake_case | `def my_awesome_function():` |
| Local variables | snake_case | `my_variable = ...` |
| Variables starting with a number | prefix `k` | `k_99th_percentile = ...` |
| Global variables | upper snake_case + prefix `G` | `G_MY_GLOBAL = ...` |
| Constants | upper snake_case | `MY_CONSTANT = ...` |

- Avoid shadowing variables declared in an outer scope.
- Initialize all externally visible members of a class in the constructor.

## Comments

- For interfaces used outside a file, prefer docstrings over comments.
- Comments are for code within a function or file-local interfaces.
- Commented-out code must have a comment explaining why it is commented out. Otherwise remove it before merging.

## Docstrings

Use [Google style](https://google.github.io/styleguide/pyguide.html) docstrings (parseable by Sphinx).

## Enforce Keyword Arguments for Ambiguous Parameters

When a function has multiple parameters of the same type that could easily be swapped by mistake, use a bare `*` to force keyword-only arguments starting from where the ambiguity begins. This prevents callers from accidentally transposing arguments.

**Don't:**
```python
def loss_fn(input: Tensor, cp_group: ProcessGroup, tp_group: ProcessGroup, cp_rank: int, tp_rank: int):
    ...

# Caller can silently swap cp_group/tp_group or cp_rank/tp_rank
loss_fn(x, tp_group, cp_group, tp_rank, cp_rank)  # wrong order, no error
```

**Do:**
```python
def loss_fn(input: Tensor, *, cp_group: ProcessGroup, tp_group: ProcessGroup, cp_rank: int, tp_rank: int):
    ...

# Caller must name every argument — swaps are impossible
loss_fn(x, cp_group=cp_group, tp_group=tp_group, cp_rank=cp_rank, tp_rank=tp_rank)
```

## Avoid Reflection

Do not use reflection when functionality can be achieved without it.

**Don't:**
```python
def make_complex(*args):
    x, y = args
    return dict(**locals())
```

**Do:**
```python
def make_complex(x, y):
    return {'x': x, 'y': y}
```

## Type Annotations

Annotate new functions and methods — both parameters and return type. When you add a parameter to an
existing signature, type it, and **match the type already used at the call site** (don't leave a new arg
untyped while the caller already declares it, e.g. `def __init__(self, teacher_worker_groups=None)` when the
caller passes `teacher_worker_groups: Optional[dict[str, Any]]`).

When you add a new module under a type-checked area, **add it to `pyrefly.toml` `project-includes`**.
`pyrefly` checks an explicit allow-list of files, so a new file that isn't listed silently escapes
type-checking and its annotations are never verified.

## Imports

Put imports at module top. Defer an `import` into a function body ONLY to break a circular import or to avoid
loading a heavy/optional dependency in a path that shouldn't need it — and when you do, add a one-line comment
saying which. An in-function `import` with no such reason should move to the top. In particular, deferring
stdlib (`concurrent.futures`, `collections`, …) or a module that is *already* imported at module top buys
nothing — hoist it.

