Skip to content

REFACTOR: Promote exception status codes & messages to ClassVar defaults - #1966

Closed
Roman Lutz (romanlutz) wants to merge 1 commit into
microsoft:mainfrom
romanlutz:romanlutz/jubilant-parakeet
Closed

REFACTOR: Promote exception status codes & messages to ClassVar defaults#1966
Roman Lutz (romanlutz) wants to merge 1 commit into
microsoft:mainfrom
romanlutz:romanlutz/jubilant-parakeet

Conversation

@romanlutz

Copy link
Copy Markdown
Contributor

Summary

Promote hard-coded status_code / message defaults out of the
__init__ signatures of PyritException and its subclasses in
pyrit/exceptions/exception_classes.py and onto each class as
DEFAULT_STATUS_CODE: ClassVar[int] / DEFAULT_MESSAGE: ClassVar[str],
in line with the class-level constants rule in the style guide and the
pattern established by #1964 and #1965.

Changes

For each of the following exception classes, the hard-coded
status_code / message defaults in the __init__ signature are
replaced with per-class DEFAULT_* ClassVar constants resolved
through the sentinel-default pattern (Noneself.DEFAULT_*):

Class DEFAULT_STATUS_CODE DEFAULT_MESSAGE
PyritException 500 "An error occurred"
BadRequestException 400 "Bad Request"
RateLimitException 429 "Rate Limit Exception"
ServerErrorException 500 "Server Error"
EmptyResponseException 204 "No Content"
InvalidJsonException "Invalid JSON Response"
MissingPromptPlaceholderException "No prompt placeholder"

InvalidJsonException and MissingPromptPlaceholderException only
parameterize message, so only DEFAULT_MESSAGE is defined for
them — they inherit DEFAULT_STATUS_CODE (500) from PyritException.

The keyword-only signature (*,) is preserved for every class. All
existing call sites already pass these arguments by name (verified by
grep across pyrit/, tests/, doc/), so this is signature-compatible.

Verification

uv run --link-mode=copy ruff check pyrit/exceptions/    # All checks passed!
uv run --link-mode=copy pytest tests/unit/exceptions/   # 84 passed

Spot-checked dependent tests that import these exceptions:
tests/unit/score/test_insecure_code_scorer.py,
tests/unit/prompt_converter/test_generic_llm_converter.py,
tests/unit/executor/attack/multi_turn/test_tree_of_attacks.py,
tests/unit/prompt_normalizer/test_prompt_normalizer.py — 146 passed.

Notes

Move hard-coded `status_code` / `message` defaults out of the
`__init__` signatures of `PyritException` and its subclasses in
`pyrit/exceptions/exception_classes.py` and onto each class as
`DEFAULT_STATUS_CODE: ClassVar[int]` / `DEFAULT_MESSAGE: ClassVar[str]`.
Use the sentinel-default pattern (`None` -> `self.DEFAULT_*`) so
subclass overrides resolve through `self` while keeping the keyword-only
signatures unchanged for all existing callers.

No value changes. No behaviour changes.

Verified: ruff / pytest tests/unit/exceptions/.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@romanlutz

Copy link
Copy Markdown
Contributor Author

Closing per maintainer feedback on the constants-audit work.

This PR introduced the sentinel-default pattern (changing x: T = <literal> into x: T | None = None + if x is None: x = self.DEFAULT_X) which widens the public API signature. The preference is to keep constants inside their owning class (Track A — see #1964, #1965, #1972, #1973, #1974, #1975) but not to promote hard-coded single-use defaults via a None-sentinel.

Branch left intact in case any portion is worth cherry-picking later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants