ruff TC008: fix changes program behavior
Hypothesis Nemotron 3 Super predicted not to fire fired 1×
Rule does not rewrite '"int" "|" "None"' (concatenated strings), so alias remains quoted; if developer expects it to be unquoted, they may misuse it.
Program
Output
'int|None'
int | None
- fired
- 1 before · 0 after
- runs
- each version twice, same file name, no network
Judge Nemotron 3 Ultra advisory real · medium
The rule removes quotes from a PEP 613 type alias that uses concatenated string literals ('int' '|' 'None'), rewriting it to the unquoted union int|None. At runtime, the original alias evaluates to the string 'int|None' (no spaces), while the fixed alias evaluates to the types.Union object int | None (repr shows spaces). This changes the program's observable behavior: the printed representation differs, and any code inspecting the alias value would see a string vs a union object. The documentation presents the rewrite as removing unnecessary quotes but does not warn that concatenated string literals produce a different runtime value than the unquoted union, making the fix unsafe for this construct.
Explanation Nemotron 3 Super unverified
The original string literal `"int" \| \"None\"` evaluates to the exact text `int|None`, which prints as `'int|None'`. Replacing it with the type expression `int|None` creates a union type object whose `__repr__` yields `int | None` (spaces added by Python’s formatting). Since a string and a type are distinct objects with different representations, the change alters observable output, violating semantic equivalence.
Reproduce
- file
prog.py(the before program)- minimized program
- r1-p005.py
- evidence sheet
- r1-p005.md
- regression fixture
- r1-p005.py the minimized program with a header stating the observed change, for the tool's test suite
python3 prog.py ruff check --preview --isolated --select TC008 --fix prog.py python3 prog.py
Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra
TC008 fix · TC008 TypeAlias string union · TypeAlias literal pipe syntax · ruff TC008 quoted types fix
no duplicate or related report 1 hits triaged
Details
- program
- String Concatenation Inside Quotes Left Unchanged
- hazard
- Rule does not rewrite '"int" "|" "None"' (concatenated strings), so alias remains quoted; if developer expects it to be unquoted, they may misuse it.
- oracle
stdout before: 'int|None' stdout after: int | None- run
- 2026-10-17T20-45-09-298Z-olks-ruff-TC008
generated program (4 lines) and the fix's diff
--- orig.py 2026-10-17 16:45:19.800591576 -0400 +++ fixed.py 2026-10-17 16:45:20.001597096 -0400 @@ -1,4 +1,4 @@ from typing import TypeAlias -BadAlias: TypeAlias = "int" "|" "None" +BadAlias: TypeAlias = int|None print(type(BadAlias)) print(repr(BadAlias))
rule documentation given to the models
# quoted-type-alias (TC008) Derived from the **flake8-type-checking** linter. Fix is always available. This rule is in preview and is not stable. The `--preview` flag is required for use. ## What it does Checks for unnecessary quotes in [PEP 613] explicit type aliases and [PEP 695] type statements. ## Why is this bad? Unnecessary string forward references can lead to additional overhead in runtime libraries making use of type hints. They can also have bad interactions with other runtime uses like [PEP 604] type unions. PEP-613 type aliases are only flagged by the rule if Ruff can have high confidence that the quotes are unnecessary. Specifically, any PEP-613 type alias where the type expression on the right-hand side contains subscripts or attribute accesses will not be flagged. This is because type aliases can reference types that are, for example, generic in stub files but not at runtime. That can mean that a type checker expects the referenced type to be subscripted with type arguments despite the fact that doing so would fail at runtime if the type alias value was not quoted. Similarly, a type alias might need to reference a module-level attribute that exists in a stub file but not at runtime, meaning that the type alias value would need to be quoted to avoid a runtime error. ## Example Given: ```python from typing import TypeAlias OptInt: TypeAlias = "int | None" ``` Use instead: ```python from typing import TypeAlias OptInt: TypeAlias = int | None ``` Given: ```python type OptInt = "int | None" ``` Use instead: ```python type OptInt = int | None ``` ## Fix safety This rule's fix is marked as safe, unless the type annotation contains comments. ## See also This rule only applies to type aliases in non-stub files. For removing quotes in other contexts or in stub files, see: - [`quoted-annotation-in-stub`][PYI020]: A rule that removes all quoted annotations from stub files - [`quoted-annotation`][UP037]: A rule that removes unnecessary quotes from *annotations* in runtime files. ## References - [PEP 613 – Explicit Type Aliases](https://peps.python.org/pep-0613/) - [PEP 695: Generic Type Alias](https://peps.python.org/pep-0695/#generic-type-alias) - [PEP 604 – Allow writing union types as `X | Y`](https://peps.python.org/pep-0604/) [PEP 604]: https://peps.python.org/pep-0604/ [PEP 613]: https://peps.python.org/pep-0613/ [PEP 695]: https://peps.python.org/pep-0695/#generic-type-alias [PYI020]: https://docs.astral.sh/ruff/rules/quoted-annotation-in-stub/ [UP037]: https://docs.astral.sh/ruff/rules/quoted-annotation/