From 13678bdc25ee6670ca54d7c7cb12d5d605256c56 Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Thu, 7 Nov 2024 12:48:16 -0500 Subject: [PATCH 01/16] add content from typeshed/CONTRIBUTING.md --- docs/guides/writing_stubs.rst | 162 ++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 17 deletions(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 71d13a71b..5f0e5d760 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -93,6 +93,15 @@ Liskov substitutability or detecting problematic overloads. It may be instructive to examine `typeshed `__'s `setup for testing stubs `__. +To suppress type errors on stubs: +* use mypy error codes for mypy-specific `# type: ignore` annotations, + e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. +* use pyright error codes for pyright-specific suppressions, + e.g. `# pyright: ignore[reportGeneralTypeIssues]`. + - pyright is configured to discard `# type: ignore` annotations. + If you need both on the same line, mypy's annotation needs to go first, + e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`. + .. TODO: consider adding examples and configurations for specific type checkers @@ -113,18 +122,6 @@ Stub Content This section documents best practices on what elements to include or leave out of stub files. -Modules excluded fom stubs --------------------------- - -Not all modules should be included in stubs. - -It is recommended to exclude: - -1. Implementation details, with `multiprocessing/popen_spawn_win32.py `_ as a notable example -2. Modules that are not supposed to be imported, such as ``__main__.py`` -3. Protected modules that start with a single ``_`` char. However, when needed protected modules can still be added (see :ref:`undocumented-objects` section below) -4. Tests - Public Interface ---------------- @@ -138,9 +135,17 @@ The following should always be included: * All objects included in ``__all__`` (if present). Other objects may be included if they are not prefixed with an underscore -or if they are being used in practice. (See the next section.) +or if they are being used in practice. + +Modules excluded from stubs +--------------------------- -.. _undocumented-objects: +The following should not be included in stubs: + +1. Implementation details, with `multiprocessing/popen_spawn_win32.py `_ as a notable example +2. Modules that are not supposed to be imported, such as ``__main__.py`` +3. Protected modules that start with a single ``_`` char. However, when needed protected modules can still be added (see :ref:`undocumented-objects` section below) +4. Tests Undocumented Objects -------------------- @@ -417,6 +422,28 @@ and the :ref:`best-practices`. There are a few exceptions, outlined below, that different structure of stub files into account and aim to create more concise files. +Syntax Example +-------------- + +The below is an excerpt from the types for the `datetime` module. + + MAXYEAR: int + MINYEAR: int + + class date: + def __new__(cls, year: SupportsIndex, month: SupportsIndex, day: SupportsIndex) -> Self: ... + @classmethod + def fromtimestamp(cls, timestamp: float, /) -> Self: ... + @classmethod + def today(cls) -> Self: ... + @classmethod + def fromordinal(cls, n: int, /) -> Self: ... + @property + def year(self) -> int: ... + def replace(self, year: SupportsIndex = ..., month: SupportsIndex = ..., day: SupportsIndex = ...) -> Self: ... + def ctime(self) -> str: ... + def weekday(self) -> int: ... + Maximum Line Length ------------------- @@ -448,14 +475,14 @@ No:: def time_func() -> None: ... - def date_func() -> None: ... # do no leave unnecessary empty lines + def date_func() -> None: ... # do not leave unnecessary empty lines def ip_func() -> None: ... class Foo: # leave only one empty line above x: int - class MyError(Exception): ... # leave an empty line between the classes + class MyError(Exception): ... Module Level Attributes ----------------------- @@ -575,6 +602,14 @@ No:: ... def to_int3(x: str) -> int: pass +Avoid invariant collection types (`list`, `dict`) for function parameters, +in favor of covariant types like `Mapping` or `Sequence`. + +Avoid union return types. See https://github.com/python/mypy/issues/1693 + +Use `float` instead of `int | float` for parameter annotations. +See [PEP 484](https://peps.python.org/pep-0484/#the-numeric-tower). + Language Features ----------------- @@ -604,6 +639,14 @@ No:: class OtherClass: ... +Use variable annotations instead of type comments, even for stubs that target +older versions of Python. + +Platform-dependent APIs +----------------------- + +Use platform checks like `if sys.platform == 'win32'` to denote platform-dependent APIs. + NamedTuple and TypedDict ------------------------ @@ -631,7 +674,7 @@ No:: Built-in Generics ----------------- -:pep:`585` built-in generics are supported and should be used instead +:pep:`585` built-in generics (such as `list`, `dict`, `tuple`, `set`) are supported and should be used instead of the corresponding types from ``typing``:: from collections import defaultdict @@ -654,3 +697,88 @@ all type checkers:: def foo(x: int | str) -> int | None: ... # recommended def foo(x: Union[int, str]) -> Optional[int]: ... # ok + +Using `Any` and `object` +------------------------ + +When adding type hints, avoid using the `Any` type when possible. Reserve +the use of `Any` for when: +* the correct type cannot be expressed in the current type system; and +* to avoid union returns (see above). + +Note that `Any` is not the correct type to use if you want to indicate +that some function can accept literally anything: in those cases use +`object` instead. + +When using `Any`, document the reason for using it in a comment. Ideally, +document what types could be used. + +Context Managers +---------------- + +When adding type annotations for context manager classes, annotate +the return type of `__exit__` as bool only if the context manager +sometimes suppresses exceptions -- if it sometimes returns `True` +at runtime. If the context manager never suppresses exceptions, +have the return type be either `None` or `bool | None`. If you +are not sure whether exceptions are suppressed or not or if the +context manager is meant to be subclassed, pick `bool | None`. +See https://github.com/python/mypy/issues/7214 for more details. + +`__enter__` methods and other methods that return instances of the +current class should be annotated with `typing_extensions.Self` +([example](https://github.com/python/typeshed/blob/3581846/stdlib/contextlib.pyi#L151)). + +Naming +------ + +Type variables and aliases you introduce purely for legibility reasons +should be prefixed with an underscore to make it obvious to the reader +they are not part of the stubbed API. + +A few guidelines for protocol names below. In cases that don't fall +into any of those categories, use your best judgement. + +* Use plain names for protocols that represent a clear concept + (e.g. `Iterator`, `Container`). +* Use `SupportsX` for protocols that provide callable methods (e.g. + `SupportsInt`, `SupportsRead`, `SupportsReadSeek`). +* Use `HasX` for protocols that have readable and/or writable attributes + or getter/setter methods (e.g. `HasItems`, `HasFileno`). + +`@deprecated` +------------- + +The `@typing_extensions.deprecated` decorator (`@warnings.deprecated` +since Python 3.13) can be used to mark deprecated functionality; see +[PEP 702](https://peps.python.org/pep-0702/). + +A few guidelines for how to use it: + +* In the standard library, apply the decorator only in Python versions + where an appropriate replacement for the deprecated functionality + exists. If in doubt, apply the decorator only on versions where the + functionality has been explicitly deprecated, either through runtime + warnings or in the documentation. Use `if sys.version_info` checks to + apply the decorator only to some versions. +* Keep the deprecation message concise, but try to mention the projected + version when the functionality is to be removed, and a suggested + replacement. + +Imports +------- + +Imports in stubs are considered private (not part of the exported API) +unless: +* they use the form ``from library import name as name`` (sic, using + explicit ``as`` even if the name stays the same); or +* they use the form ``from library import *`` which means all names + from that library are exported. + +Forward References +------------------ + +Stub files support forward references natively. In other words, the +order of class declarations and type aliases does not matter in +a stub file. Unlike regular Python files, you can use the name of the class within its own +body without writing it as a comment. From 9db1d7c36f9a786f3fc42274f5e8a89e24a4ff6d Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Thu, 7 Nov 2024 12:54:48 -0500 Subject: [PATCH 02/16] fix build errors --- docs/guides/writing_stubs.rst | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 5f0e5d760..c3e940711 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -94,13 +94,9 @@ It may be instructive to examine `typeshed `setup for testing stubs `__. To suppress type errors on stubs: -* use mypy error codes for mypy-specific `# type: ignore` annotations, - e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. -* use pyright error codes for pyright-specific suppressions, - e.g. `# pyright: ignore[reportGeneralTypeIssues]`. - - pyright is configured to discard `# type: ignore` annotations. - If you need both on the same line, mypy's annotation needs to go first, - e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`. +* use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. +* use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`. Pyright is configured to discard `# type: ignore` annotations. +* If you need both on the same line, mypy's annotation needs to go first, e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`. .. TODO: consider adding examples and configurations for specific type checkers @@ -147,6 +143,8 @@ The following should not be included in stubs: 3. Protected modules that start with a single ``_`` char. However, when needed protected modules can still be added (see :ref:`undocumented-objects` section below) 4. Tests +.. _undocumented-objects: + Undocumented Objects -------------------- @@ -532,7 +530,7 @@ Yes:: class Color(Enum): # An assignment with no type annotation is a convention used to indicate - # an enum member. + # an enum member. RED = 1 No:: @@ -770,10 +768,8 @@ Imports Imports in stubs are considered private (not part of the exported API) unless: -* they use the form ``from library import name as name`` (sic, using - explicit ``as`` even if the name stays the same); or -* they use the form ``from library import *`` which means all names - from that library are exported. +* they use the form ``from library import name as name`` (sic, using explicit ``as`` even if the name stays the same); or +* they use the form ``from library import *`` which means all names from that library are exported. Forward References ------------------ From cfc2827a630973e8624235f2c3339716856c5b11 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Fri, 6 Dec 2024 21:26:40 -0500 Subject: [PATCH 03/16] address review comments --- docs/guides/writing_stubs.rst | 46 +++++++++++------------------------ 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 828db6e1a..cebb52226 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -93,10 +93,8 @@ Liskov substitutability or detecting problematic overloads. It may be instructive to examine `typeshed `__'s `setup for testing stubs `__. -To suppress type errors on stubs: -* use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. -* use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`. Pyright is configured to discard `# type: ignore` annotations. -* If you need both on the same line, mypy's annotation needs to go first, e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`. +To suppress type errors on stubs, use a `# type: ignore` comment. Refer to the style guide for +error suppression formats specific to individual typecheckers. .. TODO: consider adding examples and configurations for specific type checkers @@ -538,7 +536,7 @@ No:: class Foo: # leave only one empty line above x: int - class MyError(Exception): ... + class MyError(Exception): ... # leave an empty line between the classes Module Level Attributes ----------------------- @@ -802,6 +800,14 @@ into any of those categories, use your best judgement. * Use `HasX` for protocols that have readable and/or writable attributes or getter/setter methods (e.g. `HasItems`, `HasFileno`). +Type Checker Error Suppression formats +-------------------------------------- + +* Use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. +* Use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`. Pyright is configured to discard `# type: ignore` annotations. +* If you need both on the same line, mypy's annotation needs to go first, e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`. + + `@deprecated` ------------- @@ -809,30 +815,6 @@ The `@typing_extensions.deprecated` decorator (`@warnings.deprecated` since Python 3.13) can be used to mark deprecated functionality; see [PEP 702](https://peps.python.org/pep-0702/). -A few guidelines for how to use it: - -* In the standard library, apply the decorator only in Python versions - where an appropriate replacement for the deprecated functionality - exists. If in doubt, apply the decorator only on versions where the - functionality has been explicitly deprecated, either through runtime - warnings or in the documentation. Use `if sys.version_info` checks to - apply the decorator only to some versions. -* Keep the deprecation message concise, but try to mention the projected - version when the functionality is to be removed, and a suggested - replacement. - -Imports -------- - -Imports in stubs are considered private (not part of the exported API) -unless: -* they use the form ``from library import name as name`` (sic, using explicit ``as`` even if the name stays the same); or -* they use the form ``from library import *`` which means all names from that library are exported. - -Forward References ------------------- - -Stub files support forward references natively. In other words, the -order of class declarations and type aliases does not matter in -a stub file. Unlike regular Python files, you can use the name of the class within its own -body without writing it as a comment. +Keep the deprecation message concise, but try to mention the projected +version when the functionality is to be removed, and a suggested +replacement. From 5c33d201ee1d5c1be56dbc3271540a973186006d Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Tue, 10 Dec 2024 06:57:35 -0800 Subject: [PATCH 04/16] Update docs/guides/writing_stubs.rst Co-authored-by: Rebecca Chen --- docs/guides/writing_stubs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index cebb52226..cb092bb98 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -93,7 +93,7 @@ Liskov substitutability or detecting problematic overloads. It may be instructive to examine `typeshed `__'s `setup for testing stubs `__. -To suppress type errors on stubs, use a `# type: ignore` comment. Refer to the style guide for +To suppress type errors in stubs, use `# type: ignore` comments. Refer to the style guide for error suppression formats specific to individual typecheckers. .. From d61da81c687096d123d29541f9789354fb4b14fd Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Tue, 10 Dec 2024 06:57:41 -0800 Subject: [PATCH 05/16] Update docs/guides/writing_stubs.rst Co-authored-by: Rebecca Chen --- docs/guides/writing_stubs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index cb092bb98..60b938dfa 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -804,7 +804,7 @@ Type Checker Error Suppression formats -------------------------------------- * Use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. -* Use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`. Pyright is configured to discard `# type: ignore` annotations. +* Use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`. * If you need both on the same line, mypy's annotation needs to go first, e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`. From f253587dad59c3f1462ceb20117ae6000d9259be Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Wed, 11 Dec 2024 08:35:23 -0800 Subject: [PATCH 06/16] add Incomplete vs Any, the Any trick sections; add reference to error suppression formats section --- docs/guides/writing_stubs.rst | 57 ++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 60b938dfa..fa20a76fe 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -93,7 +93,7 @@ Liskov substitutability or detecting problematic overloads. It may be instructive to examine `typeshed `__'s `setup for testing stubs `__. -To suppress type errors in stubs, use `# type: ignore` comments. Refer to the style guide for +To suppress type errors in stubs, use `# type: ignore` comments. Refer to the :ref:`type-checker-error-suppression` section of the style guide for error suppression formats specific to individual typecheckers. .. @@ -213,6 +213,20 @@ to use them freely to describe simple structural types. Incomplete Stubs ---------------- +When writing new stubs, it is not necessary to fully annotate all arguments, +return types, and fields. Some items may be left unannotated or +annotated with `_typeshed.Incomplete` (`documentation `_).:: + + from _typeshed import Incomplete + + field: Incomplete # unannotated + + def foo(x): ... # unannotated argument and return type + +`Incomplete` can also be used for partially known types:: + + def foo(x: Incomplete | None = None) -> list[Incomplete]: ... + Partial stubs can be useful, especially for larger packages, but they should follow the following guidelines: @@ -220,8 +234,7 @@ follow the following guidelines: can be left unannotated. * Do not use ``Any`` to mark unannotated or partially annotated values. Leave function parameters and return values unannotated. In all other cases, use - ``_typeshed.Incomplete`` - (`documentation `_):: + ``_typeshed.Incomplete``:: from _typeshed import Incomplete @@ -254,6 +267,40 @@ annotated function ``bar()``:: def bar(x: str, y, *, z=...): ... +`Any` vs. `Incomplete` +---------------------- + +While `Incomplete` is a type alias of `Any`, they serve difference purposes: +`Incomplete` is a placeholder where a proper type might be substituted. +It's a "to do" item and should be replaced if possible. `Any` is used when +it's not possible to accurately type an item using the current type system. +It should be used sparingly. + +The `Any` trick +--------------- + +In cases where a function or method can return `None`, but where forcing the +user to explicitly check for `None` can be detrimental, use +`_typeshed.MaybeNone` (an alias to `Any`), instead of `None`. + +Consider the following (simplified) signature of `re.Match[str].group`:: + + class Match: + def group(self, group: str | int, /) -> str | MaybeNone: ... + +This avoid forcing the user to check for `None`:: + + match = re.fullmatch(r"\d+_(.*)", some_string) + assert match is not None + name_group = match.group(1) # The user knows that this will never be None + return name_group.uper() # This typo will be flagged by the type checker + +In this case, the user of `match.group()` must be prepared to handle a `str`, +but type checkers are happy with `if name_group is None` checks, because we're +saying it can also be something else than an `str`. + +This is sometimes called "the Any trick". + Attribute Access ---------------- @@ -800,7 +847,9 @@ into any of those categories, use your best judgement. * Use `HasX` for protocols that have readable and/or writable attributes or getter/setter methods (e.g. `HasItems`, `HasFileno`). -Type Checker Error Suppression formats +:: _type-checker-error-suppression: + +Type Checker Error Suppression Formats -------------------------------------- * Use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. From 076d101dac14ef2b2973055472b8da5ec900abe8 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Wed, 11 Dec 2024 08:37:50 -0800 Subject: [PATCH 07/16] fix ref --- docs/guides/writing_stubs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index fa20a76fe..4fdf71451 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -847,7 +847,7 @@ into any of those categories, use your best judgement. * Use `HasX` for protocols that have readable and/or writable attributes or getter/setter methods (e.g. `HasItems`, `HasFileno`). -:: _type-checker-error-suppression: +.. _type-checker-error-suppression: Type Checker Error Suppression Formats -------------------------------------- From 0cc549ecd7c022e3a06918e12f2e928f61166575 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 12 Dec 2024 09:13:33 -0500 Subject: [PATCH 08/16] format and fix link --- docs/guides/writing_stubs.rst | 70 ++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 4fdf71451..88fa801d2 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -215,7 +215,7 @@ Incomplete Stubs When writing new stubs, it is not necessary to fully annotate all arguments, return types, and fields. Some items may be left unannotated or -annotated with `_typeshed.Incomplete` (`documentation `_).:: +annotated with ``_typeshed.Incomplete`` (`documentation `_):: from _typeshed import Incomplete @@ -223,7 +223,7 @@ annotated with `_typeshed.Incomplete` (`documentation list[Incomplete]: ... @@ -267,39 +267,16 @@ annotated function ``bar()``:: def bar(x: str, y, *, z=...): ... -`Any` vs. `Incomplete` ----------------------- +``Any`` vs. ``Incomplete`` +-------------------------- -While `Incomplete` is a type alias of `Any`, they serve difference purposes: -`Incomplete` is a placeholder where a proper type might be substituted. -It's a "to do" item and should be replaced if possible. `Any` is used when -it's not possible to accurately type an item using the current type system. -It should be used sparingly. +While ``Incomplete`` is a type alias of ``Any``, they serve difference purposes: +``Incomplete`` is a placeholder where a proper type might be substituted. +It's a "to do" item and should be replaced if possible. -The `Any` trick ---------------- - -In cases where a function or method can return `None`, but where forcing the -user to explicitly check for `None` can be detrimental, use -`_typeshed.MaybeNone` (an alias to `Any`), instead of `None`. - -Consider the following (simplified) signature of `re.Match[str].group`:: - - class Match: - def group(self, group: str | int, /) -> str | MaybeNone: ... - -This avoid forcing the user to check for `None`:: - - match = re.fullmatch(r"\d+_(.*)", some_string) - assert match is not None - name_group = match.group(1) # The user knows that this will never be None - return name_group.uper() # This typo will be flagged by the type checker - -In this case, the user of `match.group()` must be prepared to handle a `str`, -but type checkers are happy with `if name_group is None` checks, because we're -saying it can also be something else than an `str`. - -This is sometimes called "the Any trick". +``Any`` is used when it's not possible to accurately type an item using the current +type system. It should be used sparingly, as described in the :ref:`using-any` +section of the style guide. Attribute Access ---------------- @@ -799,6 +776,8 @@ all type checkers:: def foo(x: int | str) -> int | None: ... # recommended def foo(x: Union[int, str]) -> Optional[int]: ... # ok +.. _using-any: + Using `Any` and `object` ------------------------ @@ -814,6 +793,31 @@ that some function can accept literally anything: in those cases use When using `Any`, document the reason for using it in a comment. Ideally, document what types could be used. +The `Any` Trick +----------------- + +In cases where a function or method can return ``None``, but where forcing the +user to explicitly check for ``None`` can be detrimental, use +``_typeshed.MaybeNone`` (an alias to ``Any``), instead of ``None``. + +Consider the following (simplified) signature of ``re.Match[str].group``:: + + class Match: + def group(self, group: str | int, /) -> str | MaybeNone: ... + +This avoid forcing the user to check for ``None``:: + + match = re.fullmatch(r"\d+_(.*)", some_string) + assert match is not None + name_group = match.group(1) # The user knows that this will never be None + return name_group.uper() # This typo will be flagged by the type checker + +In this case, the user of ``match.group()`` must be prepared to handle a ``str``, +but type checkers are happy with ``if name_group is None`` checks, because we're +saying it can also be something else than an ``str``. + +This is sometimes called "the Any trick". + Context Managers ---------------- From d3a652adb206351052a914345e8441648cfdf7b5 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 12 Dec 2024 15:50:23 -0500 Subject: [PATCH 09/16] fix backticks, add Docstrings section --- docs/guides/writing_stubs.rst | 79 +++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 88fa801d2..1a253665a 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -503,7 +503,7 @@ more concise files. Syntax Example -------------- -The below is an excerpt from the types for the `datetime` module. +The below is an excerpt from the types for the ``datetime`` module. MAXYEAR: int MINYEAR: int @@ -680,13 +680,12 @@ No:: ... def to_int3(x: str) -> int: pass -Avoid invariant collection types (`list`, `dict`) for function parameters, -in favor of covariant types like `Mapping` or `Sequence`. +Avoid invariant collection types (``list``, ``dict``) for function parameters, +in favor of covariant types like ``Mapping`` or ``Sequence``. Avoid union return types. See https://github.com/python/mypy/issues/1693 -Use `float` instead of `int | float` for parameter annotations. -See [PEP 484](https://peps.python.org/pep-0484/#the-numeric-tower). +Use ``float`` instead of ``int | float`` for parameter annotations. See :pep:`484` for more details. Language Features ----------------- @@ -723,7 +722,7 @@ older versions of Python. Platform-dependent APIs ----------------------- -Use platform checks like `if sys.platform == 'win32'` to denote platform-dependent APIs. +Use :ref:`version-and-platform-checks` like ``if sys.platform == 'win32'`` to denote platform-dependent APIs. NamedTuple and TypedDict ------------------------ @@ -752,7 +751,7 @@ No:: Built-in Generics ----------------- -:pep:`585` built-in generics (such as `list`, `dict`, `tuple`, `set`) are supported and should be used instead +:pep:`585` built-in generics (such as ``list``, ``dict``, ``tuple``, ``set``) are supported and should be used instead of the corresponding types from ``typing``:: from collections import defaultdict @@ -770,7 +769,7 @@ generally possible and recommended:: Unions ------ -Declaring unions with the shorthand `|` syntax is recommended and supported by +Declaring unions with the shorthand ``|`` syntax is recommended and supported by all type checkers:: def foo(x: int | str) -> int | None: ... # recommended @@ -778,22 +777,22 @@ all type checkers:: .. _using-any: -Using `Any` and `object` ------------------------- +Using ``Any`` and ``object`` +---------------------------- -When adding type hints, avoid using the `Any` type when possible. Reserve -the use of `Any` for when: +When adding type hints, avoid using the ``Any`` type when possible. Reserve +the use of ``Any`` for when: * the correct type cannot be expressed in the current type system; and * to avoid union returns (see above). -Note that `Any` is not the correct type to use if you want to indicate +Note that ``Any`` is not the correct type to use if you want to indicate that some function can accept literally anything: in those cases use -`object` instead. +``object`` instead. -When using `Any`, document the reason for using it in a comment. Ideally, +When using ``Any``, document the reason for using it in a comment. Ideally, document what types could be used. -The `Any` Trick +The ``Any`` Trick ----------------- In cases where a function or method can return ``None``, but where forcing the @@ -822,17 +821,17 @@ Context Managers ---------------- When adding type annotations for context manager classes, annotate -the return type of `__exit__` as bool only if the context manager -sometimes suppresses exceptions -- if it sometimes returns `True` +the return type of ``__exit__`` as bool only if the context manager +sometimes suppresses exceptions -- if it sometimes returns ``True`` at runtime. If the context manager never suppresses exceptions, -have the return type be either `None` or `bool | None`. If you +have the return type be either ``None`` or ``bool | None``. If you are not sure whether exceptions are suppressed or not or if the -context manager is meant to be subclassed, pick `bool | None`. +context manager is meant to be subclassed, pick ``bool | None``. See https://github.com/python/mypy/issues/7214 for more details. -`__enter__` methods and other methods that return instances of the -current class should be annotated with `typing_extensions.Self` -([example](https://github.com/python/typeshed/blob/3581846/stdlib/contextlib.pyi#L151)). +``__enter__`` methods and other methods that return ``self`` or ``cls(...)`` +should be annotated with the `typing.Self` +(`example `_). Naming ------ @@ -845,29 +844,39 @@ A few guidelines for protocol names below. In cases that don't fall into any of those categories, use your best judgement. * Use plain names for protocols that represent a clear concept - (e.g. `Iterator`, `Container`). -* Use `SupportsX` for protocols that provide callable methods (e.g. - `SupportsInt`, `SupportsRead`, `SupportsReadSeek`). -* Use `HasX` for protocols that have readable and/or writable attributes - or getter/setter methods (e.g. `HasItems`, `HasFileno`). + (e.g. ``Iterator``, ``Container``). +* Use ``SupportsX`` for protocols that provide callable methods (e.g. + ``SupportsInt``, ``SupportsRead``, ``SupportsReadSeek``). +* Use ``HasX`` for protocols that have readable and/or writable attributes + or getter/setter methods (e.g. ``HasItems``, ``HasFileno``). .. _type-checker-error-suppression: Type Checker Error Suppression Formats -------------------------------------- -* Use mypy error codes for mypy-specific `# type: ignore` annotations, e.g. `# type: ignore[override]` for Liskov Substitution Principle violations. -* Use pyright error codes for pyright-specific suppressions, e.g. `# pyright: ignore[reportGeneralTypeIssues]`. -* If you need both on the same line, mypy's annotation needs to go first, e.g. `# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]`. +* Use mypy error codes for mypy-specific ``# type: ignore`` annotations, e.g. ``# type: ignore[override]`` for Liskov Substitution Principle violations. +* Use pyright error codes for pyright-specific suppressions, e.g. ``# pyright: ignore[reportGeneralTypeIssues]``. +* If you need both on the same line, mypy's annotation needs to go first, e.g. ``# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]``. -`@deprecated` -------------- +``@deprecated`` +--------------- -The `@typing_extensions.deprecated` decorator (`@warnings.deprecated` +The ``@typing_extensions.deprecated`` decorator (``@warnings.deprecated`` since Python 3.13) can be used to mark deprecated functionality; see -[PEP 702](https://peps.python.org/pep-0702/). +:pep:`702`. Keep the deprecation message concise, but try to mention the projected version when the functionality is to be removed, and a suggested replacement. + +Docstrings +---------- + +There are several tradeoffs around including docstrings in type stubs. Consider the intended purpose +of your stubs when deciding whether to include docstrings in your project's stubs. + +* They do not affect type checking results and will be ignored by type checkers. +* Docstrings can improve certain IDE functionality, such as hover info. +* Duplicating docstrings between source code and stubs requires extra work to keep them in sync. From 3e405f994459633ab8549c40105e8f5ecd840f89 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 12 Dec 2024 15:51:29 -0500 Subject: [PATCH 10/16] fix one more backtick --- docs/guides/writing_stubs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 1a253665a..ed22ee5b8 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -93,7 +93,7 @@ Liskov substitutability or detecting problematic overloads. It may be instructive to examine `typeshed `__'s `setup for testing stubs `__. -To suppress type errors in stubs, use `# type: ignore` comments. Refer to the :ref:`type-checker-error-suppression` section of the style guide for +To suppress type errors in stubs, use ``# type: ignore`` comments. Refer to the :ref:`type-checker-error-suppression` section of the style guide for error suppression formats specific to individual typecheckers. .. From 830f2479f0a62917e67ee264278889f3a191f9f2 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 12 Dec 2024 15:52:13 -0500 Subject: [PATCH 11/16] fix typo --- docs/guides/writing_stubs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index ed22ee5b8..b01f0191b 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -270,7 +270,7 @@ annotated function ``bar()``:: ``Any`` vs. ``Incomplete`` -------------------------- -While ``Incomplete`` is a type alias of ``Any``, they serve difference purposes: +While ``Incomplete`` is a type alias of ``Any``, they serve different purposes: ``Incomplete`` is a placeholder where a proper type might be substituted. It's a "to do" item and should be replaced if possible. From fbd2031cd1864e3e33d47d39c6f8fec515bbb0e3 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 12 Dec 2024 16:50:40 -0500 Subject: [PATCH 12/16] fix label --- docs/guides/writing_stubs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index b01f0191b..71b5d0eb7 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -722,7 +722,7 @@ older versions of Python. Platform-dependent APIs ----------------------- -Use :ref:`version-and-platform-checks` like ``if sys.platform == 'win32'`` to denote platform-dependent APIs. +Use :ref:`platform checks` like ``if sys.platform == 'win32'`` to denote platform-dependent APIs. NamedTuple and TypedDict ------------------------ From cc9cdf01b44556db91739d3af70dd3d4a3827e43 Mon Sep 17 00:00:00 2001 From: Rebecca Chen Date: Mon, 16 Dec 2024 22:35:37 -0800 Subject: [PATCH 13/16] Minor style and formatting fixes Removed repeated example, fixed formatting, removed stray "the". --- docs/guides/writing_stubs.rst | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 71b5d0eb7..47f48b40a 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -234,15 +234,7 @@ follow the following guidelines: can be left unannotated. * Do not use ``Any`` to mark unannotated or partially annotated values. Leave function parameters and return values unannotated. In all other cases, use - ``_typeshed.Incomplete``:: - - from _typeshed import Incomplete - - field1: Incomplete - field2: dict[str, Incomplete] - - def foo(x): ... - + ``_typeshed.Incomplete``. * Partial classes should include a ``__getattr__()`` method marked with ``_typeshed.Incomplete`` (see example below). * Partial modules (i.e. modules that are missing some or all classes, @@ -782,6 +774,7 @@ Using ``Any`` and ``object`` When adding type hints, avoid using the ``Any`` type when possible. Reserve the use of ``Any`` for when: + * the correct type cannot be expressed in the current type system; and * to avoid union returns (see above). @@ -830,7 +823,7 @@ context manager is meant to be subclassed, pick ``bool | None``. See https://github.com/python/mypy/issues/7214 for more details. ``__enter__`` methods and other methods that return ``self`` or ``cls(...)`` -should be annotated with the `typing.Self` +should be annotated with ``typing.Self`` (`example `_). Naming From 45500fdca708bcd443fc2bfec35b05a494acdc26 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Mon, 3 Feb 2025 00:11:08 -0500 Subject: [PATCH 14/16] Add additional callable kwargs conformance tests --- conformance/results/mypy/callables_kwargs.toml | 6 ++++-- conformance/results/mypy/version.toml | 4 ++-- conformance/results/pyre/callables_kwargs.toml | 2 ++ conformance/results/pyre/version.toml | 2 +- .../results/pyright/aliases_explicit.toml | 10 +++++----- .../results/pyright/aliases_type_statement.toml | 10 +++++----- .../results/pyright/aliases_typealiastype.toml | 2 +- .../pyright/annotations_forward_refs.toml | 10 +++++----- .../results/pyright/annotations_typeexpr.toml | 10 +++++----- .../results/pyright/callables_kwargs.toml | 6 ++++-- .../pyright/generics_syntax_declarations.toml | 2 +- .../pyright/generics_variance_inference.toml | 9 +++------ .../results/pyright/qualifiers_annotated.toml | 2 +- .../pyright/qualifiers_final_decorator.toml | 2 +- conformance/results/pyright/version.toml | 4 ++-- conformance/results/pytype/callables_kwargs.toml | 2 ++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 12 ++++++------ conformance/tests/callables_kwargs.py | 16 ++++++++++++++++ 19 files changed, 67 insertions(+), 46 deletions(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index 1b2578dd6..dec117dd9 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -1,4 +1,4 @@ -conformant = "Pass" +conformant = "Partial" output = """ callables_kwargs.py:22: note: "func1" defined here callables_kwargs.py:46: error: Missing named argument "v1" for "func1" [call-arg] @@ -21,6 +21,8 @@ callables_kwargs.py:103: note: "TDProtocol5.__call__" has type "Callable[[Arg(in callables_kwargs.py:111: error: Overlap between argument names and ** TypedDict items: "v1" [misc] callables_kwargs.py:122: error: Unpack item in ** argument must be a TypedDict [misc] """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 130: Expected 1 errors +Line 138: Expected 1 errors """ diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 3d498066d..7032be5e3 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ -version = "mypy 1.14.0" -test_duration = 1.6 +version = "mypy 1.14.1" +test_duration = 1.2 diff --git a/conformance/results/pyre/callables_kwargs.toml b/conformance/results/pyre/callables_kwargs.toml index 6714bfea2..766ae370b 100644 --- a/conformance/results/pyre/callables_kwargs.toml +++ b/conformance/results/pyre/callables_kwargs.toml @@ -15,6 +15,8 @@ callables_kwargs.py:102:0 Incompatible variable type [9]: v4 is declared to have callables_kwargs.py:103:0 Incompatible variable type [9]: v5 is declared to have type `TDProtocol5` but is used as type `typing.Callable(func1)[[Keywords(Unpack[TD2])], None]`. callables_kwargs.py:111:21 Duplicate parameter [65]: Duplicate parameter name `v1`. callables_kwargs.py:122:12 Invalid type [31]: `Unpack` in kwargs may only be used with typed dictionaries. `Variable[T (bound to TD2)]` is not a typed dictionary. +callables_kwargs.py:130:0 Incompatible variable type [9]: v7 is declared to have type `TDProtocol6` but is used as type `typing.Callable(func7)[[KeywordOnly(v1, int), KeywordOnly(v3, str), KeywordOnly(v2, str, default)], None]`. +callables_kwargs.py:138:4 Missing argument [20]: Call `func7` expects argument `v2` to be a required typed dictionary key, as the corresponding parameter has no default. """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 94de30557..cc9162d6c 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 7.3 +test_duration = 8.1 diff --git a/conformance/results/pyright/aliases_explicit.toml b/conformance/results/pyright/aliases_explicit.toml index a1e82c9eb..5948d8aba 100644 --- a/conformance/results/pyright/aliases_explicit.toml +++ b/conformance/results/pyright/aliases_explicit.toml @@ -10,25 +10,25 @@ aliases_explicit.py:79:21 - error: Invalid expression form for type alias defini aliases_explicit.py:79:21 - error: Call expression not allowed in type expression (reportInvalidTypeForm) aliases_explicit.py:80:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) aliases_explicit.py:80:21 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_explicit.py:80:21 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) aliases_explicit.py:81:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) aliases_explicit.py:81:21 - error: Tuple expression not allowed in type expression -  Use tuple[T1, ..., Tn] to indicate a tuple type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_explicit.py:82:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) aliases_explicit.py:82:21 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_explicit.py:82:21 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) aliases_explicit.py:83:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) aliases_explicit.py:83:21 - error: Dictionary expression not allowed in type expression -  Use Dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) aliases_explicit.py:83:21 - error: Expected class but received "dict[str, Unknown]" (reportGeneralTypeIssues) aliases_explicit.py:83:28 - error: "b" is not defined (reportUndefinedVariable) aliases_explicit.py:84:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) aliases_explicit.py:84:21 - error: Call expression not allowed in type expression (reportInvalidTypeForm) aliases_explicit.py:85:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) aliases_explicit.py:85:21 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_explicit.py:85:21 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) aliases_explicit.py:85:27 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) aliases_explicit.py:86:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) diff --git a/conformance/results/pyright/aliases_type_statement.toml b/conformance/results/pyright/aliases_type_statement.toml index 4556366e6..34269c479 100644 --- a/conformance/results/pyright/aliases_type_statement.toml +++ b/conformance/results/pyright/aliases_type_statement.toml @@ -14,20 +14,20 @@ aliases_type_statement.py:31:22 - error: Argument of type "TypeAliasType" cannot     "TypeAliasType" is not assignable to "tuple[_ClassInfo, ...]" (reportArgumentType) aliases_type_statement.py:37:22 - error: Call expression not allowed in type expression (reportInvalidTypeForm) aliases_type_statement.py:38:22 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_type_statement.py:38:22 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) aliases_type_statement.py:39:22 - error: Tuple expression not allowed in type expression -  Use tuple[T1, ..., Tn] to indicate a tuple type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_type_statement.py:40:22 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_type_statement.py:40:22 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) aliases_type_statement.py:41:22 - error: Dictionary expression not allowed in type expression -  Use Dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) aliases_type_statement.py:41:22 - error: Expected class but received "dict[str, Unknown]" (reportGeneralTypeIssues) aliases_type_statement.py:41:29 - error: "b" is not defined (reportUndefinedVariable) aliases_type_statement.py:42:22 - error: Call expression not allowed in type expression (reportInvalidTypeForm) aliases_type_statement.py:43:22 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_type_statement.py:43:22 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) aliases_type_statement.py:43:28 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) aliases_type_statement.py:44:22 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) diff --git a/conformance/results/pyright/aliases_typealiastype.toml b/conformance/results/pyright/aliases_typealiastype.toml index 64ebf8cfe..610c1a96b 100644 --- a/conformance/results/pyright/aliases_typealiastype.toml +++ b/conformance/results/pyright/aliases_typealiastype.toml @@ -19,7 +19,7 @@ aliases_typealiastype.py:56:42 - error: Expected class but received "dict[str, U aliases_typealiastype.py:56:49 - error: "b" is not defined (reportUndefinedVariable) aliases_typealiastype.py:57:42 - error: Call expression not allowed in type expression (reportInvalidTypeForm) aliases_typealiastype.py:58:42 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) aliases_typealiastype.py:58:42 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) aliases_typealiastype.py:58:48 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) aliases_typealiastype.py:59:42 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) diff --git a/conformance/results/pyright/annotations_forward_refs.toml b/conformance/results/pyright/annotations_forward_refs.toml index 297096740..ca35b5bb1 100644 --- a/conformance/results/pyright/annotations_forward_refs.toml +++ b/conformance/results/pyright/annotations_forward_refs.toml @@ -6,19 +6,19 @@ annotations_forward_refs.py:24:7 - error: Union syntax cannot be used with strin annotations_forward_refs.py:25:13 - error: Union syntax cannot be used with string operand; use quotes around entire expression (reportGeneralTypeIssues) annotations_forward_refs.py:41:10 - error: Call expression not allowed in type expression (reportInvalidTypeForm) annotations_forward_refs.py:42:10 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_forward_refs.py:42:10 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) annotations_forward_refs.py:43:10 - error: Tuple expression not allowed in type expression -  Use tuple[T1, ..., Tn] to indicate a tuple type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_forward_refs.py:44:10 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_forward_refs.py:44:10 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) annotations_forward_refs.py:45:10 - error: Dictionary expression not allowed in type expression -  Use Dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) annotations_forward_refs.py:45:10 - error: Expected class but received "dict[Unknown, Unknown]" (reportGeneralTypeIssues) annotations_forward_refs.py:46:10 - error: Call expression not allowed in type expression (reportInvalidTypeForm) annotations_forward_refs.py:47:10 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_forward_refs.py:47:10 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) annotations_forward_refs.py:47:16 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) annotations_forward_refs.py:48:10 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) diff --git a/conformance/results/pyright/annotations_typeexpr.toml b/conformance/results/pyright/annotations_typeexpr.toml index 5504e9464..e9e3fdca6 100644 --- a/conformance/results/pyright/annotations_typeexpr.toml +++ b/conformance/results/pyright/annotations_typeexpr.toml @@ -2,19 +2,19 @@ conformant = "Pass" output = """ annotations_typeexpr.py:88:9 - error: Call expression not allowed in type expression (reportInvalidTypeForm) annotations_typeexpr.py:89:9 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_typeexpr.py:89:9 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) annotations_typeexpr.py:90:9 - error: Tuple expression not allowed in type expression -  Use tuple[T1, ..., Tn] to indicate a tuple type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_typeexpr.py:91:9 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_typeexpr.py:91:9 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) annotations_typeexpr.py:92:9 - error: Dictionary expression not allowed in type expression -  Use Dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) annotations_typeexpr.py:92:9 - error: Expected class but received "dict[Unknown, Unknown]" (reportGeneralTypeIssues) annotations_typeexpr.py:93:9 - error: Call expression not allowed in type expression (reportInvalidTypeForm) annotations_typeexpr.py:94:9 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) annotations_typeexpr.py:94:9 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) annotations_typeexpr.py:94:15 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) annotations_typeexpr.py:95:9 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index 9dea95b60..23580f615 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -1,4 +1,4 @@ -conformant = "Pass" +conformant = "Partial" output = """ callables_kwargs.py:28:5 - error: Could not access item in TypedDict   "v2" is not a required key in "TD2", so access may result in runtime exception (reportTypedDictNotRequiredAccess) @@ -28,6 +28,8 @@ callables_kwargs.py:103:19 - error: Type "(**kwargs: **TD2) -> None" is not assi callables_kwargs.py:111:30 - error: Typed dictionary overlaps with keyword parameter: v1 (reportGeneralTypeIssues) callables_kwargs.py:122:21 - error: Expected TypedDict type argument for Unpack (reportGeneralTypeIssues) """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 130: Expected 1 errors +Line 138: Expected 1 errors """ diff --git a/conformance/results/pyright/generics_syntax_declarations.toml b/conformance/results/pyright/generics_syntax_declarations.toml index 25db91523..bc076dc93 100644 --- a/conformance/results/pyright/generics_syntax_declarations.toml +++ b/conformance/results/pyright/generics_syntax_declarations.toml @@ -6,7 +6,7 @@ generics_syntax_declarations.py:32:11 - error: Cannot access attribute "is_integ   Attribute "is_integer" is unknown (reportAttributeAccessIssue) generics_syntax_declarations.py:44:21 - error: TypeVar constraint type cannot be generic (reportGeneralTypeIssues) generics_syntax_declarations.py:48:17 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) generics_syntax_declarations.py:48:17 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) generics_syntax_declarations.py:60:17 - error: TypeVar must have at least two constrained types (reportGeneralTypeIssues) generics_syntax_declarations.py:64:17 - error: TypeVar must have at least two constrained types (reportGeneralTypeIssues) diff --git a/conformance/results/pyright/generics_variance_inference.toml b/conformance/results/pyright/generics_variance_inference.toml index 5c0f32e2a..9a378ad6f 100644 --- a/conformance/results/pyright/generics_variance_inference.toml +++ b/conformance/results/pyright/generics_variance_inference.toml @@ -23,12 +23,10 @@ generics_variance_inference.py:58:35 - error: Type "ShouldBeCovariant3[float]" i   "ShouldBeCovariant3[float]" is not assignable to "ShouldBeCovariant3[int]"     Type parameter "T@ShouldBeCovariant3" is covariant, but "float" is not a subtype of "int"       "float" is not assignable to "int" (reportAssignmentType) -generics_variance_inference.py:66:36 - error: Type "ShouldBeCovariant4[int]" is not assignable to declared type "ShouldBeCovariant4[float]" -  "ShouldBeCovariant4[int]" is not assignable to "ShouldBeCovariant4[float]" -    Type parameter "T@ShouldBeCovariant4" is invariant, but "int" is not the same as "float" (reportAssignmentType) generics_variance_inference.py:67:34 - error: Type "ShouldBeCovariant4[float]" is not assignable to declared type "ShouldBeCovariant4[int]"   "ShouldBeCovariant4[float]" is not assignable to "ShouldBeCovariant4[int]" -    Type parameter "T@ShouldBeCovariant4" is invariant, but "float" is not the same as "int" (reportAssignmentType) +    Type parameter "T@ShouldBeCovariant4" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) generics_variance_inference.py:80:34 - error: Type "ShouldBeCovariant5[float]" is not assignable to declared type "ShouldBeCovariant5[int]"   "ShouldBeCovariant5[float]" is not assignable to "ShouldBeCovariant5[int]"     Type parameter "T@ShouldBeCovariant5" is covariant, but "float" is not a subtype of "int" @@ -82,7 +80,6 @@ generics_variance_inference.py:194:37 - error: Type "ShouldBeContravariant2[int]     Type parameter "T@ShouldBeContravariant2" is contravariant, but "int" is not a supertype of "float"       "float" is not assignable to "int" (reportAssignmentType) """ -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 66: Unexpected errors ['generics_variance_inference.py:66:36 - error: Type "ShouldBeCovariant4[int]" is not assignable to declared type "ShouldBeCovariant4[float]"'] """ diff --git a/conformance/results/pyright/qualifiers_annotated.toml b/conformance/results/pyright/qualifiers_annotated.toml index b8c271f0e..c7f60d828 100644 --- a/conformance/results/pyright/qualifiers_annotated.toml +++ b/conformance/results/pyright/qualifiers_annotated.toml @@ -9,7 +9,7 @@ qualifiers_annotated.py:46:17 - error: Dictionary expression not allowed in type qualifiers_annotated.py:46:24 - error: "b" is not defined (reportUndefinedVariable) qualifiers_annotated.py:47:17 - error: Call expression not allowed in type expression (reportInvalidTypeForm) qualifiers_annotated.py:48:17 - error: List expression not allowed in type expression -  Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type (reportInvalidTypeForm) +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) qualifiers_annotated.py:48:17 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) qualifiers_annotated.py:48:23 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) qualifiers_annotated.py:49:17 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) diff --git a/conformance/results/pyright/qualifiers_final_decorator.toml b/conformance/results/pyright/qualifiers_final_decorator.toml index 6a3156500..36b1e38bb 100644 --- a/conformance/results/pyright/qualifiers_final_decorator.toml +++ b/conformance/results/pyright/qualifiers_final_decorator.toml @@ -6,7 +6,7 @@ qualifiers_final_decorator.py:56:9 - error: Method "method1" cannot override fin qualifiers_final_decorator.py:60:9 - error: Method "method2" cannot override final method defined in class "Base2" (reportIncompatibleMethodOverride) qualifiers_final_decorator.py:64:9 - error: Method "method3" cannot override final method defined in class "Base2" (reportIncompatibleMethodOverride) qualifiers_final_decorator.py:75:9 - error: Method "method4" cannot override final method defined in class "Base2" (reportIncompatibleMethodOverride) -qualifiers_final_decorator.py:86:9 - error: Overload for "method" is marked @final but implementation is not (reportInconsistentOverload) +qualifiers_final_decorator.py:86:9 - error: @final decorator should be applied only to the implementation (reportInconsistentOverload) qualifiers_final_decorator.py:89:9 - error: Method "method" cannot override final method defined in class "Base3" (reportIncompatibleMethodOverride) qualifiers_final_decorator.py:102:9 - error: Method "method" cannot override final method defined in class "Base4" (reportIncompatibleMethodOverride) qualifiers_final_decorator.py:118:9 - error: Method "method" cannot override final method defined in class "Base5_2" (reportIncompatibleMethodOverride) diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index e8e963b77..39a17fb79 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ -version = "pyright 1.1.391" -test_duration = 1.2 +version = "pyright 1.1.393" +test_duration = 1.5 diff --git a/conformance/results/pytype/callables_kwargs.toml b/conformance/results/pytype/callables_kwargs.toml index a44b3c5c5..727db66a8 100644 --- a/conformance/results/pytype/callables_kwargs.toml +++ b/conformance/results/pytype/callables_kwargs.toml @@ -82,6 +82,8 @@ Line 102: Expected 1 errors Line 103: Expected 1 errors Line 111: Expected 1 errors Line 122: Expected 1 errors +Line 130: Expected 1 errors +Line 138: Expected 1 errors Line 10: Unexpected errors ['callables_kwargs.py:10:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : typing.Unpack not supported yet [not-supported-yet]'] Line 24: Unexpected errors ['callables_kwargs.py:24:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func1: Unpack[TD2] [assert-type]'] Line 32: Unexpected errors ['callables_kwargs.py:32:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func1: Unpack[TD2] [assert-type]'] diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 94cd5eee5..3cd350580 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 37.2 +test_duration = 34.7 diff --git a/conformance/results/results.html b/conformance/results/results.html index e388ee383..2c9a32d51 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -158,17 +158,17 @@

Python Type System Conformance Test Results

- - +
 
mypy 1.14.0
-
1.6sec
-
pyright 1.1.391
+
mypy 1.14.1
1.2sec
pyright 1.1.393
+
1.5sec
+
pyre 0.9.23
-
7.3sec
+
8.1sec
pytype 2024.10.11
-
37.2sec
+
34.7sec
diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py index d8138efed..b6b570560 100644 --- a/conformance/tests/callables_kwargs.py +++ b/conformance/tests/callables_kwargs.py @@ -121,3 +121,19 @@ def func5(v1: int, **kwargs: Unpack[TD2]) -> None: # E: parameter v1 overlaps w def func6(**kwargs: Unpack[T]) -> None: # E: unpacked value must be a TypedDict, not a TypeVar bound to TypedDict. ... + + +def func7(*, v1: int, v3: str, v2: str = "") -> None: + ... + + +v7: TDProtocol6 = func7 # E: source does not have kwargs + + +def func8(**kwargs) -> None: + ... + +def func9(**kwargs: Unpack[TD2]) -> None: + # Kwargs hinted with an unpacked TypedDict can only be passed to another function that has **kwargs in its signature. + func7(**kwargs) # E + func8(**kwargs) # OK From ceef46e653de068bca25db40704a114ed6efa581 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Mon, 3 Feb 2025 16:31:14 -0500 Subject: [PATCH 15/16] add comments and notes --- conformance/results/mypy/callables_kwargs.toml | 4 ++++ conformance/results/pyright/callables_kwargs.toml | 4 ++++ conformance/tests/callables_kwargs.py | 9 ++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index dec117dd9..503977b69 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -1,4 +1,8 @@ conformant = "Partial" +notes=""" +Allows callable without kwargs to be assigned to callable with unpacked kwargs +Allows unpacked kwargs to be used when invoking function without kwargs +""" output = """ callables_kwargs.py:22: note: "func1" defined here callables_kwargs.py:46: error: Missing named argument "v1" for "func1" [call-arg] diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index 23580f615..322053b7a 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -1,4 +1,8 @@ conformant = "Partial" +notes=""" +Allows callable without kwargs to be assigned to callable with unpacked kwargs +Allows unpacked kwargs to be used when invoking function without kwargs +""" output = """ callables_kwargs.py:28:5 - error: Could not access item in TypedDict   "v2" is not a required key in "TD2", so access may result in runtime exception (reportTypedDictNotRequiredAccess) diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py index b6b570560..ef240c6b8 100644 --- a/conformance/tests/callables_kwargs.py +++ b/conformance/tests/callables_kwargs.py @@ -122,6 +122,10 @@ def func5(v1: int, **kwargs: Unpack[TD2]) -> None: # E: parameter v1 overlaps w def func6(**kwargs: Unpack[T]) -> None: # E: unpacked value must be a TypedDict, not a TypeVar bound to TypedDict. ... +# > The situation where the destination callable contains **kwargs: Unpack[TypedDict] and +# > the source callable doesn’t contain **kwargs should be disallowed. This is because, +# > we cannot be sure that additional keyword arguments are not being passed in when an instance of a subclass +# > had been assigned to a variable with a base class type and then unpacked in the destination callable invocation def func7(*, v1: int, v3: str, v2: str = "") -> None: ... @@ -129,11 +133,14 @@ def func7(*, v1: int, v3: str, v2: str = "") -> None: v7: TDProtocol6 = func7 # E: source does not have kwargs +# > Kwargs hinted with an unpacked TypedDict can only be passed to another function if the function +# > to which unpacked kwargs are being passed to has **kwargs in its signature as well, +# > because then additional keywords would not cause errors at runtime during function invocation. +# > Otherwise, the type checker should generate an error. def func8(**kwargs) -> None: ... def func9(**kwargs: Unpack[TD2]) -> None: - # Kwargs hinted with an unpacked TypedDict can only be passed to another function that has **kwargs in its signature. func7(**kwargs) # E func8(**kwargs) # OK From a3df774b543f52a129c79bbab0639624073d1301 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Wed, 5 Feb 2025 22:55:08 -0500 Subject: [PATCH 16/16] remove 2nd example --- .../results/mypy/callables_kwargs.toml | 6 +-- .../results/mypy/generics_defaults.toml | 40 +++++++++---------- .../mypy/generics_defaults_referential.toml | 12 +++--- .../generics_defaults_specialization.toml | 4 +- .../mypy/generics_paramspec_components.toml | 18 ++++----- conformance/results/mypy/version.toml | 2 +- .../results/pyre/callables_kwargs.toml | 3 +- conformance/results/pyre/version.toml | 2 +- .../results/pyright/callables_kwargs.toml | 6 +-- conformance/results/pyright/version.toml | 2 +- .../results/pytype/callables_kwargs.toml | 3 +- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 12 +++--- conformance/tests/callables_kwargs.py | 12 ------ 14 files changed, 53 insertions(+), 71 deletions(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index 503977b69..39eaad11f 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -1,7 +1,6 @@ conformant = "Partial" -notes=""" +notes = """ Allows callable without kwargs to be assigned to callable with unpacked kwargs -Allows unpacked kwargs to be used when invoking function without kwargs """ output = """ callables_kwargs.py:22: note: "func1" defined here @@ -27,6 +26,5 @@ callables_kwargs.py:122: error: Unpack item in ** argument must be a TypedDict """ conformance_automated = "Fail" errors_diff = """ -Line 130: Expected 1 errors -Line 138: Expected 1 errors +Line 134: Expected 1 errors """ diff --git a/conformance/results/mypy/generics_defaults.toml b/conformance/results/mypy/generics_defaults.toml index e7e5536b2..0491d9470 100644 --- a/conformance/results/mypy/generics_defaults.toml +++ b/conformance/results/mypy/generics_defaults.toml @@ -1,17 +1,17 @@ conformant = "Partial" output = """ generics_defaults.py:24: error: "T" cannot appear after "DefaultStrT" in type parameter list because it has no default type [misc] -generics_defaults.py:30: error: Expression is of type "type[NoNonDefaults[Any, Any]]", not "type[NoNonDefaults[str, int]]" [assert-type] -generics_defaults.py:31: error: Expression is of type "type[NoNonDefaults[Any, Any]]", not "type[NoNonDefaults[str, int]]" [assert-type] -generics_defaults.py:38: error: Expression is of type "type[OneDefault[Any, Any]]", not "type[OneDefault[float, bool]]" [assert-type] -generics_defaults.py:45: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[Any, Any, str, int, bool]]" [assert-type] -generics_defaults.py:46: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] +generics_defaults.py:30: error: Expression is of type "type[NoNonDefaults[DefaultStrT, DefaultIntT]]", not "type[NoNonDefaults[str, int]]" [assert-type] +generics_defaults.py:31: error: Expression is of type "type[NoNonDefaults[str, DefaultIntT]]", not "type[NoNonDefaults[str, int]]" [assert-type] +generics_defaults.py:38: error: Expression is of type "type[OneDefault[float, DefaultBoolT]]", not "type[OneDefault[float, bool]]" [assert-type] +generics_defaults.py:45: error: Expression is of type "type[AllTheDefaults[T1, T2, DefaultStrT, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[Any, Any, str, int, bool]]" [assert-type] +generics_defaults.py:46: error: Expression is of type "type[AllTheDefaults[int, complex, DefaultStrT, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] generics_defaults.py:50: error: Type application has too few types (expected between 2 and 5) [misc] -generics_defaults.py:52: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] -generics_defaults.py:55: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] -generics_defaults.py:59: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] -generics_defaults.py:79: error: Expression is of type "type[Class_ParamSpec[Any]]", not "type[Class_ParamSpec[[str, int]]]" [assert-type] -generics_defaults.py:94: error: Expression is of type "type[Class_TypeVarTuple[*tuple[Any, ...]]]", not "type[Class_TypeVarTuple[str, int]]" [assert-type] +generics_defaults.py:52: error: Expression is of type "type[AllTheDefaults[int, complex, DefaultStrT, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] +generics_defaults.py:55: error: Expression is of type "type[AllTheDefaults[int, complex, str, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] +generics_defaults.py:59: error: Expression is of type "type[AllTheDefaults[int, complex, str, int, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type] +generics_defaults.py:79: error: Expression is of type "type[Class_ParamSpec[DefaultP]]", not "type[Class_ParamSpec[[str, int]]]" [assert-type] +generics_defaults.py:94: error: Expression is of type "type[Class_TypeVarTuple[*DefaultTs]]", not "type[Class_TypeVarTuple[str, int]]" [assert-type] generics_defaults.py:151: error: Expression is of type "type[Foo6[*tuple[Any, ...], Any]]", not "type[Foo6[int, str, [float, bool]]]" [assert-type] generics_defaults.py:151: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "str" [misc] generics_defaults.py:152: error: Expression is of type "type[Foo6[*tuple[Any, ...], Any]]", not "type[Foo6[int, str, [bytes]]]" [assert-type] @@ -25,16 +25,16 @@ errors_diff = """ Line 104: Expected 1 errors Line 111: Expected 1 errors Line 138: Expected 1 errors -Line 30: Unexpected errors ['generics_defaults.py:30: error: Expression is of type "type[NoNonDefaults[Any, Any]]", not "type[NoNonDefaults[str, int]]" [assert-type]'] -Line 31: Unexpected errors ['generics_defaults.py:31: error: Expression is of type "type[NoNonDefaults[Any, Any]]", not "type[NoNonDefaults[str, int]]" [assert-type]'] -Line 38: Unexpected errors ['generics_defaults.py:38: error: Expression is of type "type[OneDefault[Any, Any]]", not "type[OneDefault[float, bool]]" [assert-type]'] -Line 45: Unexpected errors ['generics_defaults.py:45: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[Any, Any, str, int, bool]]" [assert-type]'] -Line 46: Unexpected errors ['generics_defaults.py:46: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] -Line 52: Unexpected errors ['generics_defaults.py:52: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] -Line 55: Unexpected errors ['generics_defaults.py:55: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] -Line 59: Unexpected errors ['generics_defaults.py:59: error: Expression is of type "type[AllTheDefaults[Any, Any, Any, Any, Any]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] -Line 79: Unexpected errors ['generics_defaults.py:79: error: Expression is of type "type[Class_ParamSpec[Any]]", not "type[Class_ParamSpec[[str, int]]]" [assert-type]'] -Line 94: Unexpected errors ['generics_defaults.py:94: error: Expression is of type "type[Class_TypeVarTuple[*tuple[Any, ...]]]", not "type[Class_TypeVarTuple[str, int]]" [assert-type]'] +Line 30: Unexpected errors ['generics_defaults.py:30: error: Expression is of type "type[NoNonDefaults[DefaultStrT, DefaultIntT]]", not "type[NoNonDefaults[str, int]]" [assert-type]'] +Line 31: Unexpected errors ['generics_defaults.py:31: error: Expression is of type "type[NoNonDefaults[str, DefaultIntT]]", not "type[NoNonDefaults[str, int]]" [assert-type]'] +Line 38: Unexpected errors ['generics_defaults.py:38: error: Expression is of type "type[OneDefault[float, DefaultBoolT]]", not "type[OneDefault[float, bool]]" [assert-type]'] +Line 45: Unexpected errors ['generics_defaults.py:45: error: Expression is of type "type[AllTheDefaults[T1, T2, DefaultStrT, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[Any, Any, str, int, bool]]" [assert-type]'] +Line 46: Unexpected errors ['generics_defaults.py:46: error: Expression is of type "type[AllTheDefaults[int, complex, DefaultStrT, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] +Line 52: Unexpected errors ['generics_defaults.py:52: error: Expression is of type "type[AllTheDefaults[int, complex, DefaultStrT, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] +Line 55: Unexpected errors ['generics_defaults.py:55: error: Expression is of type "type[AllTheDefaults[int, complex, str, DefaultIntT, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] +Line 59: Unexpected errors ['generics_defaults.py:59: error: Expression is of type "type[AllTheDefaults[int, complex, str, int, DefaultBoolT]]", not "type[AllTheDefaults[int, complex, str, int, bool]]" [assert-type]'] +Line 79: Unexpected errors ['generics_defaults.py:79: error: Expression is of type "type[Class_ParamSpec[DefaultP]]", not "type[Class_ParamSpec[[str, int]]]" [assert-type]'] +Line 94: Unexpected errors ['generics_defaults.py:94: error: Expression is of type "type[Class_TypeVarTuple[*DefaultTs]]", not "type[Class_TypeVarTuple[str, int]]" [assert-type]'] Line 151: Unexpected errors ['generics_defaults.py:151: error: Expression is of type "type[Foo6[*tuple[Any, ...], Any]]", not "type[Foo6[int, str, [float, bool]]]" [assert-type]', 'generics_defaults.py:151: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "str" [misc]'] Line 152: Unexpected errors ['generics_defaults.py:152: error: Expression is of type "type[Foo6[*tuple[Any, ...], Any]]", not "type[Foo6[int, str, [bytes]]]" [assert-type]', 'generics_defaults.py:152: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "str" [misc]'] Line 166: Unexpected errors ['generics_defaults.py:166: error: Expression is of type "Callable[[Self], Self]", not "Callable[[Foo7[int]], Foo7[int]]" [assert-type]'] diff --git a/conformance/results/mypy/generics_defaults_referential.toml b/conformance/results/mypy/generics_defaults_referential.toml index 3222f9e61..f2bcf685f 100644 --- a/conformance/results/mypy/generics_defaults_referential.toml +++ b/conformance/results/mypy/generics_defaults_referential.toml @@ -1,10 +1,10 @@ conformant = "Partial" output = """ -generics_defaults_referential.py:23: error: Expression is of type "type[slice[Any, Any, Any]]", not "type[slice[int, int, int | None]]" [assert-type] +generics_defaults_referential.py:23: error: Expression is of type "type[slice[StartT, StopT, StepT]]", not "type[slice[int, int, int | None]]" [assert-type] generics_defaults_referential.py:37: error: Argument 1 to "Foo" has incompatible type "str"; expected "int" [arg-type] generics_defaults_referential.py:53: error: Type parameter "Start2T" has a default type that refers to one or more type variables that are out of scope [misc] -generics_defaults_referential.py:94: error: Expression is of type "type[Bar[Any, Any]]", not "type[Bar[Any, list[Any]]]" [assert-type] -generics_defaults_referential.py:95: error: Expression is of type "type[Bar[Any, Any]]", not "type[Bar[int, list[int]]]" [assert-type] +generics_defaults_referential.py:94: error: Expression is of type "type[Bar[Z1, ListDefaultT]]", not "type[Bar[Any, list[Any]]]" [assert-type] +generics_defaults_referential.py:95: error: Expression is of type "type[Bar[int, ListDefaultT]]", not "type[Bar[int, list[int]]]" [assert-type] generics_defaults_referential.py:96: error: Expression is of type "Bar[int, list[Never]]", not "Bar[int, list[int]]" [assert-type] """ conformance_automated = "Fail" @@ -14,8 +14,8 @@ Line 60: Expected 1 errors Line 68: Expected 1 errors Line 74: Expected 1 errors Line 78: Expected 1 errors -Line 23: Unexpected errors ['generics_defaults_referential.py:23: error: Expression is of type "type[slice[Any, Any, Any]]", not "type[slice[int, int, int | None]]" [assert-type]'] -Line 94: Unexpected errors ['generics_defaults_referential.py:94: error: Expression is of type "type[Bar[Any, Any]]", not "type[Bar[Any, list[Any]]]" [assert-type]'] -Line 95: Unexpected errors ['generics_defaults_referential.py:95: error: Expression is of type "type[Bar[Any, Any]]", not "type[Bar[int, list[int]]]" [assert-type]'] +Line 23: Unexpected errors ['generics_defaults_referential.py:23: error: Expression is of type "type[slice[StartT, StopT, StepT]]", not "type[slice[int, int, int | None]]" [assert-type]'] +Line 94: Unexpected errors ['generics_defaults_referential.py:94: error: Expression is of type "type[Bar[Z1, ListDefaultT]]", not "type[Bar[Any, list[Any]]]" [assert-type]'] +Line 95: Unexpected errors ['generics_defaults_referential.py:95: error: Expression is of type "type[Bar[int, ListDefaultT]]", not "type[Bar[int, list[int]]]" [assert-type]'] Line 96: Unexpected errors ['generics_defaults_referential.py:96: error: Expression is of type "Bar[int, list[Never]]", not "Bar[int, list[int]]" [assert-type]'] """ diff --git a/conformance/results/mypy/generics_defaults_specialization.toml b/conformance/results/mypy/generics_defaults_specialization.toml index 321faa383..b29ba389c 100644 --- a/conformance/results/mypy/generics_defaults_specialization.toml +++ b/conformance/results/mypy/generics_defaults_specialization.toml @@ -1,10 +1,10 @@ conformant = "Partial" output = """ generics_defaults_specialization.py:30: error: Bad number of arguments for type alias, expected between 0 and 1, given 2 [type-arg] -generics_defaults_specialization.py:45: error: Expression is of type "type[Bar[Any]]", not "type[Bar[str]]" [assert-type] +generics_defaults_specialization.py:45: error: Expression is of type "type[Bar[DefaultStrT]]", not "type[Bar[str]]" [assert-type] generics_defaults_specialization.py:55: error: The type "type[Foo]" is not generic and not indexable [misc] """ conformance_automated = "Fail" errors_diff = """ -Line 45: Unexpected errors ['generics_defaults_specialization.py:45: error: Expression is of type "type[Bar[Any]]", not "type[Bar[str]]" [assert-type]'] +Line 45: Unexpected errors ['generics_defaults_specialization.py:45: error: Expression is of type "type[Bar[DefaultStrT]]", not "type[Bar[str]]" [assert-type]'] """ diff --git a/conformance/results/mypy/generics_paramspec_components.toml b/conformance/results/mypy/generics_paramspec_components.toml index 66a386ad0..86ba0b09b 100644 --- a/conformance/results/mypy/generics_paramspec_components.toml +++ b/conformance/results/mypy/generics_paramspec_components.toml @@ -8,10 +8,18 @@ Does not report error when keyword argument is specified between P.args and P.kw output = """ generics_paramspec_components.py:17: error: Use "P.args" for variadic "*" parameter [valid-type] generics_paramspec_components.py:17: error: Use "P.kwargs" for variadic "**" parameter [valid-type] +generics_paramspec_components.py:20: error: ParamSpec components are not allowed here [valid-type] generics_paramspec_components.py:23: error: Use "P.kwargs" for variadic "**" parameter [valid-type] +generics_paramspec_components.py:26: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [valid-type] +generics_paramspec_components.py:30: error: ParamSpec "P" is unbound [valid-type] +generics_paramspec_components.py:35: error: ParamSpec components are not allowed here [valid-type] +generics_paramspec_components.py:36: error: ParamSpec components are not allowed here [valid-type] +generics_paramspec_components.py:38: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [valid-type] +generics_paramspec_components.py:41: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [valid-type] generics_paramspec_components.py:49: error: Argument 1 has incompatible type "*P.kwargs"; expected "P.args" [arg-type] generics_paramspec_components.py:49: error: Argument 2 has incompatible type "**P.args"; expected "P.kwargs" [arg-type] generics_paramspec_components.py:51: error: Argument 1 has incompatible type "int"; expected "P.args" [arg-type] +generics_paramspec_components.py:60: error: Arguments not allowed after ParamSpec.args [valid-type] generics_paramspec_components.py:70: error: Argument 1 has incompatible type "*P.args"; expected "int" [arg-type] generics_paramspec_components.py:70: error: Argument 2 has incompatible type "int"; expected "P.args" [arg-type] generics_paramspec_components.py:72: error: Argument 1 has incompatible type "*P.args"; expected "int" [arg-type] @@ -21,14 +29,6 @@ generics_paramspec_components.py:83: error: Argument 3 to "foo" has incompatible generics_paramspec_components.py:98: error: Argument 2 to "twice" has incompatible type "str"; expected "int" [arg-type] generics_paramspec_components.py:98: error: Argument 3 to "twice" has incompatible type "int"; expected "str" [arg-type] """ -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 20: Expected 1 errors -Line 26: Expected 1 errors -Line 30: Expected 1 errors -Line 35: Expected 1 errors -Line 36: Expected 1 errors -Line 38: Expected 1 errors -Line 41: Expected 1 errors -Line 60: Expected 1 errors """ diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 7032be5e3..167122da3 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ -version = "mypy 1.14.1" +version = "mypy 1.15.0" test_duration = 1.2 diff --git a/conformance/results/pyre/callables_kwargs.toml b/conformance/results/pyre/callables_kwargs.toml index 766ae370b..a325fe97c 100644 --- a/conformance/results/pyre/callables_kwargs.toml +++ b/conformance/results/pyre/callables_kwargs.toml @@ -15,8 +15,7 @@ callables_kwargs.py:102:0 Incompatible variable type [9]: v4 is declared to have callables_kwargs.py:103:0 Incompatible variable type [9]: v5 is declared to have type `TDProtocol5` but is used as type `typing.Callable(func1)[[Keywords(Unpack[TD2])], None]`. callables_kwargs.py:111:21 Duplicate parameter [65]: Duplicate parameter name `v1`. callables_kwargs.py:122:12 Invalid type [31]: `Unpack` in kwargs may only be used with typed dictionaries. `Variable[T (bound to TD2)]` is not a typed dictionary. -callables_kwargs.py:130:0 Incompatible variable type [9]: v7 is declared to have type `TDProtocol6` but is used as type `typing.Callable(func7)[[KeywordOnly(v1, int), KeywordOnly(v3, str), KeywordOnly(v2, str, default)], None]`. -callables_kwargs.py:138:4 Missing argument [20]: Call `func7` expects argument `v2` to be a required typed dictionary key, as the corresponding parameter has no default. +callables_kwargs.py:134:0 Incompatible variable type [9]: v7 is declared to have type `TDProtocol6` but is used as type `typing.Callable(func7)[[KeywordOnly(v1, int), KeywordOnly(v3, str), KeywordOnly(v2, str, default)], None]`. """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cc9162d6c..0b853cef3 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 8.1 +test_duration = 8.3 diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index 322053b7a..c0c093879 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -1,7 +1,6 @@ conformant = "Partial" -notes=""" +notes = """ Allows callable without kwargs to be assigned to callable with unpacked kwargs -Allows unpacked kwargs to be used when invoking function without kwargs """ output = """ callables_kwargs.py:28:5 - error: Could not access item in TypedDict @@ -34,6 +33,5 @@ callables_kwargs.py:122:21 - error: Expected TypedDict type argument for Unpack """ conformance_automated = "Fail" errors_diff = """ -Line 130: Expected 1 errors -Line 138: Expected 1 errors +Line 134: Expected 1 errors """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index 39a17fb79..f4f28eead 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.393" -test_duration = 1.5 +test_duration = 1.6 diff --git a/conformance/results/pytype/callables_kwargs.toml b/conformance/results/pytype/callables_kwargs.toml index 727db66a8..9e727daec 100644 --- a/conformance/results/pytype/callables_kwargs.toml +++ b/conformance/results/pytype/callables_kwargs.toml @@ -82,8 +82,7 @@ Line 102: Expected 1 errors Line 103: Expected 1 errors Line 111: Expected 1 errors Line 122: Expected 1 errors -Line 130: Expected 1 errors -Line 138: Expected 1 errors +Line 134: Expected 1 errors Line 10: Unexpected errors ['callables_kwargs.py:10:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : typing.Unpack not supported yet [not-supported-yet]'] Line 24: Unexpected errors ['callables_kwargs.py:24:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func1: Unpack[TD2] [assert-type]'] Line 32: Unexpected errors ['callables_kwargs.py:32:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func1: Unpack[TD2] [assert-type]'] diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 3cd350580..3fc167d39 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 34.7 +test_duration = 37.0 diff --git a/conformance/results/results.html b/conformance/results/results.html index 2c9a32d51..adf04c036 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -158,17 +158,17 @@

Python Type System Conformance Test Results

- - - + + diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py index ef240c6b8..c52319fc1 100644 --- a/conformance/tests/callables_kwargs.py +++ b/conformance/tests/callables_kwargs.py @@ -132,15 +132,3 @@ def func7(*, v1: int, v3: str, v2: str = "") -> None: v7: TDProtocol6 = func7 # E: source does not have kwargs - -# > Kwargs hinted with an unpacked TypedDict can only be passed to another function if the function -# > to which unpacked kwargs are being passed to has **kwargs in its signature as well, -# > because then additional keywords would not cause errors at runtime during function invocation. -# > Otherwise, the type checker should generate an error. - -def func8(**kwargs) -> None: - ... - -def func9(**kwargs: Unpack[TD2]) -> None: - func7(**kwargs) # E - func8(**kwargs) # OK
 
mypy 1.14.1
+
mypy 1.15.0
1.2sec
pyright 1.1.393
-
1.5sec
+
1.6sec
pyre 0.9.23
-
8.1sec
+
8.3sec
pytype 2024.10.11
-
34.7sec
+
37.0sec
@@ -607,8 +607,8 @@

Python Type System Conformance Test Results

Partial

Does not support Concatenate.

Does not treat "*args: Any, **kwargs: Any" as "...".

     callables_kwargsPassPass
Partial

Allows callable without kwargs to be assigned to callable with unpacked kwargs

Partial

Allows callable without kwargs to be assigned to callable with unpacked kwargs

Pass
Unsupported

Does not understand Unpack in the context of **kwargs annotation.