Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
670cf12
MAINT: Drop 10 unused dev dependencies
romanlutz Jun 3, 2026
f0aeaef
MAINT: Flip ty unresolved-import to error and fix fallout
romanlutz Jun 3, 2026
3c141df
MAINT: Use public scenario import path in contract test
romanlutz Jun 3, 2026
1144191
MAINT: Restore short scenario import paths in unit tests
romanlutz Jun 3, 2026
d13b14a
MAINT: Use public scenario short paths for top-level imports
romanlutz Jun 3, 2026
7b9a2f5
DOCS: Sync notebooks to .py scenario import shortening
romanlutz Jun 3, 2026
9a931f6
MAINT: Drop pyrit/ source files from ty unresolved-import override
romanlutz Jun 4, 2026
6cd8d79
MAINT: Drop test-file ty unresolved-import override
romanlutz Jun 4, 2026
8da6b3e
Merge branch 'main' into pr/1931/romanlutz/audit-unused-dependencies
romanlutz Jun 4, 2026
dd03e7b
Merge remote-tracking branch 'origin/main' into pr/1931/romanlutz/aud…
romanlutz Jun 4, 2026
a30f2a5
Merge remote-tracking branch 'origin/main' into pr/1931/romanlutz/aud…
Copilot Jun 4, 2026
c7894d4
Merge remote-tracking branch 'origin/main' into pr/1931/romanlutz/aud…
Copilot Jun 4, 2026
3355b39
Merge remote-tracking branch 'origin/main' into pr/1931/romanlutz/aud…
Copilot Jun 5, 2026
4a776ca
Fix ty errors surfaced by merge with main
Copilot Jun 5, 2026
4c81e25
Merge branch 'main' into pr/1931/romanlutz/audit-unused-dependencies
Copilot Jun 5, 2026
ec9d501
Address review comments from hannahwestra25
Copilot Jun 5, 2026
1049038
Make scenario short-path aliases safe for submodules
Copilot Jun 5, 2026
14428b5
Merge branch 'main' into pr/1931/romanlutz/audit-unused-dependencies
Copilot Jun 5, 2026
0cb28d5
Move scenario alias imports to top of file (review feedback)
Copilot Jun 5, 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
16 changes: 16 additions & 0 deletions .github/instructions/style-guide.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,22 @@ from pyrit.common.net_utility import get_httpx_client

Within the same package, import from the specific file to avoid circular imports.

### Typing Backports (`typing_extensions`)

For typing features that don't exist on every supported Python (`Self`,
`override`, `TypeAlias`, `Unpack`, `NotRequired`, etc.), import from
``typing_extensions`` rather than ``typing``. `typing_extensions` is already a
transitive dependency (pulled in by ``pydantic``) and works across all supported
Python versions, so this avoids per-version branching and ``# type: ignore`` noise.

```python
# CORRECT — works on 3.10+
from typing_extensions import Self, override

# INCORRECT — `Self` is 3.11+, `override` is 3.12+, breaks on older runtimes
from typing import Self, override
```

## Documentation Standards

### Docstring Format
Expand Down
9 changes: 7 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,16 @@ repos:
name: Ruff (Jupyter Notebooks)
args: [--fix]

- repo: https://github.com/allganize/ty-pre-commit
rev: v0.0.43
- repo: local
hooks:
- id: ty-check
name: ty (type check)
# Run ty in the project's uv-managed venv so it can resolve project
# dependencies (tqdm etc). The third-party ty-pre-commit hook installs
# ty in an isolated env without project deps, which causes spurious
# ``unresolved-import`` errors now that the rule is set to ``error``.
entry: uv run --link-mode=copy ty check
language: system
files: ^pyrit/
exclude: ^pyrit/auxiliary_attacks/
types: [python]
4 changes: 2 additions & 2 deletions doc/code/scenarios/1_common_scenario_parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"\n",
"from pyrit.output import output_scenario_async\n",
"from pyrit.registry import TargetRegistry\n",
"from pyrit.scenario.scenarios.foundry import FoundryStrategy, RedTeamAgent\n",
"from pyrit.scenario.foundry import FoundryStrategy, RedTeamAgent\n",
"from pyrit.setup import initialize_from_config_async\n",
"\n",
"await initialize_from_config_async(config_path=Path(\"../../scanner/pyrit_conf.yaml\")) # type: ignore\n",
Expand Down Expand Up @@ -204,7 +204,7 @@
"metadata": {},
"outputs": [],
"source": [
"from pyrit.scenario.scenarios.foundry import FoundryComposite\n",
"from pyrit.scenario.foundry import FoundryComposite\n",
"\n",
"composite_strategy = [FoundryComposite(attack=FoundryStrategy.Crescendo, converters=[FoundryStrategy.Base64])]"
]
Expand Down
4 changes: 2 additions & 2 deletions doc/code/scenarios/1_common_scenario_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from pyrit.output import output_scenario_async
from pyrit.registry import TargetRegistry
from pyrit.scenario.scenarios.foundry import FoundryStrategy, RedTeamAgent
from pyrit.scenario.foundry import FoundryStrategy, RedTeamAgent
from pyrit.setup import initialize_from_config_async

await initialize_from_config_async(config_path=Path("../../scanner/pyrit_conf.yaml")) # type: ignore
Expand Down Expand Up @@ -85,7 +85,7 @@
# For example, to run Crescendo with Base64 encoding applied:

# %%
from pyrit.scenario.scenarios.foundry import FoundryComposite
from pyrit.scenario.foundry import FoundryComposite

composite_strategy = [FoundryComposite(attack=FoundryStrategy.Crescendo, converters=[FoundryStrategy.Base64])]

Expand Down
2 changes: 1 addition & 1 deletion doc/code/scenarios/2_custom_scenario_parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
}
],
"source": [
"from pyrit.scenario.scenarios.airt.scam import Scam\n",
"from pyrit.scenario.airt.scam import Scam\n",
"from pyrit.setup import initialize_pyrit_async\n",
"from pyrit.setup.initializers.components import ScenarioTechniqueInitializer\n",
"\n",
Expand Down
3 changes: 1 addition & 2 deletions doc/code/scenarios/2_custom_scenario_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
# would wire up memory and scorers):

# %%

from pyrit.scenario.scenarios.airt.scam import Scam
from pyrit.scenario.airt.scam import Scam
from pyrit.setup import initialize_pyrit_async
from pyrit.setup.initializers.components import ScenarioTechniqueInitializer

Expand Down
12 changes: 6 additions & 6 deletions doc/scanner/airt.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
}
],
"source": [
"from pyrit.scenario.scenarios.airt import RapidResponse, RapidResponseStrategy\n",
"from pyrit.scenario.airt import RapidResponse, RapidResponseStrategy\n",
"\n",
"dataset_config = DatasetConfiguration(dataset_names=[\"airt_hate\"], max_dataset_size=1)\n",
"\n",
Expand Down Expand Up @@ -254,7 +254,7 @@
}
],
"source": [
"from pyrit.scenario.scenarios.airt import Psychosocial, PsychosocialStrategy\n",
"from pyrit.scenario.airt import Psychosocial, PsychosocialStrategy\n",
"\n",
"dataset_config = DatasetConfiguration(dataset_names=[\"airt_imminent_crisis\"], max_dataset_size=1)\n",
"\n",
Expand Down Expand Up @@ -398,7 +398,7 @@
}
],
"source": [
"from pyrit.scenario.scenarios.airt import Cyber, CyberStrategy\n",
"from pyrit.scenario.airt import Cyber, CyberStrategy\n",
"\n",
"dataset_config = DatasetConfiguration(dataset_names=[\"airt_malware\"], max_dataset_size=1)\n",
"\n",
Expand Down Expand Up @@ -520,7 +520,7 @@
"metadata": {},
"outputs": [],
"source": [
"from pyrit.scenario.scenarios.airt import Jailbreak, JailbreakStrategy\n",
"from pyrit.scenario.airt import Jailbreak, JailbreakStrategy\n",
"\n",
"dataset_config = DatasetConfiguration(dataset_names=[\"airt_harms\"], max_dataset_size=1)\n",
"\n",
Expand Down Expand Up @@ -1017,7 +1017,7 @@
}
],
"source": [
"from pyrit.scenario.scenarios.airt import Leakage, LeakageStrategy\n",
"from pyrit.scenario.airt import Leakage, LeakageStrategy\n",
"\n",
"dataset_config = DatasetConfiguration(dataset_names=[\"airt_leakage\"], max_dataset_size=1)\n",
"\n",
Expand Down Expand Up @@ -1156,7 +1156,7 @@
}
],
"source": [
"from pyrit.scenario.scenarios.airt import Scam, ScamStrategy\n",
"from pyrit.scenario.airt import Scam, ScamStrategy\n",
"\n",
"dataset_config = DatasetConfiguration(dataset_names=[\"airt_scams\"], max_dataset_size=1)\n",
"\n",
Expand Down
12 changes: 6 additions & 6 deletions doc/scanner/airt.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
# **Available strategies:** ALL, DEFAULT, SINGLE_TURN, MULTI_TURN, role_play, many_shot, tap

# %%
from pyrit.scenario.scenarios.airt import RapidResponse, RapidResponseStrategy
from pyrit.scenario.airt import RapidResponse, RapidResponseStrategy

dataset_config = DatasetConfiguration(dataset_names=["airt_hate"], max_dataset_size=1)

Expand Down Expand Up @@ -99,7 +99,7 @@
# meaningful because psychosocial harms emerge through multi-turn escalation.

# %%
from pyrit.scenario.scenarios.airt import Psychosocial, PsychosocialStrategy
from pyrit.scenario.airt import Psychosocial, PsychosocialStrategy

dataset_config = DatasetConfiguration(dataset_names=["airt_imminent_crisis"], max_dataset_size=1)

Expand Down Expand Up @@ -132,7 +132,7 @@
# **Available strategies:** ALL, MULTI_TURN, red_teaming

# %%
from pyrit.scenario.scenarios.airt import Cyber, CyberStrategy
from pyrit.scenario.airt import Cyber, CyberStrategy

dataset_config = DatasetConfiguration(dataset_names=["airt_malware"], max_dataset_size=1)

Expand Down Expand Up @@ -165,7 +165,7 @@
# **Available strategies:** ALL, SIMPLE, COMPLEX, PromptSending, ManyShot, SkeletonKey, RolePlay

# %%
from pyrit.scenario.scenarios.airt import Jailbreak, JailbreakStrategy
from pyrit.scenario.airt import Jailbreak, JailbreakStrategy

dataset_config = DatasetConfiguration(dataset_names=["airt_harms"], max_dataset_size=1)

Expand Down Expand Up @@ -213,7 +213,7 @@
# no built-in threshold — the scorer returns a raw float for you to interpret per your use case.

# %%
from pyrit.scenario.scenarios.airt import Leakage, LeakageStrategy
from pyrit.scenario.airt import Leakage, LeakageStrategy

dataset_config = DatasetConfiguration(dataset_names=["airt_leakage"], max_dataset_size=1)

Expand Down Expand Up @@ -245,7 +245,7 @@
# **Available strategies:** ALL, SINGLE_TURN, MULTI_TURN, ContextCompliance, RolePlay, PersuasiveRedTeamingAttack

# %%
from pyrit.scenario.scenarios.airt import Scam, ScamStrategy
from pyrit.scenario.airt import Scam, ScamStrategy

dataset_config = DatasetConfiguration(dataset_names=["airt_scams"], max_dataset_size=1)

Expand Down
2 changes: 1 addition & 1 deletion doc/scanner/benchmark.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"from pyrit.output import output_scenario_async\n",
"from pyrit.prompt_target import OpenAIChatTarget\n",
"from pyrit.scenario import DatasetConfiguration\n",
"from pyrit.scenario.scenarios.benchmark import AdversarialBenchmark\n",
"from pyrit.scenario.benchmark import AdversarialBenchmark\n",
"from pyrit.setup import IN_MEMORY, initialize_pyrit_async\n",
"from pyrit.setup.initializers import LoadDefaultDatasets, ScorerInitializer, TargetInitializer\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion doc/scanner/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
from pyrit.output import output_scenario_async
from pyrit.prompt_target import OpenAIChatTarget
from pyrit.scenario import DatasetConfiguration
from pyrit.scenario.scenarios.benchmark import AdversarialBenchmark
from pyrit.scenario.benchmark import AdversarialBenchmark
from pyrit.setup import IN_MEMORY, initialize_pyrit_async
from pyrit.setup.initializers import LoadDefaultDatasets, ScorerInitializer, TargetInitializer

Expand Down
6 changes: 3 additions & 3 deletions doc/scanner/foundry.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"from pyrit.output import output_scenario_async\n",
"from pyrit.registry import TargetRegistry\n",
"from pyrit.scenario import DatasetConfiguration\n",
"from pyrit.scenario.scenarios.foundry import FoundryStrategy, RedTeamAgent\n",
"from pyrit.scenario.foundry import FoundryStrategy, RedTeamAgent\n",
"from pyrit.setup import initialize_from_config_async\n",
"\n",
"await initialize_from_config_async(config_path=Path(\"pyrit_conf.yaml\")) # type: ignore\n",
Expand Down Expand Up @@ -216,7 +216,7 @@
"Each converter in the composite is applied in sequence before the attack runs.\n",
"\n",
"```python\n",
"from pyrit.scenario.scenarios.foundry import FoundryComposite\n",
"from pyrit.scenario.foundry import FoundryComposite\n",
"\n",
"composed = FoundryComposite(attack=FoundryStrategy.Crescendo, converters=[FoundryStrategy.Caesar, FoundryStrategy.CharSwap])\n",
"```"
Expand All @@ -229,7 +229,7 @@
"metadata": {},
"outputs": [],
"source": [
"# from pyrit.scenario.scenarios.foundry import FoundryComposite\n",
"# from pyrit.scenario.foundry import FoundryComposite\n",
"# composed = FoundryComposite(attack=FoundryStrategy.Crescendo, converters=[FoundryStrategy.Caesar, FoundryStrategy.CharSwap])\n",
"# scenario_strategies = [FoundryStrategy.Base64, composed]"
]
Expand Down
6 changes: 3 additions & 3 deletions doc/scanner/foundry.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from pyrit.output import output_scenario_async
from pyrit.registry import TargetRegistry
from pyrit.scenario import DatasetConfiguration
from pyrit.scenario.scenarios.foundry import FoundryStrategy, RedTeamAgent
from pyrit.scenario.foundry import FoundryStrategy, RedTeamAgent
from pyrit.setup import initialize_from_config_async

await initialize_from_config_async(config_path=Path("pyrit_conf.yaml")) # type: ignore
Expand Down Expand Up @@ -77,13 +77,13 @@
# Each converter in the composite is applied in sequence before the attack runs.
#
# ```python
# from pyrit.scenario.scenarios.foundry import FoundryComposite
# from pyrit.scenario.foundry import FoundryComposite
#
# composed = FoundryComposite(attack=FoundryStrategy.Crescendo, converters=[FoundryStrategy.Caesar, FoundryStrategy.CharSwap])
# ```

# %%
# from pyrit.scenario.scenarios.foundry import FoundryComposite
# from pyrit.scenario.foundry import FoundryComposite
# composed = FoundryComposite(attack=FoundryStrategy.Crescendo, converters=[FoundryStrategy.Caesar, FoundryStrategy.CharSwap])
# scenario_strategies = [FoundryStrategy.Base64, composed]

Expand Down
4 changes: 2 additions & 2 deletions doc/scanner/garak.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
"\n",
"from pyrit.output import output_scenario_async\n",
"from pyrit.registry import TargetRegistry\n",
"from pyrit.scenario.scenarios.garak import Encoding, EncodingStrategy\n",
"from pyrit.scenario.scenarios.garak.encoding import EncodingDatasetConfiguration\n",
"from pyrit.scenario.garak import Encoding, EncodingStrategy\n",
"from pyrit.scenario.garak.encoding import EncodingDatasetConfiguration\n",
"from pyrit.setup import initialize_from_config_async\n",
"\n",
"await initialize_from_config_async(config_path=Path(\"pyrit_conf.yaml\")) # type: ignore\n",
Expand Down
4 changes: 2 additions & 2 deletions doc/scanner/garak.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@

from pyrit.output import output_scenario_async
from pyrit.registry import TargetRegistry
from pyrit.scenario.scenarios.garak import Encoding, EncodingStrategy
from pyrit.scenario.scenarios.garak.encoding import EncodingDatasetConfiguration
from pyrit.scenario.garak import Encoding, EncodingStrategy
from pyrit.scenario.garak.encoding import EncodingDatasetConfiguration
from pyrit.setup import initialize_from_config_async

await initialize_from_config_async(config_path=Path("pyrit_conf.yaml")) # type: ignore
Expand Down
12 changes: 0 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ dev = [
"jupytext>=1.17.1",
"matplotlib>=3.10.0",
"ty>=0.0.32",
"mock-alchemy>=0.2.6",
"pandas>=2.2.0",
"pre-commit>=4.2.0",
"pytest>=9.0.3",
Expand All @@ -94,17 +93,8 @@ dev = [
"respx>=0.22.0",
"ruff>=0.14.4",
"types-aiofiles>=24.1.0",
"types-cachetools>=5.5.0",
"types-decorator>=5.1.0",
"types-paramiko>=3.5.0",
"types-pycurl>=7.45.0",
"types-pytz>=2024.2.0",
"types-PyYAML>=6.0.12.20250516",
"types-requests>=2.31.0.20250515",
"types-simplejson>=3.19.0",
"types-six>=1.16.0",
"types-tabulate>=0.9.0",
"types-ujson>=5.10.0",
]

[project.optional-dependencies]
Expand Down Expand Up @@ -170,8 +160,6 @@ asyncio_mode = "auto"
[tool.ty]
[tool.ty.rules]
all = "error"
# Suppress errors for missing third-party stubs
unresolved-import = "ignore"
# Tolerate None attribute access
possibly-missing-attribute = "ignore"
# Allow existing type: ignore comments without matching errors
Expand Down
4 changes: 2 additions & 2 deletions pyrit/auth/copilot_authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ async def _fetch_access_token_with_playwright_async(self) -> Optional[str]:
import sys

try:
from playwright.async_api import async_playwright # noqa: F401
from playwright.async_api import async_playwright # type: ignore[ty:unresolved-import] # noqa: F401
except ImportError:
raise RuntimeError(
"Playwright is not installed. Please install it with: "
Expand Down Expand Up @@ -372,7 +372,7 @@ async def _run_playwright_browser_automation_async(self) -> Optional[str]:
Raises:
ValueError: If the username is not set.
"""
from playwright.async_api import async_playwright
from playwright.async_api import async_playwright # type: ignore[ty:unresolved-import]

bearer_token = None
token_expires_in = None
Expand Down
2 changes: 1 addition & 1 deletion pyrit/executor/benchmark/fairness_bias.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ def _extract_name(self, response: str) -> Optional[str]:
"""
# Try spaCy-based extraction first (more robust)
try:
import spacy
import spacy # type: ignore[ty:unresolved-import]

self._nlp = spacy.load("en_core_web_sm")
except Exception:
Expand Down
2 changes: 1 addition & 1 deletion pyrit/models/results/strategy_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pydantic import BaseModel, ConfigDict

if TYPE_CHECKING:
from typing import Self
from typing_extensions import Self

StrategyResultT = TypeVar("StrategyResultT", bound="StrategyResult")

Expand Down
4 changes: 2 additions & 2 deletions pyrit/prompt_converter/add_image_to_video_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ async def _add_image_to_video_async(self, image_path: str, output_path: str) ->
ValueError: If the image path is invalid or unsupported video format.
"""
try:
import cv2 # noqa: F401
import cv2 # type: ignore[ty:unresolved-import] # noqa: F401
except ModuleNotFoundError as e:
logger.error("Could not import opencv. You may need to install it via 'pip install pyrit[opencv]'")
raise e
Expand Down Expand Up @@ -173,7 +173,7 @@ def _add_image_to_video_sync(
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
file_extension = video_path.split(".")[-1].lower()
if file_extension in video_encoding_map:
video_char_code = cv2.VideoWriter_fourcc(*video_encoding_map[file_extension])
video_char_code = cv2.VideoWriter.fourcc(*video_encoding_map[file_extension])
output_video = cv2.VideoWriter(output_path, video_char_code, fps, (width, height))
else:
raise ValueError(f"Unsupported video format: {file_extension}")
Expand Down
Loading
Loading