Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7926cc4
Refactor printers: extract formatting into lightweight base classes
rlundeen2 May 14, 2026
30f6151
Consolidate all printers into pyrit/printer/ module
rlundeen2 May 14, 2026
de61795
Add deprecation warnings for old printer import paths (removed in 0.1…
rlundeen2 May 14, 2026
837ed3f
Rename concrete printers to *MemoryPrinter, move pyrit internals out …
rlundeen2 May 14, 2026
788eceb
Refactor markdown printer, delete dead old ABC files
rlundeen2 May 14, 2026
91a417a
Add missing __all__ to scenario printer deprecation shim
rlundeen2 May 14, 2026
f31d0d0
Fix type checker errors in from_dict methods and MemoryPrinter types
rlundeen2 May 14, 2026
86172c9
Fix ruff lint errors: return types, docstrings, noqa
rlundeen2 May 14, 2026
d117af2
Fix ty type check: make ScenarioResult identifier params optional
rlundeen2 May 14, 2026
65becc5
pr feedback
rlundeen2 May 14, 2026
1eeb7a3
pre-commit
rlundeen2 May 14, 2026
4f29026
fixing test
rlundeen2 May 14, 2026
69ccff3
fixing test
rlundeen2 May 15, 2026
bf32513
self-review
rlundeen2 May 15, 2026
e6a939b
Rename console→pretty, add Sink/PrinterBase plumbing
rlundeen2 May 15, 2026
ae13cdb
Route all output through sinks, add write_async and convenience methods
rlundeen2 May 15, 2026
35f8e9d
Move convenience functions into their domain modules
rlundeen2 May 15, 2026
4720ff7
Move convenience functions to printer/helpers.py
rlundeen2 May 15, 2026
65fedab
Add copilot instructions for printer module
rlundeen2 May 15, 2026
fdfabcc
Add render_async/write_async contract, rename to→sink, add deprecatio…
rlundeen2 May 15, 2026
bd6f021
Extract conversation and score printers, slim attack_result/pretty.py
rlundeen2 May 15, 2026
1bdc53d
Add render_async to leaf classes, update printer instructions
rlundeen2 May 15, 2026
17b8bc7
Add IPythonMarkdownSink, get_default_sink auto-detection, markdown co…
rlundeen2 May 15, 2026
62efab5
adding docs
rlundeen2 May 15, 2026
582f34a
updating help
rlundeen2 May 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/instructions/printer.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
applyTo: "pyrit/printer/**"
---

# PyRIT Printer Module — Coding & Review Guidelines

For full architecture documentation, usage examples, and extension guides, see [doc/code/printer/0_printer.py](../../../doc/code/printer/0_printer.py).

This file covers the rules for **writing and reviewing** code in `pyrit/printer/`.

## Critical Rules

### Output goes through the sink — never call `print()` directly

All rendering methods return `str`. The inherited `write_async` calls `render_async` then `_write_async(content)`. No bare `print()` calls anywhere in the printer module except inside `StdoutSink`.

When reviewing: reject any `print()` call outside `StdoutSink`.

### Data fetching belongs in leaf classes only

Format classes (`PrettyAttackResultPrinter`, `MarkdownAttackResultPrinter`) must not import or reference `CentralMemory`. Only `*MemoryPrinter` leaf classes do data I/O.

When reviewing: reject any `CentralMemory` import in a non-leaf file (`pretty.py`, `markdown.py`, `json.py`).

### Sinks must use async I/O

Sink implementations must not block the event loop. Use `asyncio.to_thread()` or native async libraries for I/O operations. `FileSink` uses an `asyncio.Lock` to prevent concurrent write races.

When reviewing: reject synchronous `open()`, `write()`, or network calls inside a sink's `write_async`.

## Three-Layer Hierarchy

Every domain follows this structure. Do not mix responsibilities across layers.

| Layer | File | Responsibility | May import CentralMemory? |
|-------|------|---------------|---------------------------|
| **Base** | `base.py` | Abstract data-fetching methods + abstract `render_async` | No |
| **Format** | `pretty.py`, `markdown.py`, `json.py` | Implements `render_async`, returns `str` | No |
| **Leaf** | Same file as format (e.g., `PrettyAttackResultMemoryPrinter`) | Implements data methods via CentralMemory; forwarding `render_async` | Yes |

### File names = output format

- `pretty.py` — ANSI-colored human-readable
- `markdown.py` — Markdown
- `json.py` — structured JSON

### Memory leaf classes must work with zero args

```python
printer = PrettyAttackResultMemoryPrinter() # defaults: StdoutSink, matching sub-printers
await printer.write_async(result)
```

Pass `sink=` to redirect output. Pass sub-printers only to override defaults.

### Convenience functions live in `helpers.py`

Every new domain printer **must** have a corresponding convenience function added to `helpers.py`. This is the primary entry point most callers use.

```python
from pyrit.printer.helpers import print_attack_result_async
await print_attack_result_async(result, format="pretty")
```

`helpers.py` resolves `format` → printer class, `sink` → Sink, and calls `write_async`.

When reviewing: if a new domain printer is added without a helper function, request one.
5 changes: 5 additions & 0 deletions doc/code/framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ Run standardized evaluation scenarios at scale across harm categories.
Register and discover targets, scorers, and converters via class and instance registries.
::::

::::{card} 🖨️ Printer
:link: ./printer/0_printer
Render attack results, scenario results, conversations, and scores to terminal, files, or Jupyter.
::::

:::::

---
Expand Down
Loading
Loading