Back to Ty

Coming from mypy or pyright

docs/coming-from-mypy-or-pyright.md

0.0.7490.6 KB
Original Source

Coming from mypy or pyright

This guide helps you migrate a project from mypy or pyright to ty.

Migration tips

  • mypy disables an error code with # type: ignore[code]; pyright suppresses a single line with # pyright: ignore[reportXyz]; ty's equivalent is # ty: ignore[rule]. See this page for more information about suppression comments.
  • mypy's disable_error_code and pyright's reportXyz = "none" both correspond to setting <rule> = "ignore" under [tool.ty.rules]. See this section for details.
  • Severities in ty are ignore, warn, error. Pyright's "information" level and basedpyright's "hint" level have no direct ty equivalent — use warn for both.
  • If you are looking for the equivalent of disallow_untyped_defs / no-untyped-def (mypy) or reportMissingParameterType, reportUnknownParameterType (pyright), check out this FAQ entry.
  • Unlike mypy, ty checks the bodies of unannotated functions unconditionally, so there is no ty rule corresponding to mypy's check_untyped_defs setting. The equivalent pyright setting is analyzeUnannotatedFunctions = true.

Stricter checking with ty

For both mypy and pyright, "strict" mode enables several error codes that are otherwise disabled by default, but also makes fundamental changes to the way type inference and type checking works. Mypy's strict mode includes --check-untyped-defs, for example, without which unannotated functions are left unchecked; pyright's strict mode includes strictListInference, without which [1, "foo"] will be inferred as having type list[Unknown] rather than list[int | str] or similar.

ty's default mode is currently stricter by default than either mypy or pyright in many ways. ty does not have flags such as --check-untyped-defs or strictListInference, because these are ty's default behaviour and are not currently configurable. Meanwhile, nearly all ty rules are enabled by default, and the ones that are disabled by default are usually in that category because they are either very opinionated or have many false positives.

To enable all ty rules at once with the error severity, you can simply use --error=all, but we wouldn't recommend it. Instead, you can currently approximate something similar to the --strict mode of other type checkers with the following configuration:

toml
[tool.ty.rules]
dynamic-function-decorator-return = "error"
missing-type-argument = "error"
possibly-unresolved-reference = "warn"
unsound-return-statement = "error"

[tool.ruff.lint]
extend-select = ["ANN", "PYI"]
preview = true

This configuration:

An even stricter configuration -- that goes beyond what mypy and pyright check for in their default --strict mode in several respects -- might look like this:

toml
[tool.ty.rules]
blanket-ignore-comment = "error"
dynamic-function-decorator-return = "error"
missing-type-argument = "error"
possibly-unresolved-reference = "warn"
unsound-assignment = "error"
unsound-return-statement = "error"
unsound-yield = "error"
unsupported-dynamic-base = "warn"

# NOTE: the following rules are known to have a significant number of false positives,
# which is mostly unavoidable. Enable them at your own risk!
division-by-zero = "warn"
possibly-missing-attribute = "warn"
possibly-missing-import = "warn"

[tool.ty.analysis]
strict-literal-narrowing = true
strict-generic-narrowing = true

[tool.ruff.lint]
extend-select = ["ANN", "PYI", "PGH003"]
preview = true

Note that several checks in mypy and pyright are not yet implemented in ty. See the rule mapping table below for more details.

Mapping pyright/mypy rules to ty/Ruff rules

How to read this table

  • ty or Ruff rule: the canonical name, as listed in Rules if it is a ty rule. Configure ty rules under [tool.ty.rules]. Where Ruff provides equivalent coverage for a check that has no ty rule, the relevant Ruff rule or rule group is linked instead.
  • Mypy error code: the value passed to # type: ignore[<code>] or disable_error_code. Some ty rules surface as one of mypy's catch-all codes (misc, assignment, valid-type); these mappings are deliberately broad.
  • Pyright diagnostic: the report* setting in pyrightconfig.json or [tool.pyright].

A diagnostic may appear in multiple rows when it covers distinct cases. A blank cell means no direct equivalent exists in that checker (the diagnostic is either not emitted, or is folded into a broader category that already appears for another ty rule).

Rules

ty or Ruff ruleMypy error codePyright or basedpyright diagnostic
abstract-and-final-methodmisc
abstract-method-in-final-classmiscreportGeneralTypeIssues
call-abstract-methodreportAbstractUsage
call-non-callableoperator
miscreportCallIssue
reportOptionalCall
conflicting-declarationsno-redefreportRedeclaration
conflicting-metaclassmetaclassreportGeneralTypeIssues
cyclic-class-definitionmiscreportGeneralTypeIssues
dataclass-field-ordermiscreportGeneralTypeIssues
deprecateddeprecatedreportDeprecated
division-by-zero
duplicate-basemiscreportGeneralTypeIssues
duplicate-kw-onlymisc
dynamic-function-decorator-returnuntyped-decoratorreportUntypedFunctionDecorator (Unknown returns only)
empty-bodyempty-bodyreportReturnType (... bodies are exempt)
final-on-non-methodmiscreportGeneralTypeIssues
final-without-valuemiscreportGeneralTypeIssues
inconsistent-mromiscreportGeneralTypeIssues
index-out-of-boundsmiscreportGeneralTypeIssues
invalid-argument-typearg-type
index
type-var
typeddict-itemreportArgumentType
reportAssignmentType
invalid-assignmentassignment
list-item
dict-itemreportAssignmentType
invalid-assignment (incompatible method replacements only)method-assign (rejects all method assignments)reportAttributeAccessIssue (incompatible replacements only)
invalid-assignment (TypedDict item values)typeddict-itemreportGeneralTypeIssues
invalid-assignment (read-only TypedDict keys)typeddict-readonly-mutatedreportTypedDictNotRequiredAccess (read-only mutations only)
invalid-attribute-accessmiscreportAttributeAccessIssue
invalid-attribute-overridemiscreportIncompatibleVariableOverride (class/instance variables only)
invalid-awaitmiscreportGeneralTypeIssues
invalid-basevalid-type
miscreportGeneralTypeIssues
invalid-context-managermisc
attr-defined
union-attrreportGeneralTypeIssues
reportOptionalContextManager
invalid-dataclassmisc
invalid-exception-caughtmiscreportGeneralTypeIssues
invalid-explicit-overridemiscreportGeneralTypeIssues
invalid-frozen-dataclass-subclassmiscreportGeneralTypeIssues
invalid-keytypeddict-item
typeddict-unknown-keyreportGeneralTypeIssues
reportAssignmentType
reportCallIssue
invalid-legacy-type-variablemisc
valid-typereportGeneralTypeIssues
reportInvalidTypeForm
invalid-metaclassmetaclass
invalid-method-overrideoverridereportIncompatibleMethodOverride
invalid-module-getattr-call
invalid-newtypevalid-newtype
miscreportGeneralTypeIssues
reportArgumentType
invalid-overloadno-overload-impl
miscreportNoOverloadImplementation
reportInconsistentOverload
invalid-parameter-defaultassignmentreportArgumentType
invalid-protocolmiscreportGeneralTypeIssues
invalid-raisemiscreportGeneralTypeIssues
invalid-return-typereturn
return-valuereportReturnType
invalid-type-argumentsmisc
type-varreportInvalidTypeArguments
invalid-type-formvalid-typereportInvalidTypeForm
reportGeneralTypeIssues
invalid-type-guard-definitionnarrowed-type-not-subtype
valid-typereportGeneralTypeIssues
invalid-type-variable-boundvalid-type
miscreportGeneralTypeIssues
invalid-type-variable-constraintsvalid-type
miscreportGeneralTypeIssues
invalid-type-variable-defaultmiscreportGeneralTypeIssues
invalid-typed-dict-fieldmiscreportIncompatibleVariableOverride
invalid-yieldmiscreportReturnType
isinstance-against-protocolmiscreportArgumentType
reportGeneralTypeIssues
isinstance-against-typed-dictmiscreportArgumentType
reportGeneralTypeIssues
mismatched-type-namename-match
miscreportGeneralTypeIssues
missing-argumentcall-argreportCallIssue
missing-override-decoratorexplicit-overridereportImplicitOverride
missing-type-argumenttype-argreportMissingTypeArgument
missing-typed-dict-keytypeddict-itemreportAssignmentType
no-matching-overloadcall-overloadreportCallIssue
not-iterablemisc
attr-defined
union-attrreportGeneralTypeIssues
reportOptionalIterable
not-subscriptableindexreportIndexIssue
reportOptionalSubscript
override-of-final-methodmiscreportIncompatibleMethodOverride
override-of-final-variablemiscreportGeneralTypeIssues
parameter-already-assignedmisc
call-argreportCallIssue
positional-only-parameter-as-kwargcall-argreportCallIssue
possibly-missing-attribute
possibly-unresolved-referencepossibly-undefinedreportPossiblyUnboundVariable
redundant-castredundant-castreportUnnecessaryCast
subclass-of-final-classmiscreportGeneralTypeIssues
too-many-positional-argumentscall-argreportCallIssue
type-assertion-failureassert-typereportAssertTypeFailure
unbound-type-variablevalid-typereportGeneralTypeIssues
undefined-revealunimported-reveal
unknown-argumentcall-argreportCallIssue
unresolved-attributeattr-defined
union-attrreportAttributeAccessIssue
reportFunctionMemberAccess
reportOptionalMemberAccess
unresolved-importimport-not-foundreportMissingImports
unresolved-reference, Ruff F823name-defined
used-before-defreportUndefinedVariable
reportUnboundVariable
unsound-assignment (variables only)
unsound-return-statementno-any-return
unsound-yield
unsupported-operatoroperatorreportOperatorIssue
reportOptionalOperand
unused-awaitable (native coroutines only)unused-coroutine
unused-awaitablereportUnusedCoroutine
unused-ignore-commentunused-ignorereportUnnecessaryTypeIgnoreComment
unused-type-ignore-commentunused-ignorereportUnnecessaryTypeIgnoreComment
blanket-ignore-comment, Ruff PGH003ignore-without-codereportIgnoreCommentWithoutRule (basedpyright only)
None yet for instantiating abstract classesabstractreportAbstractUsage
No direct equivalent planned for passing abstract classes where concrete classes are requiredtype-abstract
None yet for calling abstract methods through super()safe-superreportAbstractUsage
Ruff F631reportAssertAlwaysTrue
Ruff B006
Ruff B008 (partial coverage; immutable annotations and calls are excluded)reportCallInDefaultInitializer
None yet (tracked in Ruff #10137)reportConstantRedefinition
Ruff F811
Ruff I001 (partial coverage; separate import blocks may be missed)reportDuplicateImport
Ruff ISC001
Ruff ISC002reportImplicitStringConcatenation
None yet (tracked in #3647)reportImportCycles
None yet for mutable attribute types (tracked in #2158)mutable-overridereportIncompatibleVariableOverride
None yetreportIncompleteStub
None yet (tracked in #3651)reportInconsistentConstructor
Ruff W605reportInvalidStringEscapeSequence
Ruff PYI010
Ruff PYI017
Ruff PYI048
Ruff PYI052reportInvalidStubStatement
None yet (tracked in #1017, #3636, #3637)type-varreportInvalidTypeVarUse
None yet (tracked in #1060)exhaustive-matchreportMatchNotExhaustive
None yet (tracked in #1577)reportMissingModuleSource
None yet (tracked in #3652)reportMissingSuperCall
None yet (tracked in #3638)import-untypedreportMissingTypeStubs
None yet (tracked in #103)overload-cannot-match
overload-overlapreportOverlappingOverload
None yet (tracked in #200)attr-defined
(extended by --no-implicit-reexport)reportPrivateImportUsage
reportPrivateLocalImportUsage (basedpyright only)
Ruff SLF001
Ruff PLC2701 (partial coverage; PLC2701 requires preview)reportPrivateUsage
None yet (tracked in #3633)reportPropertyTypeMismatch
Ruff N804
Ruff N805reportSelfClsParameterName
Ruff PYI033 (.py files require preview)reportTypeCommentUsage
None yet for accessing non-required keys (tracked in #2810)reportTypedDictNotRequiredAccess
None yet (tracked in #3781)reportUnhashable
None yet (tracked in #2954)reportUninitializedInstanceVariable
None yetreportUnknownArgumentType
reportUnknownLambdaType
reportUnknownMemberType
None yetvar-annotated
None yetreportUnknownVariableType
None yet (tracked in #576)comparison-overlapreportUnnecessaryComparison
reportUnnecessaryContains
None yetreportUnnecessaryIsInstance
None yet (tracked in #1948)unreachablereportUnreachable
Ruff F822
Ruff PLE0604
Ruff PLE0605
Ruff PYI056reportUnsupportedDunderAll
None yetreportUntypedBaseClass
reportUntypedClassDecorator
Ruff PYI024reportUntypedNamedTuple
None yetreportUnusedClass
reportUnusedFunction
None yetreportUnusedCallResult
Ruff ARG rulesreportUnusedParameter (basedpyright only)
Ruff B025 (duplicate exception handlers only; other cases tracked in #3701)reportUnusedExcept
Ruff B015
Ruff B018reportUnusedExpression
Ruff F401reportUnusedImport
Ruff F841 (function-local variables only)reportUnusedVariable
Ruff F403reportWildcardImportFromLibrary
Ruff ANN401 (function annotations only)explicit-anyreportExplicitAny (basedpyright only)
None yetfunc-returns-value
None yetno-any-unimported
None yetno-untyped-call
None yettruthy-functionreportUnnecessaryComparison
None yettruthy-iterable
Ruff ANN rulesno-untyped-defreportMissingParameterType
reportUnknownParameterType

The full list of ty rules — including those without a direct equivalent above — is in Rules. Contributions to extend this mapping are welcome via pull request to the ty repository; see issue #2111 for context.