From 4a2803e5a16151ae73e5551a687dcd1b110d7369 Mon Sep 17 00:00:00 2001 From: Roman Lutz Date: Mon, 23 Feb 2026 10:15:47 -0800 Subject: [PATCH 1/2] Enable ruff PIE (flake8-pie) rules and fix all violations - Remove unnecessary pass statements in non-empty class/function bodies (PIE790) - Remove unnecessary range(0, ...) start arguments (PIE808) - Replace reimplemented container builtins with builtin calls (PIE807) - Remove duplicate class field definitions (PIE794) - Merge multiple startswith/endswith calls (PIE810) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build_scripts/check_links.py | 3 +-- pyproject.toml | 1 + pyrit/datasets/executors/question_answer/wmdp_dataset.py | 4 ++-- pyrit/datasets/seed_datasets/seed_dataset_provider.py | 2 -- pyrit/executor/attack/multi_turn/chunked_request.py | 1 - pyrit/executor/attack/multi_turn/crescendo.py | 1 - pyrit/executor/attack/multi_turn/multi_prompt_sending.py | 1 - pyrit/executor/attack/multi_turn/red_teaming.py | 1 - pyrit/executor/attack/multi_turn/tree_of_attacks.py | 1 - pyrit/executor/attack/printer/attack_result_printer.py | 3 --- pyrit/executor/attack/single_turn/prompt_sending.py | 1 - pyrit/executor/benchmark/question_answering.py | 1 - pyrit/executor/core/strategy.py | 5 ----- pyrit/executor/promptgen/anecdoctor.py | 1 - pyrit/executor/promptgen/fuzzer/fuzzer.py | 1 - pyrit/executor/promptgen/fuzzer/fuzzer_converter_base.py | 1 - pyrit/executor/workflow/core/workflow_strategy.py | 4 ---- pyrit/executor/workflow/xpia.py | 1 - pyrit/memory/memory_models.py | 2 -- pyrit/models/data_type_serializer.py | 2 +- pyrit/models/seeds/seed.py | 8 ++++---- pyrit/models/seeds/seed_prompt.py | 2 +- pyrit/prompt_converter/add_image_to_video_converter.py | 2 +- pyrit/prompt_converter/base2048_converter.py | 1 - pyrit/prompt_converter/base64_converter.py | 3 --- pyrit/prompt_converter/ecoji_converter.py | 1 - pyrit/prompt_converter/insert_punctuation_converter.py | 4 ++-- pyrit/prompt_converter/text_selection_strategy.py | 2 -- pyrit/prompt_converter/word_level_converter.py | 2 -- pyrit/prompt_target/common/prompt_chat_target.py | 1 - pyrit/prompt_target/openai/openai_target.py | 4 ---- pyrit/prompt_target/text_target.py | 1 - pyrit/registry/class_registries/base_class_registry.py | 2 -- pyrit/scenario/core/scenario.py | 4 ---- pyrit/scenario/printer/scenario_result_printer.py | 1 - pyrit/scenario/scenarios/airt/jailbreak.py | 2 +- pyrit/score/conversation_scorer.py | 1 - pyrit/score/printer/scorer_printer.py | 2 -- pyrit/score/scorer_evaluation/scorer_evaluator.py | 2 -- pyrit/setup/initializers/pyrit_initializer.py | 3 --- 40 files changed, 14 insertions(+), 71 deletions(-) diff --git a/build_scripts/check_links.py b/build_scripts/check_links.py index 342d57df1a..1a5fafab53 100644 --- a/build_scripts/check_links.py +++ b/build_scripts/check_links.py @@ -89,8 +89,7 @@ def check_url(url, retries=2, delay=2): or any(url.endswith(reference) for reference in custom_myst_references) or os.path.isfile(url) or os.path.isdir(url) - or url.startswith("mailto:") - or url.startswith("attachment:") + or url.startswith(("mailto:", "attachment:")) ): return url, True diff --git a/pyproject.toml b/pyproject.toml index f7fad04abe..f7d470f6c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -251,6 +251,7 @@ select = [ "DOC", # https://docs.astral.sh/ruff/rules/#pydoclint-doc "F401", # unused-import "I", # isort + "PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie ] ignore = [ "D100", # Missing docstring in public module diff --git a/pyrit/datasets/executors/question_answer/wmdp_dataset.py b/pyrit/datasets/executors/question_answer/wmdp_dataset.py index ed91730989..83df807429 100644 --- a/pyrit/datasets/executors/question_answer/wmdp_dataset.py +++ b/pyrit/datasets/executors/question_answer/wmdp_dataset.py @@ -42,10 +42,10 @@ def fetch_wmdp_dataset(category: Optional[str] = None) -> QuestionAnsweringDatas questions_answers = [] for name in data_categories: ds = load_dataset("cais/wmdp", name) - for i in range(0, len(ds["test"])): + for i in range(len(ds["test"])): # For each question, save the 4 possible choices and their respective index choices = [] - for j in range(0, 4): + for j in range(4): c = QuestionChoice(index=j, text=ds["test"]["choices"][i][j]) choices.append(c) diff --git a/pyrit/datasets/seed_datasets/seed_dataset_provider.py b/pyrit/datasets/seed_datasets/seed_dataset_provider.py index 80626d4620..9e7d7aa1d5 100644 --- a/pyrit/datasets/seed_datasets/seed_dataset_provider.py +++ b/pyrit/datasets/seed_datasets/seed_dataset_provider.py @@ -50,7 +50,6 @@ def dataset_name(self) -> str: Returns: str: The dataset name (e.g., "HarmBench", "JailbreakBench JBB-Behaviors") """ - pass @abstractmethod async def fetch_dataset(self, *, cache: bool = True) -> SeedDataset: @@ -67,7 +66,6 @@ async def fetch_dataset(self, *, cache: bool = True) -> SeedDataset: Raises: Exception: If the dataset cannot be fetched or processed. """ - pass @classmethod def get_all_providers(cls) -> Dict[str, Type["SeedDatasetProvider"]]: diff --git a/pyrit/executor/attack/multi_turn/chunked_request.py b/pyrit/executor/attack/multi_turn/chunked_request.py index feabb98215..825681c6a9 100644 --- a/pyrit/executor/attack/multi_turn/chunked_request.py +++ b/pyrit/executor/attack/multi_turn/chunked_request.py @@ -375,4 +375,3 @@ async def _teardown_async(self, *, context: ChunkedRequestAttackContext) -> None Args: context (ChunkedRequestAttackContext): The attack context containing conversation session. """ - pass diff --git a/pyrit/executor/attack/multi_turn/crescendo.py b/pyrit/executor/attack/multi_turn/crescendo.py index b005e21218..1d7434ef50 100644 --- a/pyrit/executor/attack/multi_turn/crescendo.py +++ b/pyrit/executor/attack/multi_turn/crescendo.py @@ -402,7 +402,6 @@ async def _teardown_async(self, *, context: CrescendoAttackContext) -> None: context (CrescendoAttackContext): The attack context. """ # Nothing to be done here, no-op - pass @pyrit_json_retry async def _get_attack_prompt_async( diff --git a/pyrit/executor/attack/multi_turn/multi_prompt_sending.py b/pyrit/executor/attack/multi_turn/multi_prompt_sending.py index 51d9f2f80b..b379949b96 100644 --- a/pyrit/executor/attack/multi_turn/multi_prompt_sending.py +++ b/pyrit/executor/attack/multi_turn/multi_prompt_sending.py @@ -328,7 +328,6 @@ def _determine_attack_outcome( async def _teardown_async(self, *, context: MultiTurnAttackContext[Any]) -> None: """Clean up after attack execution.""" # Nothing to be done here, no-op - pass async def _send_prompt_to_objective_target_async( self, *, current_message: Message, context: MultiTurnAttackContext[Any] diff --git a/pyrit/executor/attack/multi_turn/red_teaming.py b/pyrit/executor/attack/multi_turn/red_teaming.py index 33b2c75d75..0353561766 100644 --- a/pyrit/executor/attack/multi_turn/red_teaming.py +++ b/pyrit/executor/attack/multi_turn/red_teaming.py @@ -323,7 +323,6 @@ async def _perform_async(self, *, context: MultiTurnAttackContext[Any]) -> Attac async def _teardown_async(self, *, context: MultiTurnAttackContext[Any]) -> None: """Clean up after attack execution.""" # Nothing to be done here, no-op - pass async def _generate_next_prompt_async(self, context: MultiTurnAttackContext[Any]) -> Message: """ diff --git a/pyrit/executor/attack/multi_turn/tree_of_attacks.py b/pyrit/executor/attack/multi_turn/tree_of_attacks.py index 48d6025a47..b44a98502f 100644 --- a/pyrit/executor/attack/multi_turn/tree_of_attacks.py +++ b/pyrit/executor/attack/multi_turn/tree_of_attacks.py @@ -1546,7 +1546,6 @@ async def _teardown_async(self, *, context: TAPAttackContext) -> None: state after execution. """ # No specific teardown needed for TAP attack - pass async def _prepare_nodes_for_iteration_async(self, context: TAPAttackContext) -> None: """ diff --git a/pyrit/executor/attack/printer/attack_result_printer.py b/pyrit/executor/attack/printer/attack_result_printer.py index 189f931af3..1c180ba2d0 100644 --- a/pyrit/executor/attack/printer/attack_result_printer.py +++ b/pyrit/executor/attack/printer/attack_result_printer.py @@ -37,7 +37,6 @@ async def print_result_async( conversation (the red teaming LLM's reasoning). Only shown for successful attacks to avoid overwhelming output. Defaults to False. """ - pass @abstractmethod async def print_conversation_async(self, result: AttackResult, *, include_scores: bool = False) -> None: @@ -49,7 +48,6 @@ async def print_conversation_async(self, result: AttackResult, *, include_scores include_scores (bool): Whether to include scores in the output. Defaults to False. """ - pass @abstractmethod async def print_summary_async(self, result: AttackResult) -> None: @@ -59,7 +57,6 @@ async def print_summary_async(self, result: AttackResult) -> None: Args: result (AttackResult): The attack result to summarize """ - pass @staticmethod def _get_outcome_icon(outcome: AttackOutcome) -> str: diff --git a/pyrit/executor/attack/single_turn/prompt_sending.py b/pyrit/executor/attack/single_turn/prompt_sending.py index 3b3f623506..1a9449f66f 100644 --- a/pyrit/executor/attack/single_turn/prompt_sending.py +++ b/pyrit/executor/attack/single_turn/prompt_sending.py @@ -276,7 +276,6 @@ def _determine_attack_outcome( async def _teardown_async(self, *, context: SingleTurnAttackContext[Any]) -> None: """Clean up after attack execution.""" # Nothing to be done here, no-op - pass def _get_message(self, context: SingleTurnAttackContext[Any]) -> Message: """ diff --git a/pyrit/executor/benchmark/question_answering.py b/pyrit/executor/benchmark/question_answering.py index d2a244a381..456353e81d 100644 --- a/pyrit/executor/benchmark/question_answering.py +++ b/pyrit/executor/benchmark/question_answering.py @@ -253,7 +253,6 @@ async def _teardown_async(self, *, context: QuestionAnsweringBenchmarkContext) - Args: context (QuestionAnsweringBenchmarkContext): The context for the strategy. """ - pass @overload async def execute_async( diff --git a/pyrit/executor/core/strategy.py b/pyrit/executor/core/strategy.py index 1ef0f94cff..b78f00bb67 100644 --- a/pyrit/executor/core/strategy.py +++ b/pyrit/executor/core/strategy.py @@ -96,7 +96,6 @@ async def on_event(self, event_data: StrategyEventData[StrategyContextT, Strateg Args: event_data: Data about the event that occurred. """ - pass class StrategyLogAdapter(logging.LoggerAdapter): # type: ignore[type-arg] @@ -198,7 +197,6 @@ def _validate_context(self, *, context: StrategyContextT) -> None: Raises: Exception: If the context is invalid for this strategy. """ - pass @abstractmethod async def _setup_async(self, *, context: StrategyContextT) -> None: @@ -210,7 +208,6 @@ async def _setup_async(self, *, context: StrategyContextT) -> None: Args: context (StrategyContextT): The context for the strategy. """ - pass @abstractmethod async def _perform_async(self, *, context: StrategyContextT) -> StrategyResultT: @@ -224,7 +221,6 @@ async def _perform_async(self, *, context: StrategyContextT) -> StrategyResultT: Returns: StrategyResultT: The result of the strategy execution. """ - pass @abstractmethod async def _teardown_async(self, *, context: StrategyContextT) -> None: @@ -236,7 +232,6 @@ async def _teardown_async(self, *, context: StrategyContextT) -> None: Args: context (StrategyContextT): The context for the strategy. """ - pass async def _handle_event( self, diff --git a/pyrit/executor/promptgen/anecdoctor.py b/pyrit/executor/promptgen/anecdoctor.py index 82ecb25e5f..7cb87f6341 100644 --- a/pyrit/executor/promptgen/anecdoctor.py +++ b/pyrit/executor/promptgen/anecdoctor.py @@ -237,7 +237,6 @@ async def _teardown_async(self, *, context: AnecdoctorContext) -> None: context (AnecdoctorContext): The generation context. """ # Nothing to clean up for this prompt generation - pass async def _prepare_examples_async(self, *, context: AnecdoctorContext) -> str: """ diff --git a/pyrit/executor/promptgen/fuzzer/fuzzer.py b/pyrit/executor/promptgen/fuzzer/fuzzer.py index 93360a16dd..bf8164a49b 100644 --- a/pyrit/executor/promptgen/fuzzer/fuzzer.py +++ b/pyrit/executor/promptgen/fuzzer/fuzzer.py @@ -844,7 +844,6 @@ async def _teardown_async(self, *, context: FuzzerContext) -> None: context (FuzzerContext): The generation context. """ # No specific teardown needed - pass def _should_stop_generation(self, context: FuzzerContext) -> bool: """ diff --git a/pyrit/executor/promptgen/fuzzer/fuzzer_converter_base.py b/pyrit/executor/promptgen/fuzzer/fuzzer_converter_base.py index 140ffbb570..a0b759f7de 100644 --- a/pyrit/executor/promptgen/fuzzer/fuzzer_converter_base.py +++ b/pyrit/executor/promptgen/fuzzer/fuzzer_converter_base.py @@ -61,7 +61,6 @@ def __init__( def update(self, **kwargs: Any) -> None: """Update the converter with new parameters.""" - pass async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ diff --git a/pyrit/executor/workflow/core/workflow_strategy.py b/pyrit/executor/workflow/core/workflow_strategy.py index 1b6fab4d14..695d3b3aae 100644 --- a/pyrit/executor/workflow/core/workflow_strategy.py +++ b/pyrit/executor/workflow/core/workflow_strategy.py @@ -26,15 +26,11 @@ class WorkflowContext(StrategyContext, ABC): """Base class for all workflow contexts.""" - pass - @dataclass class WorkflowResult(StrategyResult, ABC): """Base class for all workflow results.""" - pass - class _DefaultWorkflowEventHandler(StrategyEventHandler[WorkflowContextT, WorkflowResultT]): """ diff --git a/pyrit/executor/workflow/xpia.py b/pyrit/executor/workflow/xpia.py index 2f5baf83f4..bb100fd9e2 100644 --- a/pyrit/executor/workflow/xpia.py +++ b/pyrit/executor/workflow/xpia.py @@ -400,7 +400,6 @@ async def _teardown_async(self, *, context: XPIAContext) -> None: context (XPIAContext): The context for the workflow. """ # No specific teardown operations required for base XPIA workflow - pass @overload async def execute_async( diff --git a/pyrit/memory/memory_models.py b/pyrit/memory/memory_models.py index 5ae66e4d6c..f0be05dc7b 100644 --- a/pyrit/memory/memory_models.py +++ b/pyrit/memory/memory_models.py @@ -116,8 +116,6 @@ class Base(DeclarativeBase): Base class for all database models. """ - pass - class PromptMemoryEntry(Base): """ diff --git a/pyrit/models/data_type_serializer.py b/pyrit/models/data_type_serializer.py index 4ace4e2bba..0bd659c85a 100644 --- a/pyrit/models/data_type_serializer.py +++ b/pyrit/models/data_type_serializer.py @@ -326,7 +326,7 @@ def __init__(self, *, category: str, prompt_text: str, extension: Optional[str] self.value = prompt_text self.data_sub_directory = f"/{category}/urls" self.file_extension = extension if extension else "txt" - self.on_disk = not (prompt_text.startswith("http://") or prompt_text.startswith("https://")) + self.on_disk = not (prompt_text.startswith(("http://", "https://"))) def data_on_disk(self) -> bool: return self.on_disk diff --git a/pyrit/models/seeds/seed.py b/pyrit/models/seeds/seed.py index f2a6bc4697..37934202a2 100644 --- a/pyrit/models/seeds/seed.py +++ b/pyrit/models/seeds/seed.py @@ -65,16 +65,16 @@ class Seed(YamlLoadable): dataset_name: Optional[str] = None # Categories of harm associated with this prompt - harm_categories: Optional[Sequence[str]] = field(default_factory=lambda: []) + harm_categories: Optional[Sequence[str]] = field(default_factory=list) # Description of the prompt description: Optional[str] = None # Authors of the prompt - authors: Optional[Sequence[str]] = field(default_factory=lambda: []) + authors: Optional[Sequence[str]] = field(default_factory=list) # Groups affiliated with the prompt - groups: Optional[Sequence[str]] = field(default_factory=lambda: []) + groups: Optional[Sequence[str]] = field(default_factory=list) # Source of the prompt source: Optional[str] = None @@ -86,7 +86,7 @@ class Seed(YamlLoadable): added_by: Optional[str] = None # Arbitrary metadata that can be attached to the prompt - metadata: Optional[Dict[str, Union[str, int]]] = field(default_factory=lambda: {}) + metadata: Optional[Dict[str, Union[str, int]]] = field(default_factory=dict) # Unique identifier for the prompt group prompt_group_id: Optional[uuid.UUID] = None diff --git a/pyrit/models/seeds/seed_prompt.py b/pyrit/models/seeds/seed_prompt.py index 8d478f7faa..879b44caa4 100644 --- a/pyrit/models/seeds/seed_prompt.py +++ b/pyrit/models/seeds/seed_prompt.py @@ -43,7 +43,7 @@ class SeedPrompt(Seed): sequence: int = 0 # Parameters that can be used in the prompt template - parameters: Optional[Sequence[str]] = field(default_factory=lambda: []) + parameters: Optional[Sequence[str]] = field(default_factory=list) def __post_init__(self) -> None: """Post-initialization to render the template to replace existing values.""" diff --git a/pyrit/prompt_converter/add_image_to_video_converter.py b/pyrit/prompt_converter/add_image_to_video_converter.py index dd71c51048..0f71abe0f6 100644 --- a/pyrit/prompt_converter/add_image_to_video_converter.py +++ b/pyrit/prompt_converter/add_image_to_video_converter.py @@ -162,7 +162,7 @@ async def _add_image_to_video(self, image_path: str, output_path: str) -> str: # Blend overlay with frame if overlay.shape[2] == 4: # Check number of channels on image alpha_overlay = overlay[:, :, 3] / 255.0 - for c in range(0, 3): + for c in range(3): frame[y : y + image_height, x : x + image_width, c] = ( alpha_overlay * overlay[:, :, c] + (1 - alpha_overlay) * frame[y : y + image_height, x : x + image_width, c] diff --git a/pyrit/prompt_converter/base2048_converter.py b/pyrit/prompt_converter/base2048_converter.py index 50e17d9a22..55805e7d1d 100644 --- a/pyrit/prompt_converter/base2048_converter.py +++ b/pyrit/prompt_converter/base2048_converter.py @@ -26,7 +26,6 @@ class Base2048Converter(PromptConverter): def __init__(self) -> None: """Initialize the Base2048Converter.""" - pass async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ diff --git a/pyrit/prompt_converter/base64_converter.py b/pyrit/prompt_converter/base64_converter.py index ccbb685fa1..8d45bd662a 100644 --- a/pyrit/prompt_converter/base64_converter.py +++ b/pyrit/prompt_converter/base64_converter.py @@ -22,9 +22,6 @@ class Base64Converter(PromptConverter): SUPPORTED_INPUT_TYPES = ("text",) SUPPORTED_OUTPUT_TYPES = ("text",) - SUPPORTED_INPUT_TYPES = ("text",) - SUPPORTED_OUTPUT_TYPES = ("text",) - EncodingFunc = Literal[ "b64encode", "urlsafe_b64encode", diff --git a/pyrit/prompt_converter/ecoji_converter.py b/pyrit/prompt_converter/ecoji_converter.py index 88cee5a6e9..b33ae9c469 100644 --- a/pyrit/prompt_converter/ecoji_converter.py +++ b/pyrit/prompt_converter/ecoji_converter.py @@ -25,7 +25,6 @@ class EcojiConverter(PromptConverter): def __init__(self) -> None: """Initialize the Ecoji converter.""" - pass async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ diff --git a/pyrit/prompt_converter/insert_punctuation_converter.py b/pyrit/prompt_converter/insert_punctuation_converter.py index 91d15378d1..5424596491 100644 --- a/pyrit/prompt_converter/insert_punctuation_converter.py +++ b/pyrit/prompt_converter/insert_punctuation_converter.py @@ -119,7 +119,7 @@ def _insert_punctuation(self, prompt: str, punctuation_list: List[str]) -> str: # Words list contains single spaces, single word without punctuations, single punctuations words = re.findall(r"\w+|[^\w\s]|\s", prompt) # Maintains indices for actual "words", i.e. letters and numbers not divided by punctuations - word_indices = [i for i in range(0, len(words)) if not re.match(r"\W", words[i])] + word_indices = [i for i in range(len(words)) if not re.match(r"\W", words[i])] # Calculate the number of insertions num_insertions = max( 1, round(len(word_indices) * self._word_swap_ratio) @@ -178,7 +178,7 @@ def _insert_within_words(self, prompt: str, num_insertions: int, punctuation_lis # Store random indices of prompt_list into insert_indices # If the prompt has only 0 or 1 chars, insert at the end of the prompt insert_indices = ( - [1] if len(prompt_list) <= num_insertions else random.sample(range(0, len(prompt_list) - 1), num_insertions) + [1] if len(prompt_list) <= num_insertions else random.sample(range(len(prompt_list) - 1), num_insertions) ) for index in insert_indices: diff --git a/pyrit/prompt_converter/text_selection_strategy.py b/pyrit/prompt_converter/text_selection_strategy.py index cb4583a59f..38eb9d0c95 100644 --- a/pyrit/prompt_converter/text_selection_strategy.py +++ b/pyrit/prompt_converter/text_selection_strategy.py @@ -25,7 +25,6 @@ def select_range(self, *, text: str) -> tuple[int, int]: tuple[int, int]: A tuple of (start_index, end_index) representing the character range. The range is inclusive of start_index and exclusive of end_index. """ - pass class TokenSelectionStrategy(TextSelectionStrategy): @@ -86,7 +85,6 @@ def select_words(self, *, words: List[str]) -> List[int]: Returns: List[int]: A list of indices representing which words should be converted. """ - pass def select_range(self, *, text: str, word_separator: str = " ") -> tuple[int, int]: """ diff --git a/pyrit/prompt_converter/word_level_converter.py b/pyrit/prompt_converter/word_level_converter.py index 96ed8a0661..44cd9b11cf 100644 --- a/pyrit/prompt_converter/word_level_converter.py +++ b/pyrit/prompt_converter/word_level_converter.py @@ -72,11 +72,9 @@ async def convert_word_async(self, word: str) -> str: Returns: str: The converted word. """ - pass def validate_input(self, prompt: str) -> None: """Validate the input before processing (can be overridden by subclasses).""" - pass def join_words(self, words: list[str]) -> str: """ diff --git a/pyrit/prompt_target/common/prompt_chat_target.py b/pyrit/prompt_target/common/prompt_chat_target.py index b837918295..4817dcf2ac 100644 --- a/pyrit/prompt_target/common/prompt_chat_target.py +++ b/pyrit/prompt_target/common/prompt_chat_target.py @@ -86,7 +86,6 @@ def is_json_response_supported(self) -> bool: Returns: bool: True if JSON response is supported, False otherwise. """ - pass def is_response_format_json(self, message_piece: MessagePiece) -> bool: """ diff --git a/pyrit/prompt_target/openai/openai_target.py b/pyrit/prompt_target/openai/openai_target.py index 1db5661991..b561cada9a 100644 --- a/pyrit/prompt_target/openai/openai_target.py +++ b/pyrit/prompt_target/openai/openai_target.py @@ -256,7 +256,6 @@ def _get_target_api_paths(self) -> list[str]: Returns: List of API paths (e.g., ["/chat/completions", "/v1/chat/completions"]) """ - pass @abstractmethod def _get_provider_examples(self) -> dict[str, str]: @@ -269,7 +268,6 @@ def _get_provider_examples(self) -> dict[str, str]: Dict mapping provider patterns to example URLs (e.g., {".openai.azure.com": "https://{resource}.openai.azure.com/openai/v1"}) """ - pass def _validate_url_for_target(self, endpoint_url: str) -> None: """ @@ -537,7 +535,6 @@ async def _construct_message_from_response(self, response: Any, request: Message Returns: Message: Constructed message with extracted content. """ - pass def _check_content_filter(self, response: Any) -> bool: """ @@ -694,4 +691,3 @@ def is_json_response_supported(self) -> bool: Returns: bool: True if JSON response is supported, False otherwise. """ - pass diff --git a/pyrit/prompt_target/text_target.py b/pyrit/prompt_target/text_target.py index ecc9bf2013..784309b93c 100644 --- a/pyrit/prompt_target/text_target.py +++ b/pyrit/prompt_target/text_target.py @@ -92,4 +92,3 @@ def _validate_request(self, *, message: Message) -> None: async def cleanup_target(self) -> None: """Target does not require cleanup.""" - pass diff --git a/pyrit/registry/class_registries/base_class_registry.py b/pyrit/registry/class_registries/base_class_registry.py index f481ac31b0..1bc986e0ee 100644 --- a/pyrit/registry/class_registries/base_class_registry.py +++ b/pyrit/registry/class_registries/base_class_registry.py @@ -164,7 +164,6 @@ def _discover(self) -> None: Subclasses implement this to populate self._class_entries with discovered classes. """ - pass @abstractmethod def _build_metadata(self, name: str, entry: ClassEntry[T]) -> MetadataT: @@ -180,7 +179,6 @@ def _build_metadata(self, name: str, entry: ClassEntry[T]) -> MetadataT: Returns: A metadata dataclass with descriptive information about the registered class. """ - pass def get_class(self, name: str) -> Type[T]: """ diff --git a/pyrit/scenario/core/scenario.py b/pyrit/scenario/core/scenario.py index 7d9b3c3a3f..efe8dc83e5 100644 --- a/pyrit/scenario/core/scenario.py +++ b/pyrit/scenario/core/scenario.py @@ -141,7 +141,6 @@ def get_strategy_class(cls) -> Type[ScenarioStrategy]: Returns: Type[ScenarioStrategy]: The strategy enum class (e.g., FoundryStrategy, EncodingStrategy). """ - pass @classmethod @abstractmethod @@ -156,7 +155,6 @@ def get_default_strategy(cls) -> ScenarioStrategy: Returns: ScenarioStrategy: The default aggregate strategy (e.g., FoundryStrategy.EASY, EncodingStrategy.ALL). """ - pass @classmethod @abstractmethod @@ -171,7 +169,6 @@ def default_dataset_config(cls) -> DatasetConfiguration: Returns: DatasetConfiguration: The default dataset configuration. """ - pass @apply_defaults async def initialize_async( @@ -518,7 +515,6 @@ async def _get_atomic_attacks_async(self) -> List[AtomicAttack]: Returns: List[AtomicAttack]: The list of AtomicAttack instances in this scenario. """ - pass async def run_async(self) -> ScenarioResult: """ diff --git a/pyrit/scenario/printer/scenario_result_printer.py b/pyrit/scenario/printer/scenario_result_printer.py index 4ccbf58733..1e25e7a364 100644 --- a/pyrit/scenario/printer/scenario_result_printer.py +++ b/pyrit/scenario/printer/scenario_result_printer.py @@ -28,4 +28,3 @@ async def print_summary_async(self, result: ScenarioResult) -> None: Args: result (ScenarioResult): The scenario result to summarize """ - pass diff --git a/pyrit/scenario/scenarios/airt/jailbreak.py b/pyrit/scenario/scenarios/airt/jailbreak.py index 4ef4993ecf..aade4672e1 100644 --- a/pyrit/scenario/scenarios/airt/jailbreak.py +++ b/pyrit/scenario/scenarios/airt/jailbreak.py @@ -320,7 +320,7 @@ async def _get_atomic_attacks_async(self) -> List[AtomicAttack]: for strategy in strategies: for template_name in self._jailbreaks: - for _ in range(0, self._num_attempts): + for _ in range(self._num_attempts): atomic_attack = await self._get_atomic_attack_from_strategy_async( strategy=strategy, jailbreak_template_name=template_name ) diff --git a/pyrit/score/conversation_scorer.py b/pyrit/score/conversation_scorer.py index a390a0df2b..68d0772660 100644 --- a/pyrit/score/conversation_scorer.py +++ b/pyrit/score/conversation_scorer.py @@ -127,7 +127,6 @@ def _get_wrapped_scorer(self) -> Scorer: This must be implemented by the factory-created subclass. """ - pass def validate_return_scores(self, scores: list[Score]) -> None: """ diff --git a/pyrit/score/printer/scorer_printer.py b/pyrit/score/printer/scorer_printer.py index 640b2598d6..6379c792fd 100644 --- a/pyrit/score/printer/scorer_printer.py +++ b/pyrit/score/printer/scorer_printer.py @@ -28,7 +28,6 @@ def print_objective_scorer(self, *, scorer_identifier: ScorerIdentifier) -> None Args: scorer_identifier (ScorerIdentifier): The scorer identifier to print information for. """ - pass @abstractmethod def print_harm_scorer(self, scorer_identifier: ScorerIdentifier, *, harm_category: str) -> None: @@ -44,4 +43,3 @@ def print_harm_scorer(self, scorer_identifier: ScorerIdentifier, *, harm_categor scorer_identifier (ScorerIdentifier): The scorer identifier to print information for. harm_category (str): The harm category for looking up metrics (e.g., "hate_speech", "violence"). """ - pass diff --git a/pyrit/score/scorer_evaluation/scorer_evaluator.py b/pyrit/score/scorer_evaluation/scorer_evaluator.py index 7c7a2e9b80..e4759f14f7 100644 --- a/pyrit/score/scorer_evaluation/scorer_evaluator.py +++ b/pyrit/score/scorer_evaluation/scorer_evaluator.py @@ -435,7 +435,6 @@ def _validate_and_extract_data( Raises: ValueError: If the dataset is invalid for this evaluator. """ - pass @abc.abstractmethod def _compute_metrics( @@ -466,7 +465,6 @@ def _compute_metrics( Returns: ScorerMetrics subclass with computed metrics. """ - pass def _write_metrics_to_registry( self, diff --git a/pyrit/setup/initializers/pyrit_initializer.py b/pyrit/setup/initializers/pyrit_initializer.py index 45ec72a57b..46b91c5ef5 100644 --- a/pyrit/setup/initializers/pyrit_initializer.py +++ b/pyrit/setup/initializers/pyrit_initializer.py @@ -31,7 +31,6 @@ class PyRITInitializer(ABC): def __init__(self) -> None: """Initialize the PyRIT initializer with no parameters.""" - pass @property @abstractmethod @@ -42,7 +41,6 @@ def name(self) -> str: Returns: str: A clear, descriptive name for this initializer. """ - pass @property def description(self) -> str: @@ -99,7 +97,6 @@ async def initialize_async(self) -> None: calls to set_default_value() and set_global_variable() as needed. All initializers must implement this as an async method. """ - pass def validate(self) -> None: """ From 3e3500b72728401a917a163e95923589268c7e79 Mon Sep 17 00:00:00 2001 From: Roman Lutz Date: Wed, 25 Feb 2026 06:20:09 -0800 Subject: [PATCH 2/2] FIX Mock tokenizer in unit test to avoid HuggingFace network call The chatml_tokenizer_normalizer fixture was calling AutoTokenizer.from_pretrained() which requires network access to HuggingFace. Replaced with a mock that simulates ChatML template formatting, making the test fully offline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../test_chat_normalizer_tokenizer.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/unit/message_normalizer/test_chat_normalizer_tokenizer.py b/tests/unit/message_normalizer/test_chat_normalizer_tokenizer.py index 0086f162ea..81122fcc57 100644 --- a/tests/unit/message_normalizer/test_chat_normalizer_tokenizer.py +++ b/tests/unit/message_normalizer/test_chat_normalizer_tokenizer.py @@ -5,7 +5,6 @@ from unittest.mock import MagicMock, patch import pytest -from transformers import AutoTokenizer from pyrit.message_normalizer import TokenizerTemplateNormalizer from pyrit.models import Message, MessagePiece @@ -116,8 +115,18 @@ class TestNormalizeStringAsync: @pytest.fixture def chatml_tokenizer_normalizer(self): - tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta") - return TokenizerTemplateNormalizer(tokenizer=tokenizer) + def _apply_chatml_template(messages, tokenize=False, add_generation_prompt=False): + """Simulate ChatML template formatting.""" + result = "" + for msg in messages: + result += f"<|{msg['role']}|>\n{msg['content']}\n" + if add_generation_prompt: + result += "<|assistant|>\n" + return result + + mock_tokenizer = MagicMock() + mock_tokenizer.apply_chat_template.side_effect = _apply_chatml_template + return TokenizerTemplateNormalizer(tokenizer=mock_tokenizer) @pytest.mark.asyncio async def test_normalize_chatml(self, chatml_tokenizer_normalizer: TokenizerTemplateNormalizer):