Skip to content

Commit 6c8303f

Browse files
MAINT: introduce ruff-check for docstring quality enforcement (microsoft#1181)
Co-authored-by: Roman Lutz <romanlutz13@gmail.com>
1 parent 0989c79 commit 6c8303f

146 files changed

Lines changed: 599 additions & 536 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ repos:
4949
name: Import Sort (Jupyter Notebooks)
5050
args: [--profile=black]
5151

52+
- repo: https://github.com/astral-sh/ruff-pre-commit
53+
rev: v0.14.4
54+
hooks:
55+
- id: ruff-check
56+
name: ruff-check
57+
5258
- repo: https://github.com/PyCQA/flake8
5359
rev: 7.1.2
5460
hooks:
@@ -67,13 +73,6 @@ repos:
6773
additional_dependencies: ['requests']
6874
exclude: (release_process.md|git.md|^doc/deployment/|tests|pyrit/prompt_converter/morse_converter.py|.github|pyrit/prompt_converter/emoji_converter.py|pyrit/score/markdown_injection.py|^pyrit/datasets/|^pyrit/auxiliary_attacks/gcg/)
6975

70-
- repo: https://github.com/pycqa/pylint
71-
rev: v3.3.7
72-
hooks:
73-
- id: pylint
74-
args: [--disable=all, --enable=unused-import]
75-
exclude: NOTICE.txt
76-
7776
- repo: https://github.com/pre-commit/mirrors-mypy
7877
rev: v1.15.0
7978
hooks:

doc/code/scoring/7_batch_scorer.ipynb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
{
44
"cell_type": "markdown",
55
"id": "0",
6-
"metadata": {
7-
"lines_to_next_cell": 0
8-
},
6+
"metadata": {},
97
"source": [
108
"# 7. Batch Scoring\n",
119
"\n",
@@ -133,10 +131,9 @@
133131
}
134132
],
135133
"source": [
136-
"# pylint: disable=W0611\n",
137134
"from pyrit.memory import CentralMemory\n",
138135
"from pyrit.prompt_target import OpenAIChatTarget\n",
139-
"from pyrit.score import (\n",
136+
"from pyrit.score import ( # noqa: F401\n",
140137
" AzureContentFilterScorer,\n",
141138
" BatchScorer,\n",
142139
" ContentClassifierPaths,\n",
@@ -205,12 +202,11 @@
205202
}
206203
],
207204
"source": [
208-
"# pylint: disable=W0611\n",
209205
"import uuid\n",
210206
"\n",
211207
"from pyrit.memory import CentralMemory\n",
212208
"from pyrit.prompt_target import OpenAIChatTarget\n",
213-
"from pyrit.score import (\n",
209+
"from pyrit.score import ( # noqa: F401\n",
214210
" AzureContentFilterScorer,\n",
215211
" BatchScorer,\n",
216212
" ContentClassifierPaths,\n",
@@ -256,7 +252,8 @@
256252
],
257253
"metadata": {
258254
"jupytext": {
259-
"cell_metadata_filter": "-all"
255+
"cell_metadata_filter": "-all",
256+
"main_language": "python"
260257
},
261258
"language_info": {
262259
"codemirror_mode": {

doc/code/scoring/7_batch_scorer.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,9 @@
6464
# Once the prompts are in the database (which again, is often automatic) we can use `BatchScorer` to score them with whatever scorers we want. It works in parallel with batches.
6565

6666
# %%
67-
# pylint: disable=W0611
6867
from pyrit.memory import CentralMemory
6968
from pyrit.prompt_target import OpenAIChatTarget
70-
from pyrit.score import (
69+
from pyrit.score import ( # noqa: F401
7170
AzureContentFilterScorer,
7271
BatchScorer,
7372
ContentClassifierPaths,
@@ -113,12 +112,11 @@
113112
# - Converted Value SHA256
114113

115114
# %%
116-
# pylint: disable=W0611
117115
import uuid
118116

119117
from pyrit.memory import CentralMemory
120118
from pyrit.prompt_target import OpenAIChatTarget
121-
from pyrit.score import (
119+
from pyrit.score import ( # noqa: F401
122120
AzureContentFilterScorer,
123121
BatchScorer,
124122
ContentClassifierPaths,

pyproject.toml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ dev = [
8181
"pytest-cov>=6.1.1",
8282
"pytest-timeout>=2.4.0",
8383
"respx>=0.22.0",
84+
"ruff>=0.14.4",
8485
"sphinxcontrib-mermaid>=1.0.0",
8586
"types-PyYAML>=6.0.12.20250516",
8687
"types-requests>=2.31.0.20250515",
@@ -174,6 +175,9 @@ formats = "ipynb,py:percent"
174175

175176
[tool.ruff]
176177
line-length = 120
178+
179+
[tool.ruff.lint]
180+
preview = true
177181
fixable = [
178182
"A",
179183
"B",
@@ -220,3 +224,34 @@ fixable = [
220224
"UP",
221225
"YTT",
222226
]
227+
select = [
228+
"D", # https://docs.astral.sh/ruff/rules/#pydocstyle-d
229+
"DOC", # https://docs.astral.sh/ruff/rules/#pydoclint-doc
230+
"F401", # unused-import
231+
]
232+
ignore = [
233+
"D100", # Missing docstring in public module
234+
"D200", # One-line docstring should fit on one line
235+
"D205", # 1 blank line required between summary line and description
236+
"D212", # Multi-line docstring summary should start at the first line
237+
"D301", # Use r""" if any backslashes in a docstring
238+
"DOC502", # Raised exception is not explicitly raised
239+
]
240+
extend-select = [
241+
"D204", # 1 blank line required after class docstring
242+
"D213", # Multi-line docstring summary should start at the second line
243+
"D401", # First line of docstring should be in imperative mood
244+
"D404", # First word of the docstring should not be "This"
245+
]
246+
247+
[tool.ruff.lint.per-file-ignores]
248+
# Ignore D and DOC rules everywhere except for the pyrit/ directory
249+
"!pyrit/**.py" = ["D", "DOC"]
250+
# Temporary ignores for pyrit/ subdirectories until issue #1176
251+
# https://github.com/Azure/PyRIT/issues/1176 is fully resolved
252+
# TODO: Remove these ignores once the issues are fixed
253+
"pyrit/{analytics,auth,auxiliary_attacks,chat_message_normalizer,cli,common,datasets,embedding,exceptions,executor,memory,models,prompt_converter,prompt_normalizer,prompt_target,scenarios,score,setup,ui}/**/*.py" = ["D101", "D102", "D103", "D104", "D105", "D106", "D107", "D401", "D404", "D417", "D418", "DOC102", "DOC201", "DOC202", "DOC402", "DOC501"]
254+
"pyrit/__init__.py" = ["D104"]
255+
256+
[tool.ruff.lint.pydocstyle]
257+
convention = "google"

pyrit/analytics/conversation_analytics.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def get_prompt_entries_with_same_converted_content(
2929
self, *, chat_message_content: str
3030
) -> list[ConversationMessageWithSimilarity]:
3131
"""
32-
Retrieves chat messages that have the same converted content
32+
Retrieves chat messages that have the same converted content.
3333
3434
Args:
3535
chat_message_content (str): The content of the chat message to find similar messages for.
@@ -68,7 +68,6 @@ def get_similar_chat_messages_by_embedding(
6868
List[ConversationMessageWithSimilarity]: A list of ConversationMessageWithSimilarity objects representing
6969
the similar chat messages based on embedding similarity.
7070
"""
71-
7271
all_embdedding_memory = self.memory_interface.get_all_embeddings()
7372
similar_messages = []
7473

pyrit/analytics/result_analysis.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ def analyze_results(attack_results: list[AttackResult]) -> dict:
4949
"By_attack_identifier": dict[str, AttackStats]
5050
}
5151
"""
52-
5352
if not attack_results:
5453
raise ValueError("attack_results cannot be empty")
5554

pyrit/auth/azure_auth.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ def _set_default_token(self) -> None:
4141
self.token = self.access_token.token
4242

4343
def refresh_token(self) -> str:
44-
"""Refresh the access token if it is expired.
44+
"""
45+
Refresh the access token if it is expired.
4546
4647
Returns:
4748
A token
@@ -79,7 +80,8 @@ def get_access_token_from_azure_cli(*, scope: str, tenant_id: str = ""):
7980

8081

8182
def get_access_token_from_azure_msi(*, client_id: str, scope: str):
82-
"""Connect to an AOAI endpoint via managed identity credential attached to an Azure resource.
83+
"""
84+
Connect to an AOAI endpoint via managed identity credential attached to an Azure resource.
8385
For proper setup and configuration of MSI
8486
https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview.
8587
@@ -100,7 +102,8 @@ def get_access_token_from_azure_msi(*, client_id: str, scope: str):
100102

101103

102104
def get_access_token_from_msa_public_client(*, client_id: str, scope: str):
103-
"""Uses MSA account to connect to an AOAI endpoint via interactive login. A browser window
105+
"""
106+
Uses MSA account to connect to an AOAI endpoint via interactive login. A browser window
104107
will open and ask for login credentials.
105108
106109
Args:
@@ -120,7 +123,8 @@ def get_access_token_from_msa_public_client(*, client_id: str, scope: str):
120123

121124

122125
def get_access_token_from_interactive_login(scope: str) -> str:
123-
"""Connects to an OpenAI endpoint with an interactive login from Azure. A browser window will
126+
"""
127+
Connects to an OpenAI endpoint with an interactive login from Azure. A browser window will
124128
open and ask for login credentials. The token will be scoped for Azure Cognitive services.
125129
126130
Returns:
@@ -135,7 +139,8 @@ def get_access_token_from_interactive_login(scope: str) -> str:
135139

136140

137141
def get_token_provider_from_default_azure_credential(scope: str) -> Callable[[], str]:
138-
"""Connect to an AOAI endpoint via default Azure credential.
142+
"""
143+
Connect to an AOAI endpoint via default Azure credential.
139144
140145
Returns:
141146
Authentication token provider
@@ -149,7 +154,8 @@ def get_token_provider_from_default_azure_credential(scope: str) -> Callable[[],
149154

150155

151156
def get_default_scope(endpoint: str) -> str:
152-
"""Get the default scope for the given endpoint.
157+
"""
158+
Get the default scope for the given endpoint.
153159
154160
Args:
155161
endpoint (str): The endpoint to get the scope for.
@@ -170,12 +176,13 @@ def get_default_scope(endpoint: str) -> str:
170176
def get_speech_config(resource_id: Union[str, None], key: Union[str, None], region: str):
171177
"""
172178
Get the speech config using key/region pair (for key auth scenarios) or resource_id/region pair
173-
(for Entra auth scenarios)
179+
(for Entra auth scenarios).
174180
175181
Args:
176182
resource_id (Union[str, None]): The resource ID to get the token for.
177183
key (Union[str, None]): The Azure Speech key
178184
region (str): The region to get the token for.
185+
179186
Returns:
180187
The speech config based on passed in args
181188
@@ -207,7 +214,8 @@ def get_speech_config(resource_id: Union[str, None], key: Union[str, None], regi
207214

208215

209216
def get_speech_config_from_default_azure_credential(resource_id: str, region: str):
210-
"""Get the speech config for the given resource ID and region.
217+
"""
218+
Get the speech config for the given resource ID and region.
211219
212220
Args:
213221
resource_id (str): The resource ID to get the token for.

0 commit comments

Comments
 (0)