Skip to main content
rule

tqdm — Cursor rule: progress bars in Python done right

A Cursor rules file that pins down how the agent should use tqdm in Python: imports, cleanup, console output and live metrics in the postfix.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/tqdm.mdc
Open on GitHub

What tqdm does

The rule lives in .cursor/rules/tqdm.mdc and fires on Python files matched by **/*.py. It ships eight sections, each with a bad and a good example: importing, context managers, manual updates, labels, tqdm.write() output, nested bars, pandas hooks and a note on speed.

In practice the agent drops import tqdm plus tqdm.tqdm(...) in favour of from tqdm import tqdm, trange. A bar gets wrapped in with ... as pbar: so it closes on exceptions. Manual pbar.update(n) calls must come with total, otherwise the percentage and ETA stay blank. Printing mid-loop means tqdm.write(), since plain print() smears an active bar across the terminal.

Nested loops get leave=False; byte counters get unit="B" alongside unit_scale=True. On tight loops the advice is to update per batch rather than per item. tqdm.pandas() registers .progress_apply() on a DataFrame, tqdm.auto picks terminal or notebook rendering on its own. None of the snippets run without the tqdm package installed, so add the dependency before you lean on the rule.

What it helps with

  • keep progress bars consistent across a codebase
  • stop print() calls from wrecking an active bar
  • close the bar when a loop raises
  • label file downloads and training runs with live metrics
  • show progress for pandas operations and notebooks

Example prompts for the agent

  • Wrap the training loop in a tqdm bar per the rule, with a context manager and set_postfix for loss.
  • Swap the print() inside this loop for tqdm.write().
  • Add progress to df.progress_apply and hide nested bars with leave=False.

How to install tqdm

Manually: put the files in place

.cursor/rules/tqdm.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about tqdm

How do I install the tqdm rule in Cursor?

Place .cursor/rules/tqdm.mdc inside the .cursor/rules folder of your project. Cursor picks it up and applies it to **/*.py files.

What does the tqdm rule do?

It gives the Cursor agent a fixed set of conventions for tqdm: direct imports, context managers instead of calling close() by hand, total whenever updates are manual, and tqdm.write() for messages.

Why avoid print() while a bar is running?

It collides with the bar's own output and breaks the line. The rule routes messages through tqdm.write() so the display stays intact.

Source text

tqdm.mdc · CC0-1.0
---
description: This guide defines best practices for using the `tqdm` library to implement clear, efficient, and robust progress bars in Python applications, ensuring consistent user feedback and maintainable code.
globs: **/*.py
---
# tqdm Best Practices

`tqdm` is the definitive library for adding progress bars to Python applications. It's lightweight, fast, and works across various environments (terminal, Jupyter). This guide outlines the mandatory practices for its use.

## 1. Import Strategy

Always import `tqdm` directly from the library for clarity. Use `trange` as a convenient shortcut for `tqdm(range(N))`.

❌ BAD: Generic import
```python
import tqdm
for i in tqdm.tqdm(range(100)):
    pass
```

✅ GOOD: Direct and clear imports
```python
from tqdm import tqdm, trange

for i in trange(100):  # Use trange for simple range loops
    pass

data = [1, 2, 3] * 10
for item in tqdm(data, desc="Processing items"): # Use tqdm for iterables
    pass
```

## 2. Prefer Context Managers

Use `tqdm` as a context manager (`with ... as pbar:`) whenever possible. This ensures the progress bar is properly closed and resources are released, even if exceptions occur.

❌ BAD: Manual `close()` (prone to errors)
```python
pbar = tqdm(total=100)
for i in range(100):
    pbar.update(1)
    # What if an error occurs here? pbar.close() might be skipped.
pbar.close()
```

✅ GOOD: Context manager for robust cleanup
```python
with tqdm(total=100, desc="Calculating") as pbar:
    for i in range(100):
        # Simulate work
        if i == 50:
            # Example of an error, pbar will still close
            # raise ValueError("Something went wrong!")
            pass
        pbar.update(1)
```

## 3. Manual Updates for Non-Iterables or Custom Logic

When wrapping an iterable isn't feasible (e.g., callbacks, async operations, or custom progress logic), use `pbar.update(n)`. **Always provide `total`** when using manual updates.

❌ BAD: Missing `total` with manual updates (bar won't show percentage/ETA)
```python
pbar = tqdm(desc="Downloading")
# ... later in a callback ...
pbar.update(chunk_size)
```

✅ GOOD: Explicit `total` for manual updates
```python
import time
import random

file_size = 1024 * 1024 * 50  # 50 MB
downloaded_bytes = 0

with tqdm(total=file_size, unit="B", unit_scale=True, desc="Downloading file") as pbar:
    while downloaded_bytes < file_size:
        chunk = random.randint(1024, 1024 * 10) # Simulate chunk download
        chunk = min(chunk, file_size - downloaded_bytes)
        downloaded_bytes += chunk
        pbar.update(chunk)
        time.sleep(0.01)
```

## 4. Use Descriptive Labels and Postfix Information

Enhance readability by providing a `desc` (description) and dynamic `postfix` information. `set_description()` and `set_postfix()` are preferred for updating these dynamically.

❌ BAD: Static or cluttered description
```python
for epoch in tqdm(range(10), desc="Epoch"):
    loss = 0.5 - epoch * 0.01
    # This doesn't update dynamically without re-creating the bar
    for batch in tqdm(range(100), desc=f"Epoch {epoch} Loss: {loss:.4f}", leave=False):
        pass
```

✅ GOOD: Dynamic description and postfix updates
```python
import random

for epoch in tqdm(range(10), desc="Training Epochs"):
    total_loss = 0.0
    for batch in trange(100, desc=f"Batch {epoch+1}", leave=False):
        current_loss = random.uniform(0.1, 0.5)
        total_loss += current_loss
        # Update postfix with real-time metrics
        tqdm.set_postfix(loss=f"{current_loss:.4f}", avg_loss=f"{total_loss/(batch+1):.4f}")
        # Simulate work
        time.sleep(0.005)
```

## 5. Handle Output with `tqdm.write()`

Never use `print()` directly when a `tqdm` bar is active. It will corrupt the progress bar display. Use `tqdm.write()` instead, which intelligently prints messages without interfering.

❌ BAD: Corrupts progress bar
```python
for i in trange(10):
    if i == 5:
        print("Mid-loop message!") # This will mess up the bar
    time.sleep(0.1)
```

✅ GOOD: Preserves progress bar integrity
```python
for i in trange(10, desc="Processing"):
    if i == 5:
        tqdm.write("Mid-loop message: Halfway there!")
    time.sleep(0.1)
```

## 6. Fine-tune Display Options

Use `leave=False` for inner loops to remove them upon completion, keeping the terminal clean. Leverage `unit`, `unit_scale`, and `dynamic_ncols` for better presentation.

❌ BAD: Cluttered terminal with nested bars
```python
for i in tqdm(range(3), desc="Outer"):
    for j in tqdm(range(5), desc="Inner"): # leave=True by default
        time.sleep(0.05)
```

✅ GOOD: Clean nested bars and scaled units
```python
for i in tqdm(range(3), desc="Outer Loop"):
    for j in tqdm(range(5), desc="Inner Loop", leave=False): # Bar disappears after inner loop finishes
        time.sleep(0.05)

# Example with units
with tqdm(total=100_000_000, unit="B", unit_scale=True, dynamic_ncols=True, desc="Transferring") as pbar:
    for _ in range(100):
        pbar.update(1_000_000)
        time.sleep(0.01)
```

## 7. Integrate with Data Science Libraries

`tqdm.pandas()` and `tqdm.auto` provide seamless integration for common data science workflows.

### Pandas DataFrames

Use `tqdm.pandas()` to add a `.progress_apply()` method to DataFrames.

```python
import pandas as pd
from tqdm.auto import tqdm # Use tqdm.auto for notebook compatibility

tqdm.pandas() # Register tqdm with pandas

df = pd.DataFrame({'col': range(100000)})

# Applying a function with a progress bar
df['new_col'] = df['col'].progress_apply(lambda x: x * 2)
```

### Jupyter Notebooks

`tqdm.auto` automatically detects if it's running in a Jupyter environment and uses the appropriate display.

```python
# In a .py file that might be run in a notebook or terminal
from tqdm.auto import tqdm, trange

def long_running_task():
    for i in trange(100, desc="Auto-detected progress"):
        time.sleep(0.01)

long_running_task()
```

## 8. Performance Considerations

`tqdm` is highly optimized, but for extremely fast loops (e.g., millions of iterations per second), consider batching updates or disabling the bar if not strictly necessary.

```python
# For loops where each iteration is extremely fast
total_iterations = 10_000_000
batch_size = 1000

with tqdm(total=total_iterations, desc="Batch Processing") as pbar:
    for i in range(0, total_iterations, batch_size):
        # Perform batch_size operations here
        # ...
        pbar.update(batch_size) # Update once per batch
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Programming languages →

From the same set240

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor