Skip to content

[ty] Support type:ignore[ty:code] suppressions#24096

Merged
MichaReiser merged 1 commit intomainfrom
micha/type-ignore-namespaced
Mar 24, 2026
Merged

[ty] Support type:ignore[ty:code] suppressions#24096
MichaReiser merged 1 commit intomainfrom
micha/type-ignore-namespaced

Conversation

@MichaReiser
Copy link
Copy Markdown
Member

@MichaReiser MichaReiser commented Mar 21, 2026

Summary

This PR changes how ty handles type:ignore[<codes>] suppressions.

Before, a type:ignore[<codes>] suppression suppressed any error on its line.

Now:

  • type:ignore[ty:<code>] suppressions are now handled the same as a ty:ignore[code] suppression. They suppress the specific code only.
  • type:ignore[<code>] suppressions where code isn't prefixed with ty: are ignored

The advantage of the new design is that it enables suppressing errors from multiple type checkers using a single comment, e.g., users can now write
# type:ignore[mypy-code, ty:ty-code] where they previously had to use two comments like this # type:ignore[mypy-code] # ty:ignore[ty-code] or had to use # type:ignore, which is broader than what they want.

This should not lead to compatibility issues because mypy ignores unknown rule codes.

The semantics of a type:ignore suppression (without codes) remains unchanged. It suppresses all errors on its line.

I decided to add support for file-level ty:ignore[code] suppressions in this PR. It felt weird that it is supported for type:ignore comments but not
when using our recommended ty:ignore comments. This fixes astral-sh/ty#1891

Closes astral-sh/ty#2928

Test Plan

Updated and added integration tests

@MichaReiser MichaReiser added the ty Multi-file analysis & type inference label Mar 21, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 21, 2026

Typing conformance results regressed ❌

The percentage of diagnostics emitted that were expected errors decreased from 85.38% to 85.29%. The percentage of expected errors that received a diagnostic held steady at 78.70%. The number of fully passing files regressed from 65/133 to 64/133.

Summary

How are test cases classified?

Each test case represents one expected error annotation or a group of annotations sharing a tag. Counts are per test case, not per diagnostic — multiple diagnostics on the same line count as one. Required annotations (E) are true positives when ty flags the expected location and false negatives when it does not. Optional annotations (E?) are true positives when flagged but true negatives (not false negatives) when not. Tagged annotations (E[tag]) require ty to flag exactly one of the tagged lines; tagged multi-annotations (E[tag+]) allow any number up to the tag count. Flagging unexpected locations counts as a false positive.

Metric Old New Diff Outcome
True Positives 835 835 +0
False Positives 143 144 +1 ⏫ (❌)
False Negatives 226 226 +0
Total Diagnostics 1055 1056 +1
Precision 85.38% 85.29% -0.09% ⏬ (❌)
Recall 78.70% 78.70% +0.00%
Passing Files 65/133 64/133 -1 ⏬ (❌)

Test file breakdown

1 file altered
File True Positives False Positives False Negatives Status
directives_type_ignore.py 0 1 (+1) ❌ 0 ❌ Newly Failing ☹️
Total (all files) 835 144 (+1) ❌ 226 64/133

False positives added (1)

1 diagnostic
Test case Diff

directives_type_ignore.py:14

+error[invalid-assignment] Object of type `Literal[""]` is not assignable to `int`

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 21, 2026

Memory usage report

Summary

Project Old New Diff Outcome
flake8 48.17MB 48.17MB -
prefect 705.63MB 705.55MB -0.01% (80.15kB) ⬇️
trio 116.28MB 116.17MB -0.09% (111.46kB) ⬇️
sphinx 263.36MB 263.19MB -0.06% (167.66kB) ⬇️

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
check_file_impl 17.91MB 17.84MB -0.41% (74.54kB) ⬇️
suppressions 387.33kB 381.70kB -1.45% (5.62kB) ⬇️
infer_scope_types_impl 53.57MB 53.57MB +0.00% (28.00B) ⬇️
infer_definition_types 90.85MB 90.85MB -0.00% (16.00B) ⬇️
infer_expression_types_impl 56.20MB 56.20MB +0.00% (4.00B) ⬇️

trio

Name Old New Diff Outcome
check_file_impl 1.93MB 1.83MB -5.31% (104.93kB) ⬇️
suppressions 46.12kB 35.67kB -22.66% (10.45kB) ⬇️
infer_definition_types 7.52MB 7.52MB +0.01% (921.00B) ⬇️
infer_expression_type_impl 1.52MB 1.52MB +0.04% (648.00B) ⬇️
infer_expression_types_impl 6.08MB 6.08MB +0.01% (644.00B) ⬇️
FunctionType<'db>::signature_ 1.07MB 1.07MB +0.05% (520.00B) ⬇️
resolve_module_query 152.55kB 153.00kB +0.30% (464.00B) ⬇️
TupleType<'db>::to_class_type_ 51.14kB 51.42kB +0.56% (292.00B) ⬇️
Specialization 473.05kB 473.27kB +0.05% (224.00B) ⬇️
infer_scope_types_impl 4.76MB 4.76MB -0.00% (156.00B) ⬇️
GenericAlias 200.18kB 200.32kB +0.07% (144.00B) ⬇️
UnionType 300.94kB 301.06kB +0.04% (128.00B) ⬇️
ModuleNameIngredient 35.18kB 35.30kB +0.33% (120.00B) ⬇️
loop_header_reachability 137.15kB 137.17kB +0.02% (24.00B) ⬇️
all_narrowing_constraints_for_expression 637.20kB 637.23kB +0.00% (24.00B) ⬇️
... 1 more

sphinx

Name Old New Diff Outcome
check_file_impl 5.06MB 4.91MB -2.93% (152.12kB) ⬇️
suppressions 186.80kB 165.66kB -11.32% (21.14kB) ⬇️
infer_expression_types_impl 19.85MB 19.85MB +0.02% (3.48kB) ⬇️
infer_definition_types 24.44MB 24.44MB +0.00% (997.00B) ⬇️
FunctionType<'db>::signature_ 2.26MB 2.26MB +0.03% (692.00B) ⬇️
TupleType<'db>::to_class_type_ 160.14kB 160.59kB +0.28% (464.00B) ⬇️
infer_scope_types_impl 15.55MB 15.55MB -0.00% (252.00B) ⬇️
UnionType 1.24MB 1.24MB +0.01% (144.00B) ⬇️
Specialization 1.02MB 1.02MB +0.01% (112.00B) ⬇️
GenericAlias 450.14kB 450.21kB +0.02% (72.00B) ⬇️
infer_deferred_types 5.60MB 5.60MB -0.00% (56.00B) ⬇️

@MichaReiser MichaReiser force-pushed the micha/type-ignore-namespaced branch from 9d1df54 to b331fb8 Compare March 21, 2026 15:40
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 21, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
unused-type-ignore-comment 0 4,943 0
invalid-argument-type 1,775 0 0
unresolved-attribute 1,343 0 0
invalid-assignment 632 0 0
invalid-method-override 449 0 0
invalid-return-type 236 0 0
unresolved-import 204 0 0
unresolved-reference 155 0 0
no-matching-overload 153 0 0
unsupported-operator 106 0 0
missing-argument 89 0 0
empty-body 80 0 0
invalid-key 61 0 0
unknown-argument 59 0 0
invalid-type-form 49 0 0
not-subscriptable 48 0 0
call-non-callable 46 0 0
too-many-positional-arguments 42 0 0
possibly-unresolved-reference 41 0 0
invalid-parameter-default 34 0 0
deprecated 21 0 0
not-iterable 19 0 0
dataclass-field-order 18 0 0
invalid-type-arguments 11 0 0
unsupported-base 10 0 0
invalid-await 9 0 0
possibly-missing-submodule 9 0 0
invalid-attribute-access 7 0 0
call-top-callable 6 0 0
invalid-yield 5 0 0
subclass-of-final-class 5 0 0
override-of-final-method 4 0 0
conflicting-declarations 3 0 0
invalid-protocol 3 0 0
invalid-super-argument 3 0 0
parameter-already-assigned 3 0 0
redundant-cast 3 0 0
final-without-value 2 0 0
possibly-missing-import 2 0 0
unused-awaitable 2 0 0
invalid-base 1 0 0
invalid-type-guard-definition 1 0 0
possibly-missing-attribute 1 0 0
shadowed-type-variable 1 0 0
type-assertion-failure 1 0 0
unavailable-implicit-super-arguments 1 0 0
Total 5,753 4,943 0

Changes in flaky projects detected. Raw diff output excludes flaky projects; see the HTML report for details.

Showing a random sample of 100 of 10547 changes. See the HTML report for the full diff.

Raw diff sample (100 of 10547 changes)
PyWinCtl (https://github.com/Kalmat/PyWinCtl)
- src/pywinctl/_pywinctl_macos.py:553:53 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

aiohttp (https://github.com/aio-libs/aiohttp)
+ aiohttp/_cookie_helpers.py:109:5 [error] [unresolved-attribute] Object of type `Morsel[str]` has no attribute `__setstate__`
- aiohttp/worker.py:27:26 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

aiohttp-devtools (https://github.com/aio-libs/aiohttp-devtools)
+ aiohttp_devtools/runserver/utils.py:29:9 [error] [invalid-method-override] Invalid override of method `__eq__`: Definition is incompatible with `object.__eq__`

archinstall (https://github.com/archlinux/archinstall)
- archinstall/lib/output.py:53:51 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

beartype (https://github.com/beartype/beartype)
+ beartype/_data/typing/datatyping.py:130:53 [error] [invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member
- beartype/_cave/_cavefast.py:188:26 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- beartype/door/_cls/doormeta.py:204:30 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

core (https://github.com/home-assistant/core)
- homeassistant/components/time_date/sensor.py:53:79 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- homeassistant/components/adguard/entity.py:64:20 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- homeassistant/components/fronius/sensor.py:673:52 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- homeassistant/components/recorder/util.py:453:63 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- homeassistant/components/tts/__init__.py:1171:38 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ homeassistant/components/websocket_api/decorators.py:148:28 [error] [invalid-argument-type] Argument to function `len` is incorrect: Expected `Sized`, found `dict[str | Marker, Any] | All`
+ homeassistant/components/withings/__init__.py:385:17 [error] [invalid-argument-type] Argument to function `__new__` is incorrect: Expected `str | Buffer | SupportsInt | SupportsIndex`, found `str | bytes | FileField`

cryptography (https://github.com/pyca/cryptography)
+ tests/hazmat/primitives/decrepit/test_algorithms.py:86:23 [error] [invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `bytes`, found `Literal["0000000000000000"]`
+ tests/hazmat/primitives/test_block.py:201:17 [error] [invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `bytes | bytearray | memoryview[int]`, found `list[int]`
+ tests/hazmat/primitives/test_dh.py:707:17 [error] [invalid-argument-type] Argument to bound method `private_bytes` is incorrect: Expected `PrivateFormat`, found `Literal["invalidformat"]`
+ tests/x509/test_x509_ext.py:2129:32 [error] [invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Name`, found `float`
+ tests/x509/test_x509_ext.py:6512:17 [error] [invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int | None`, found `Literal["notanint"]`
+ tests/x509/test_x509_ext.py:6727:17 [error] [invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | None`, found `Literal[42]`
+ tests/hazmat/primitives/test_ssh.py:1986:33 [error] [invalid-argument-type] Argument to function `ssh_key_fingerprint` is incorrect: Expected `EllipticCurvePublicKey | RSAPublicKey | DSAPublicKey | Ed25519PublicKey`, found `object`

dd-trace-py (https://github.com/DataDog/dd-trace-py)
+ ddtrace/debugging/_function/store.py:75:17 [error] [unresolved-attribute] Attribute `__wrapped__` is not defined on `None` in union `Unknown | None`
+ ddtrace/internal/settings/http.py:61:9 [error] [unresolved-attribute] Object of type `(Unknown, /) -> Any` has no attribute `cache_clear`
+ tests/profiling/collector/test_threading.py:1709:30 [warning] [unsupported-base] Unsupported class base with type `(type[Semaphore] & _LockAllocatorWrapper) | (type[Condition] & _LockAllocatorWrapper)`

dedupe (https://github.com/dedupeio/dedupe)
- dedupe/api.py:551:47 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

hydpy (https://github.com/hydpy-dev/hydpy)
- hydpy/core/sequencetools.py:3773:33 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- hydpy/tests/plugin/check_submodeladder_hooks.py:44:34 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- hydpy/tests/plugin/check_trim_hook.py:36:26 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

jax (https://github.com/google/jax)
- jax/_src/earray.py:81:84 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ jax/_src/checkify.py:263:34 [error] [not-subscriptable] Cannot subscript object of type `int` with no `__getitem__` method
+ jax/_src/numpy/indexing.py:292:51 [error] [invalid-argument-type] Argument to function `ndim` is incorrect: Expected `_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | ... omitted 5 union elements`, found `int | integer[Any] | slice[Any, Any, Any] | ... omitted 5 union elements`

kornia (https://github.com/kornia/kornia)
- kornia/augmentation/container/patch.py:419:109 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
+ pymongo/asynchronous/bulk.py:458:17 [error] [invalid-argument-type] Argument to bound method `command` is incorrect: Expected `MongoClient[Any] | None`, found `AsyncMongoClient[Any]`
+ pymongo/pool_shared.py:346:17 [warning] [deprecated] The function `match_hostname` is deprecated: Deprecated since Python 3.7; removed in Python 3.12.
- pymongo/ssl_support.py:62:44 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ pymongo/synchronous/collection.py:968:25 [error] [invalid-assignment] Cannot assign to a subscript on an object of type `_DocumentType@Collection`

mypy (https://github.com/python/mypy)
- mypy/typeshed/stdlib/encodings/big5.pyi:17:77 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- mypy/typeshed/stdlib/encodings/cp950.pyi:17:77 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi:21:9 [error] [invalid-method-override] Invalid override of method `decode`: Definition is incompatible with `Codec.decode`
- mypy/typeshed/stdlib/ssl.pyi:377:44 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

openlibrary (https://github.com/internetarchive/openlibrary)
+ openlibrary/asgi_app.py:68:13 [error] [unresolved-attribute] Unresolved attribute `infobase` on type `<module 'infogami.config'>`.

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/arrays/period.py:395:41 [error] [unresolved-attribute] Attribute `dtype` is not defined on `NaTType & ~Period` in union `(NaTType & ~Period) | (PeriodArray & ~Period)`
+ pandas/core/construction.py:516:16 [error] [unresolved-attribute] Object of type `T@extract_array` has no attribute `to_numpy`
- pandas/core/frame.py:8200:41 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pandas/core/frame.py:17272:22 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ pandas/core/frame.py:596:25 [error] [invalid-argument-type] Argument to function `nested_data_to_arrays` is incorrect: Expected `Index | None`, found `(ExtensionArray & ~Top[set[Unknown]]) | (ndarray[tuple[Any, ...], dtype[Any]] & ~Top[set[Unknown]]) | (Index & ~Top[set[Unknown]]) | ... omitted 5 union elements`
+ pandas/core/groupby/groupby.py:4707:29 [error] [unresolved-attribute] Object of type `(ExtensionArray & ~BaseMaskedArray) | (ndarray[tuple[Any, ...], dtype[Any]] & ~BaseMaskedArray)` has no attribute `_ndarray`
- pandas/core/indexes/base.py:8257:54 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pandas/core/indexes/range.py:1152:52 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ pandas/core/indexes/range.py:300:9 [error] [invalid-method-override] Invalid override of method `_format_attrs`: Definition is incompatible with `Index._format_attrs`
+ pandas/core/ops/array_ops.py:285:31 [error] [invalid-argument-type] Argument to function `_bool_arith_check` is incorrect: Expected `ndarray[tuple[Any, ...], dtype[Any]]`, found `ExtensionArray | ndarray[tuple[Any, ...], dtype[Any]]`
+ pandas/core/reshape/tile.py:434:31 [error] [invalid-argument-type] Argument to function `abs` is incorrect: Expected `SupportsAbs[Unknown]`, found `~Literal[0]`
+ pandas/core/window/ewm.py:396:17 [error] [invalid-argument-type] Argument to function `get_center_of_mass` is incorrect: Expected `int | float | None`, found `None | (int & ~timedelta64[timedelta | int | None]) | (float & ~timedelta64[timedelta | int | None]) | (signedinteger[_64Bit] & ~str & ~timedelta & ~timedelta64[timedelta | int | None])`
- pandas/plotting/_matplotlib/converter.py:407:49 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pandas/plotting/_matplotlib/hist.py:207:70 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

pip (https://github.com/pypa/pip)
+ src/pip/_vendor/certifi/core.py:11:5 [error] [unresolved-attribute] Object of type `None` has no attribute `__exit__`
- src/pip/_vendor/pkg_resources/__init__.py:1907:37 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ src/pip/_vendor/truststore/_macos.py:279:1 [error] [invalid-assignment] Object of type `def _handle_osstatus(result: c_int32, _: Any, args: Any) -> Any` is not assignable to attribute `errcheck` of type `(_CData | None, CFuncPtr, tuple[_CData, ...], /) -> _SimpleCData[Any] | _Pointer[Any] | CFuncPtr | ... omitted 3 union elements`

poetry (https://github.com/python-poetry/poetry)
- tests/console/commands/self/test_show_plugins.py:111:22 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/_queries.py:155:29 [error] [invalid-argument-type] Method `__getitem__` of type `Overload[(index: int, /) -> Any, (index: slice[int | None, int | None, int | None], /) -> Sequence[Any]]` cannot be called with key of type `str` on object of type `Sequence[Any]`
- tests/test_pipeline_async.py:57:48 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/types/test_multirange.py:262:24 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/types/test_range.py:169:29 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

pydantic (https://github.com/pydantic/pydantic)
+ pydantic/json_schema.py:2201:62 [error] [invalid-key] Unknown key "schema" for TypedDict `EnumSchema`

pylint (https://github.com/pycqa/pylint)
- pylint/checkers/base/comparison_checker.py:152:81 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

pytest (https://github.com/pytest-dev/pytest)
+ src/_pytest/fixtures.py:951:18 [error] [missing-argument] No arguments provided for required parameters 1, 2
- testing/test_collection.py:65:29 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ testing/test_pytester.py:488:26 [error] [invalid-argument-type] Argument to bound method `fnmatch_lines` is incorrect: Expected `Sequence[Unknown]`, found `Source`

rotki (https://github.com/rotki/rotki)
+ rotkehlchen/api/rest.py:3914:21 [error] [invalid-argument-type] Argument to function `should_exclude_possible_match` is incorrect: Expected `AssetMovement`, found `HistoryBaseEntry[Unknown]`
+ rotkehlchen/chain/evm/decoding/velodrome/decoder.py:454:17 [error] [unresolved-attribute] Attribute `pop` is not defined on `None` in union `Unknown | None`

schemathesis (https://github.com/schemathesis/schemathesis)
- src/schemathesis/specs/openapi/stateful/__init__.py:256:24 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

scipy (https://github.com/scipy/scipy)
- scipy/_lib/_util.py:968:37 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ scipy/stats/_new_distributions.py:543:5 [error] [unresolved-attribute] Unresolved attribute `orders` on type `def _moment_central_formula(self, order, *, n, p, **kwargs) -> Unknown`
+ subprojects/array_api_extra/src/array_api_extra/_lib/_lazy.py:215:23 [error] [invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Iterable[tuple[int | None, ...]]`, found `tuple[int | None, ...] | Sequence[tuple[int | None, ...]]`

scrapy (https://github.com/scrapy/scrapy)
- scrapy/core/downloader/handlers/http11.py:221:66 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ tests/test_spidermiddleware_base.py:55:55 [error] [invalid-argument-type] Argument to bound method `process_start_requests` is incorrect: Expected `Spider`, found `None`

setuptools (https://github.com/pypa/setuptools)
+ setuptools/_vendor/packaging/metadata.py:495:13 [error] [invalid-argument-type] Argument to bound method `append` is incorrect: Expected `str`, found `Message[str, str] | bytes | Any | str | list[Message[str, str] | str]`

sphinx (https://github.com/sphinx-doc/sphinx)
+ sphinx/ext/autodoc/_legacy_class_based/_documenters.py:2551:20 [error] [unresolved-attribute] Object of type `<super: <class 'RuntimeInstanceAttributeMixin'>, Self@import_object>` has no attribute `import_object`
+ sphinx/search/zh.py:18:12 [error] [unresolved-import] Cannot resolve imported module `jieba`
+ sphinx/util/docutils.py:95:23 [error] [unresolved-attribute] Module `docutils.parsers.rst.roles` has no member `_roles`
- sphinx/util/inspect.py:718:27 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ sphinx/util/parallel.py:46:19 [error] [missing-argument] No argument provided for required parameter 1
- sphinx/writers/html5.py:666:41 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

static-frame (https://github.com/static-frame/static-frame)
- static_frame/core/interface.py:1038:82 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

trio (https://github.com/python-trio/trio)
+ src/trio/_tests/test_socket.py:739:31 [error] [invalid-argument-type] Argument to function `res` is incorrect: Expected `tuple[str, int] | tuple[str, int, int] | tuple[str, int, int, int] | ... omitted 3 union elements`, found `tuple[Literal["1.2.3.4"], Literal[80], Literal[0], Literal[0], Literal[0]]`
+ src/trio/_core/_tests/test_ki.py:520:13 [error] [unresolved-attribute] Module `threading` has no member `_active`
- src/trio/_util.py:181:19 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ src/trio/testing/_check_streams.py:148:34 [error] [invalid-argument-type] Argument to bound method `receive_some` is incorrect: Expected `int | None`, found `float`

urllib3 (https://github.com/urllib3/urllib3)
+ src/urllib3/connection.py:269:66 [error] [unresolved-attribute] Object of type `Self@_tunnel` has no attribute `_method`
+ src/urllib3/util/ssl_.py:98:38 [error] [invalid-assignment] Object of type `Literal[2]` is not assignable to `Literal[_SSLMethod.PROTOCOL_SSLv23]`
- test/conftest.py:381:68 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- test/contrib/emscripten/test_emscripten.py:378:73 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive

werkzeug (https://github.com/pallets/werkzeug)
+ src/werkzeug/datastructures/structures.py:375:9 [error] [invalid-method-override] Invalid override of method `values`: Definition is incompatible with `dict.values`
+ src/werkzeug/routing/rules.py:779:44 [error] [unsupported-operator] Operator `+` is not supported between two objects of type `str | bytes | int | ... omitted 4 union elements`
+ tests/conftest.py:178:40 [error] [invalid-assignment] Object of type `HTTPResponse` is not assignable to `DataHTTPResponse`

xarray (https://github.com/pydata/xarray)
- xarray/core/_typed_ops.py:397:55 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
- xarray/namedarray/daskmanager.py:237:47 [warning] [unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ xarray/tests/test_groupby.py:992:9 [error] [unsupported-operator] Operator `+` is not supported between two objects of type `DatasetGroupBy`
+ xarray/tests/test_groupby.py:1514:30 [error] [invalid-argument-type] Argument to bound method `map` is incorrect: Expected `(...) -> DataArray`, found `Overload[[_ScalarT](a: _SupportsArray[dtype[_ScalarT]] | _NestedSequence[_SupportsArray[dtype[_ScalarT]]], dtype: None = None, order: Literal["K", "A", "C", "F"] | None = ..., *, device: Literal["cpu"] | None = ..., copy: bool | None = ..., like: _SupportsArrayFunc | None = ...) -> ndarray[tuple[Any, ...], dtype[_ScalarT]], [_ScalarT](a: Any, dtype: type[_ScalarT] | dtype[_ScalarT] | _HasDType[dtype[_ScalarT]] | _HasNumPyDType[dtype[_ScalarT]], order: Literal["K", "A", "C", "F"] | None = ..., *, device: Literal["cpu"] | None = ..., copy: bool | None = ..., like: _SupportsArrayFunc | None = ...) -> ndarray[tuple[Any, ...], dtype[_ScalarT]], (a: Any, dtype: type[Any] | dtype[Any] | _HasDType[dtype[Any]] | ... omitted 6 union elements = ..., order: Literal["K", "A", "C", "F"] | None = ..., *, device: Literal["cpu"] | None = ..., copy: bool | None = ..., like: _SupportsArrayFunc | None = ...) -> ndarray[tuple[Any, ...], dtype[Any]]]`

Full report with detailed diff (timing results)

@MichaReiser
Copy link
Copy Markdown
Member Author

The typing conformance regression is because of:

# The following type violation should be suppressed.
z: int = ""  # type: ignore[additional_stuff]

Where ty previously suppressed the error but now does no more. This matches mypy's behavior. IMO, the spec isn't clear if this should even be considered a type:ignore comment because it doesn't specify whether it's allowed that there's anything coming after type:ignore

@MichaReiser MichaReiser added the breaking Breaking API change label Mar 21, 2026
@MichaReiser MichaReiser force-pushed the micha/type-ignore-namespaced branch from b331fb8 to 3aa2144 Compare March 21, 2026 16:01
@MichaReiser
Copy link
Copy Markdown
Member Author

MichaReiser commented Mar 21, 2026

The ecosystem results are interesting and it shows two drawbacks of the new approach:

  • Many projects have type:ignore[code] comments that aren't needed for ty. They are no longer reported as unused
  • There are many cases where both ty and mypy require a suppression and a single type:ignore[code] comment is now no longer sufficient.

Specifically, I think it increases the churn for projects migrating from mypy to ty:

  1. There's no longer an automated fix to remove suppressions only needed by mypy (because ty doesn't report them as unused).
  2. Running ty on a mypy project means that users see many additional errors that also exist when using mypy, but were never surfaced because they're suppressed.

One option to fix 1) is to introduce a new rule with a fix that bans type:ignore comments or type:ignore coments with codes not prefixed with ty:. Another option is to add a respect-type-ignore-comments = "ignore-codes" option, which falls back to the old behavior.

The easiest way to fix 2) is to run ty check --add-ignore. However, it makes it more difficult to see what are new errors and which errors are the same as reported by mypy.

I do think the new approach is better for projects using multiple type checkers. But it certainly is a regression for projects migrating to ty.

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 21, 2026

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@MichaReiser MichaReiser force-pushed the micha/type-ignore-namespaced branch from 3aa2144 to d462221 Compare March 21, 2026 16:14
@MichaReiser MichaReiser marked this pull request as ready for review March 21, 2026 16:24
@AlexWaygood
Copy link
Copy Markdown
Member

I do think the new approach is better for projects using multiple type checkers. But it certainly is a regression for projects migrating to ty.

I'm actually not sure I agree. Fine-grained ignore comments for mypy such as # type: ignore[arg-type] are a somewhat advanced feature. Users are only likely to employ this kind of feature if they are interested in strict typing generally, and only want to ignore very specific kinds of errors on a certain line. I think these users would probably find it unwelcome if ty silently interpreted these fine-grained ignore comments they already had for mypy as very broad ignore comments that for ty would suppress all errors. This change increases the churn for those users, but I think they would probably not see it as a regression: I think they'd welcome the fact that they are now forced to specify exactly which ty error codes they want to ignore.

@AlexWaygood
Copy link
Copy Markdown
Member

Mypy has a nice feature where it points out that you have a type: ignore comment on a line but the type error isn't covered by the specified error code:

~/dev/ruff (micha/type-ignore-namespaced)⚡ % cat foo.py                                                                  
x: int = "foo"  # type: ignore[not-a-mypy-code]
~/dev/ruff (micha/type-ignore-namespaced)⚡ % uvx mypy foo.py --pretty        
foo.py:1: error: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
    x: int = "foo"  # type: ignore[not-a-mypy-code]
             ^~~~~
foo.py:1: note: Error code "assignment" not covered by "type: ignore" comment
Found 1 error in 1 file (checked 1 source file)
image

I wonder if we could offer a similar subdiagnostic? Doesn't need to be done in this PR, of course

@MichaReiser
Copy link
Copy Markdown
Member Author

MichaReiser commented Mar 21, 2026

I'm actually not sure I agree. Fine-grained ignore comments for mypy such as # type: ignore[arg-type] are a somewhat advanced feature.

I agree with this. The way I would migrate such a project today is to set respect-type-ignore-comments = false, run --add-ignore, unset respect-type-ignore-comments, and then run --fix (future). This will no longer work because we always ignore unnamespaces codes in type:ignore

I wonder if we could offer a similar subdiagnostic? Doesn't need to be done in this PR, of course

Yeah, I saw that. It's pretty cool, but feels a bit out of scope because it equally applies to ty:ignore comments.

Copy link
Copy Markdown
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! We'll want to document this, but I guess that will be a separate PR to ty repo.

|
1 |
- b = a / 0 # type:ignore[mypy-code]
2 + b = a / 0 # type:ignore[mypy-code] # ty:ignore[unresolved-reference]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this be somewhat nicer here?

Suggested change
2 + b = a / 0 # type:ignore[mypy-code] # ty:ignore[unresolved-reference]
2 + b = a / 0 # type:ignore[mypy-code, ty:unresolved-reference]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly about this.

The reason I decided to insert a ty:ignore for now is that ty:ignore is our recommended suppression style, and it's unclear to me if all projects are fine with using type:ignore for both ty and mypy suppressions (over type:ignore for mypy and ty:ignore for ty).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way is fine with me. I'm halfway tempted to change our recommended style to # type: ignore[ty:code] instead of # ty: ignore[code], except that it is more verbose. Feels like the former is better for multi-type-checker libraries, the latter for ty-only applications. And I agree that auto-ignore can't really tell which one the project is.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to keep recommending ty: ignore[code]. There are differences between type checkers on which lines are suppressed if you add a type: ignore (is it the starting line only? Is it the entire logical line? is it start and end lines?). I also want to introduce an own-line ty:ignore in the future, something we can't add support for when using type: ignore

File level suppression comments are currently intentionally unsupported because we've yet to decide
if they should use a different syntax that also supports enabling rules or changing the rule's
severity: `ty: possibly-undefined-reference=error`
File level suppression comments suppress all errors in a file with a given code.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We previously supported this only for # type: ignore comments, and now we support it also for # ty: ignore? Makes sense.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly

@carljm
Copy link
Copy Markdown
Contributor

carljm commented Mar 23, 2026

The ecosystem results are interesting and it shows two drawbacks of the new approach:

* Many projects have `type:ignore[code]` comments that aren't needed for ty. They are no longer reported as unused

* There are many cases where both ty and mypy require a suppression and a single `type:ignore[code]` comment is now no longer sufficient.

I see both of these as improvements, not drawbacks. The first makes it much easier to add ty to an existing codebase that will continue to use mypy for now. And the second means that we are no longer blindly suppressing type errors without knowing whether the error is actually the one the user intended to suppress.

It seems to me the only remaining gap after this PR is that it would be good to have an opt-in (auto-fixable) rule that errors on any type: ignore with non-ty code (as you mentioned). I think this approach, with that rule, covers all use cases well.

@MichaReiser MichaReiser merged commit 84ff94b into main Mar 24, 2026
49 checks passed
@MichaReiser MichaReiser deleted the micha/type-ignore-namespaced branch March 24, 2026 08:33
charliermarsh pushed a commit to astral-sh/ty that referenced this pull request Mar 24, 2026
## Summary

Update the documentation to reflect the changes introduced in
astral-sh/ruff#24096
carljm added a commit that referenced this pull request Mar 25, 2026
* main:
  [ty] Avoid eager TypedDict diagnostics in `TypedDict | dict` unions (#24151)
  `F507`: Fix false negative for non-tuple RHS in `%`-formatting (#24142)
  [ty] Update `SpecializationBuilder` hook to get both lower/upper bounds (#23848)
  Fix `%foo?` parsing in IPython assignment expressions (#24152)
  `E501`/`W505`/formatter: Exclude nested pragma comments from line width calculation  (#24071)
  [ty] Fix Salsa panic propagation (#24141)
  [ty] Support `type:ignore[ty:code]` suppressions (#24096)
  [ty] Support narrowing for extended walrus targets (#24129)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaking API change ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop treating type: ignore[some-code] as a blanket type: ignore Allow to disable a specific rule for a whole file

3 participants