Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion doc/code/converters/0_converters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@
"\n",
"# Display all rows\n",
"pd.set_option(\"display.max_rows\", None)\n",
"df"
"print(df)"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion doc/code/converters/0_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

# Display all rows
pd.set_option("display.max_rows", None)
df
print(df)

# %% [markdown]
# ## Converter Categories
Expand Down
2 changes: 1 addition & 1 deletion doc/code/converters/4_video_converters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"\n",
"video = AddImageVideoConverter(video_path=input_video)\n",
"converted_vid = await video.convert_async(prompt=input_image, input_type=\"image_path\") # type: ignore\n",
"converted_vid"
"print(converted_vid)"
]
}
],
Expand Down
2 changes: 1 addition & 1 deletion doc/code/converters/4_video_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@

video = AddImageVideoConverter(video_path=input_video)
converted_vid = await video.convert_async(prompt=input_image, input_type="image_path") # type: ignore
converted_vid
print(converted_vid)
4 changes: 2 additions & 2 deletions doc/code/memory/embeddings.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
"from pyrit.common.path import DB_DATA_PATH\n",
"\n",
"saved_embedding_path = embedding_response.save_to_file(directory_path=DB_DATA_PATH)\n",
"saved_embedding_path"
"print(saved_embedding_path)"
]
},
{
Expand Down Expand Up @@ -143,7 +143,7 @@
"from pyrit.common.path import DB_DATA_PATH\n",
"\n",
"saved_embedding_path = embedding_response.save_to_file(directory_path=DB_DATA_PATH)\n",
"saved_embedding_path"
"print(saved_embedding_path)"
]
}
],
Expand Down
4 changes: 2 additions & 2 deletions doc/code/memory/embeddings.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from pyrit.common.path import DB_DATA_PATH

saved_embedding_path = embedding_response.save_to_file(directory_path=DB_DATA_PATH)
saved_embedding_path
print(saved_embedding_path)

# %% [markdown]
# To load an embedding from disk
Expand All @@ -59,4 +59,4 @@
from pyrit.common.path import DB_DATA_PATH

saved_embedding_path = embedding_response.save_to_file(directory_path=DB_DATA_PATH)
saved_embedding_path
print(saved_embedding_path)
4 changes: 2 additions & 2 deletions doc/code/scoring/8_scorer_metrics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@
"\n",
"print(\"Top 5 configurations by F1 Score:\")\n",
"print(\"-\" * 80)\n",
"for i, entry in enumerate(sorted_by_f1[:5], 1):\n",
"for _i, entry in enumerate(sorted_by_f1[:5], 1):\n",
" printer = ConsoleScorerPrinter()\n",
" printer.print_objective_scorer(scorer_identifier=entry.scorer_identifier)\n",
"\n",
Expand Down Expand Up @@ -468,7 +468,7 @@
"\n",
"print(\"Top configurations by Mean Absolute Error:\")\n",
"print(\"-\" * 80)\n",
"for i, e in enumerate(sorted_by_mae[:5], 1):\n",
"for _i, e in enumerate(sorted_by_mae[:5], 1):\n",
" printer = ConsoleScorerPrinter()\n",
" printer.print_harm_scorer(scorer_identifier=e.scorer_identifier, harm_category=\"violence\")"
]
Expand Down
4 changes: 2 additions & 2 deletions doc/code/scoring/8_scorer_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@

print("Top 5 configurations by F1 Score:")
print("-" * 80)
for i, entry in enumerate(sorted_by_f1[:5], 1):
for _i, entry in enumerate(sorted_by_f1[:5], 1):
printer = ConsoleScorerPrinter()
printer.print_objective_scorer(scorer_identifier=entry.scorer_identifier)

Expand Down Expand Up @@ -227,7 +227,7 @@

print("Top configurations by Mean Absolute Error:")
print("-" * 80)
for i, e in enumerate(sorted_by_mae[:5], 1):
for _i, e in enumerate(sorted_by_mae[:5], 1):
printer = ConsoleScorerPrinter()
printer.print_harm_scorer(scorer_identifier=e.scorer_identifier, harm_category="violence")

Expand Down
4 changes: 2 additions & 2 deletions doc/cookbooks/4_testing_bias.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@
"for job, gender in itertools.product(jobs[:3], genders): # for demonstration purposes we just use first 3 jobs\n",
" question = f\"Is the {job} job better performed by {gender}?\"\n",
" answers[question] = []\n",
" for i in range(10):\n",
" for _i in range(10):\n",
" result = await attack.execute_async(objective=question, prepended_conversation=[system_prompt]) # type: ignore\n",
" answer = memory.get_conversation(conversation_id=result.conversation_id)[2].get_value()\n",
" if answer.startswith(\"YES\"):\n",
Expand Down Expand Up @@ -626,7 +626,7 @@
" ],\n",
" )\n",
" answers[question] = []\n",
" for i in range(5):\n",
" for _i in range(5):\n",
" result = await benchmark.execute_async(question_answering_entry=question_answering_entry) # type: ignore\n",
" answers[question].append(str(result.outcome))\n",
"\n",
Expand Down
4 changes: 2 additions & 2 deletions doc/cookbooks/4_testing_bias.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
for job, gender in itertools.product(jobs[:3], genders): # for demonstration purposes we just use first 3 jobs
question = f"Is the {job} job better performed by {gender}?"
answers[question] = []
for i in range(10):
for _i in range(10):
result = await attack.execute_async(objective=question, prepended_conversation=[system_prompt]) # type: ignore
answer = memory.get_conversation(conversation_id=result.conversation_id)[2].get_value()
if answer.startswith("YES"):
Expand Down Expand Up @@ -172,7 +172,7 @@
],
)
answers[question] = []
for i in range(5):
for _i in range(5):
result = await benchmark.execute_async(question_answering_entry=question_answering_entry) # type: ignore
answers[question].append(str(result.outcome))

Expand Down
2 changes: 1 addition & 1 deletion doc/generate_docs/pct_to_ipynb.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def find_files(directory, file_extension):
dir_path = os.path.join(directory, included_dir)
if not os.path.exists(dir_path):
continue
for root, dirs, files in os.walk(dir_path):
for root, _dirs, files in os.walk(dir_path):
for file in files:
if file.endswith("_helpers.py"):
continue
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ fixable = [
"YTT",
]
select = [
"B", # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
"C4", # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4
"CPY001", # missing-copyright-notice
"D", # https://docs.astral.sh/ruff/rules/#pydocstyle-d
Expand All @@ -265,6 +266,7 @@ select = [
"W", # https://docs.astral.sh/ruff/rules/#pycodestyle-w
]
ignore = [
"B903", # class-as-data-structure (test helper classes use @apply_defaults pattern)
"D100", # Missing docstring in public module
"D200", # One-line docstring should fit on one line
"D205", # 1 blank line required between summary line and description
Expand Down Expand Up @@ -297,10 +299,12 @@ notice-rgx = "Copyright \\(c\\) Microsoft Corporation\\.\\s*\\n.*Licensed under
# Temporary ignores for pyrit/ subdirectories until issue #1176
# https://github.com/Azure/PyRIT/issues/1176 is fully resolved
# TODO: Remove these ignores once the issues are fixed
"pyrit/{auxiliary_attacks,ui}/**/*.py" = ["D101", "D102", "D103", "D104", "D105", "D106", "D107", "D401", "D404", "D417", "D418", "DOC102", "DOC201", "DOC202", "DOC402", "DOC501", "SIM101", "SIM108"]
"pyrit/{auxiliary_attacks,ui}/**/*.py" = ["B905", "D101", "D102", "D103", "D104", "D105", "D106", "D107", "D401", "D404", "D417", "D418", "DOC102", "DOC201", "DOC202", "DOC402", "DOC501", "SIM101", "SIM108"]
# Backend API routes raise HTTPException handled by FastAPI, not true exceptions
"pyrit/backend/**/*.py" = ["DOC501"]
"pyrit/backend/**/*.py" = ["DOC501", "B008"]
"pyrit/__init__.py" = ["D104"]
# Allow broad pytest.raises(Exception) in tests
"tests/**/*.py" = ["B017"]

[tool.ruff.lint.pydocstyle]
convention = "google"
2 changes: 1 addition & 1 deletion pyrit/auth/copilot_authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ async def _fetch_access_token_with_playwright(self) -> Optional[str]:
raise RuntimeError(
"Playwright is not installed. Please install it with: "
"'pip install playwright && playwright install chromium'"
)
) from None

# On Windows, when using SelectorEventLoop (common in Jupyter), we need to run
# Playwright in a separate thread with ProactorEventLoop to support subprocesses
Expand Down
2 changes: 1 addition & 1 deletion pyrit/auth/manual_copilot_authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def __init__(self, *, access_token: Optional[str] = None) -> None:
resolved_token, algorithms=["RS256"], options={"verify_signature": False}
)
except jwt.exceptions.DecodeError as e:
raise ValueError(f"Failed to decode access_token as JWT: {e}")
raise ValueError(f"Failed to decode access_token as JWT: {e}") from e

required_claims = ["tid", "oid"]
missing_claims = [claim for claim in required_claims if claim not in self._claims]
Expand Down
8 changes: 4 additions & 4 deletions pyrit/backend/routes/attacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ async def create_attack(request: CreateAttackRequest) -> CreateAttackResponse:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(e),
)
) from e


@router.get(
Expand Down Expand Up @@ -280,13 +280,13 @@ async def add_message(
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=error_msg,
)
) from e
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=error_msg,
)
) from e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to add message: {str(e)}",
)
) from e
8 changes: 4 additions & 4 deletions pyrit/backend/routes/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ async def create_converter(request: CreateConverterRequest) -> CreateConverterRe
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
) from e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to create converter: {str(e)}",
)
) from e


@router.get(
Expand Down Expand Up @@ -126,9 +126,9 @@ async def preview_conversion(request: ConverterPreviewRequest) -> ConverterPrevi
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
) from e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Converter preview failed: {str(e)}",
)
) from e
4 changes: 2 additions & 2 deletions pyrit/backend/routes/targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ async def create_target(request: CreateTargetRequest) -> TargetInstance:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
) from e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to create target: {str(e)}",
)
) from e


@router.get(
Expand Down
2 changes: 1 addition & 1 deletion pyrit/common/notebook_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def is_in_ipython_session() -> bool:
bool: True if the code is running in an IPython session, False otherwise.
"""
try:
__IPYTHON__ # type: ignore
__IPYTHON__ # type: ignore # noqa: B018
return True
except NameError:
return False
6 changes: 3 additions & 3 deletions pyrit/common/yaml_loadable.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
T = TypeVar("T", bound="YamlLoadable")


class YamlLoadable(abc.ABC):
class YamlLoadable(abc.ABC): # noqa: B024
"""
Abstract base class for objects that can be loaded from YAML files.
"""
Expand All @@ -36,10 +36,10 @@ def from_yaml_file(cls: type[T], file: Union[Path | str]) -> T:
try:
yaml_data = yaml.safe_load(file.read_text("utf-8"))
except yaml.YAMLError as exc:
raise ValueError(f"Invalid YAML file '{file}': {exc}")
raise ValueError(f"Invalid YAML file '{file}': {exc}") from exc

# If this class provides a from_dict factory, use it;
# otherwise, just instantiate directly with **yaml_data
if hasattr(cls, "from_dict") and callable(getattr(cls, "from_dict")):
if hasattr(cls, "from_dict") and callable(getattr(cls, "from_dict")): # noqa: B009
return cls.from_dict(yaml_data) # type: ignore
return cls(**yaml_data)
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ async def fetch_dataset(self, *, cache: bool = True) -> SeedDataset:

except Exception as e:
logger.error(f"Failed to load JBB-Behaviors dataset: {str(e)}")
raise Exception(f"Error loading JBB-Behaviors dataset: {str(e)}")
raise Exception(f"Error loading JBB-Behaviors dataset: {str(e)}") from e

def _map_jbb_category_to_harm_category(self, jbb_category: str) -> list[str]:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(
source: str = "https://raw.githubusercontent.com/apple/ml-vlsu/main/data/VLSU.csv",
source_type: Literal["public_url", "file"] = "public_url",
categories: Optional[list[VLSUCategory]] = None,
unsafe_grades: Optional[list[str]] = ["unsafe", "borderline"],
unsafe_grades: Optional[list[str]] = None,
max_examples: Optional[int] = None,
):
"""
Expand All @@ -77,6 +77,8 @@ def __init__(
Raises:
ValueError: If any of the specified categories are invalid.
"""
if unsafe_grades is None:
unsafe_grades = ["unsafe", "borderline"]
self.source = source
self.source_type: Literal["public_url", "file"] = source_type
self.categories = categories
Expand Down
2 changes: 1 addition & 1 deletion pyrit/datasets/seed_datasets/seed_dataset_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def get_all_dataset_names(cls) -> list[str]:
provider = provider_class()
dataset_names.add(provider.dataset_name)
except Exception as e:
raise ValueError(f"Could not get dataset name from {provider_class.__name__}: {e}")
raise ValueError(f"Could not get dataset name from {provider_class.__name__}: {e}") from e
return sorted(dataset_names)

@classmethod
Expand Down
2 changes: 1 addition & 1 deletion pyrit/executor/attack/core/attack_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def _process_execution_results(
completed: list[AttackStrategyResultT] = []
incomplete: list[tuple[str, BaseException]] = []

for objective, result in zip(objectives, results_or_exceptions):
for objective, result in zip(objectives, results_or_exceptions, strict=False):
if isinstance(result, BaseException):
incomplete.append((objective, result))
else:
Expand Down
8 changes: 6 additions & 2 deletions pyrit/executor/attack/multi_turn/tree_of_attacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1133,13 +1133,17 @@ def _parse_red_teaming_response(self, red_teaming_response: str) -> str:
red_teaming_response_dict = json.loads(red_teaming_response)
except json.JSONDecodeError:
logger.error(f"The response from the red teaming chat is not in JSON format: {red_teaming_response}")
raise InvalidJsonException(message="The response from the red teaming chat is not in JSON format.")
raise InvalidJsonException(
message="The response from the red teaming chat is not in JSON format."
) from None

try:
return cast(str, red_teaming_response_dict["prompt"])
except KeyError:
logger.error(f"The response from the red teaming chat does not contain a prompt: {red_teaming_response}")
raise InvalidJsonException(message="The response from the red teaming chat does not contain a prompt.")
raise InvalidJsonException(
message="The response from the red teaming chat does not contain a prompt."
) from None

def __str__(self) -> str:
"""
Expand Down
2 changes: 1 addition & 1 deletion pyrit/executor/attack/single_turn/context_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def _load_context_description_instructions(self, *, instructions_path: Path) ->
try:
context_description_instructions = SeedDataset.from_yaml_file(instructions_path)
except Exception as e:
raise ValueError(f"Failed to load context description instructions from {instructions_path}: {e}")
raise ValueError(f"Failed to load context description instructions from {instructions_path}: {e}") from e

if len(context_description_instructions.prompts) < 3:
raise ValueError(
Expand Down
2 changes: 1 addition & 1 deletion pyrit/executor/core/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@


@dataclass
class StrategyContext(ABC):
class StrategyContext(ABC): # noqa: B024
"""Base class for all strategy contexts."""

def duplicate(self: StrategyContextT) -> StrategyContextT:
Expand Down
2 changes: 1 addition & 1 deletion pyrit/executor/promptgen/fuzzer/fuzzer_converter_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ async def send_prompt_async(self, request: Message) -> str:
return str(parsed_response["output"])

except json.JSONDecodeError:
raise InvalidJsonException(message=f"Invalid JSON encountered: {response_msg}")
raise InvalidJsonException(message=f"Invalid JSON encountered: {response_msg}") from None

def input_supported(self, input_type: PromptDataType) -> bool:
"""
Expand Down
6 changes: 4 additions & 2 deletions pyrit/memory/memory_embedding.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,7 @@ def default_memory_embedding_factory(embedding_model: Optional[EmbeddingSupport]
try:
model = OpenAITextEmbedding()
return MemoryEmbedding(embedding_model=model)
except ValueError:
raise ValueError("No embedding model was provided and no OpenAI embedding model was found in the environment.")
except ValueError as e:
raise ValueError(
"No embedding model was provided and no OpenAI embedding model was found in the environment."
) from e
Loading