From 000eb4460b6d0f28fb5d6b2802df0c5ea08c7659 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 4 Jan 2023 22:38:02 -0500 Subject: [PATCH 1/9] Explain why we sublass ImageQt with Any --- stubs/Pillow/PIL/ImageQt.pyi | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stubs/Pillow/PIL/ImageQt.pyi b/stubs/Pillow/PIL/ImageQt.pyi index 47283703386e..61fc1ac1601d 100644 --- a/stubs/Pillow/PIL/ImageQt.pyi +++ b/stubs/Pillow/PIL/ImageQt.pyi @@ -3,7 +3,10 @@ from typing_extensions import Literal, TypeAlias from .Image import Image -QImage: TypeAlias = Any # imported from either of {PyQt6,PySide6,PyQt5,PySide2}.QtGui +# imported from either of {PyQt6,PySide6,PyQt5,PySide2}.QtGui +# These are way too complex, with 4 different possible sources (2 deprecated) +# And we don't want to force the user to install PyQt or Pyside when they may not even use it. +QImage: TypeAlias = Any QPixmap: TypeAlias = Any qt_versions: Any @@ -15,7 +18,7 @@ def fromqimage(im: Image | QImage) -> Image: ... def fromqpixmap(im: Image | QImage) -> Image: ... def align8to32(bytes: bytes, width: int, mode: Literal["1", "L", "P"]) -> bytes: ... -class ImageQt(QImage): +class ImageQt(QImage): # type: ignore[misc] def __init__(self, im: Image) -> None: ... def toqimage(im: Image) -> ImageQt: ... From 679b2764806a2268323541c2dec0b3b8f9617ef5 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 11 Jan 2023 00:36:56 -0500 Subject: [PATCH 2/9] disallow subclassing any --- tests/mypy_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/mypy_test.py b/tests/mypy_test.py index 0fd07095b1d5..366e2f43d2f3 100644 --- a/tests/mypy_test.py +++ b/tests/mypy_test.py @@ -247,7 +247,6 @@ def run_mypy( # Stub completion is checked by pyright (--allow-*-defs) "--allow-untyped-defs", "--allow-incomplete-defs", - "--allow-subclassing-any", # TODO: Do we still need this now that non-types dependencies are allowed? (#5768) "--enable-error-code", "ignore-without-code", "--config-file", From d7fd16551a095a751ef493f105beecc9179b0341 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 11 Jan 2023 02:58:39 -0500 Subject: [PATCH 3/9] Add comment explaining NonCallableMock Any subclassing --- stdlib/unittest/mock.pyi | 5 ++++- stubs/mock/mock/mock.pyi | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/stdlib/unittest/mock.pyi b/stdlib/unittest/mock.pyi index 47535499a9f2..9f2bbc6711c5 100644 --- a/stdlib/unittest/mock.pyi +++ b/stdlib/unittest/mock.pyi @@ -102,7 +102,10 @@ class _CallList(list[_Call]): class Base: def __init__(self, *args: Any, **kwargs: Any) -> None: ... -class NonCallableMock(Base, Any): +# Defining this and other mock classes exactly like the source causes +# many false positives with mypy and production code. +# We improve by use a class with an "Any" base class. +class NonCallableMock(Base, Any): # type: ignore[misc] def __new__(__cls: type[Self], *args: Any, **kw: Any) -> Self: ... def __init__( self, diff --git a/stubs/mock/mock/mock.pyi b/stubs/mock/mock/mock.pyi index 6e52136ce660..df549960ebb2 100644 --- a/stubs/mock/mock/mock.pyi +++ b/stubs/mock/mock/mock.pyi @@ -76,7 +76,10 @@ class _CallList(list[_Call]): class Base: def __init__(self, *args: Any, **kwargs: Any) -> None: ... -class NonCallableMock(Base, Any): +# Defining this and other mock classes exactly like the source causes +# many false positives with mypy and production code. +# We improve by use a class with an "Any" base class. +class NonCallableMock(Base, Any): # type: ignore[misc] def __new__( cls: type[Self], spec: list[str] | object | type[object] | None = ..., From 85f73613034e0103042a7ea2d313b24745af3b24 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 11 Jan 2023 03:03:48 -0500 Subject: [PATCH 4/9] reword --- stdlib/unittest/mock.pyi | 5 ++--- stubs/mock/mock/mock.pyi | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/stdlib/unittest/mock.pyi b/stdlib/unittest/mock.pyi index 9f2bbc6711c5..3256461df907 100644 --- a/stdlib/unittest/mock.pyi +++ b/stdlib/unittest/mock.pyi @@ -102,9 +102,8 @@ class _CallList(list[_Call]): class Base: def __init__(self, *args: Any, **kwargs: Any) -> None: ... -# Defining this and other mock classes exactly like the source causes -# many false positives with mypy and production code. -# We improve by use a class with an "Any" base class. +# We subclass with "Any" because defining this and other mock classes exactly like the source +# causes many false positives with mypy and production code. class NonCallableMock(Base, Any): # type: ignore[misc] def __new__(__cls: type[Self], *args: Any, **kw: Any) -> Self: ... def __init__( diff --git a/stubs/mock/mock/mock.pyi b/stubs/mock/mock/mock.pyi index df549960ebb2..9c6e8dc2fac0 100644 --- a/stubs/mock/mock/mock.pyi +++ b/stubs/mock/mock/mock.pyi @@ -76,9 +76,8 @@ class _CallList(list[_Call]): class Base: def __init__(self, *args: Any, **kwargs: Any) -> None: ... -# Defining this and other mock classes exactly like the source causes -# many false positives with mypy and production code. -# We improve by use a class with an "Any" base class. +# We subclass with "Any" because defining this and other mock classes exactly like the source +# causes many false positives with mypy and production code. class NonCallableMock(Base, Any): # type: ignore[misc] def __new__( cls: type[Self], From 9005a1821e679e3cd662b2bb19f43400705e23a6 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 11 Jan 2023 18:22:24 -0500 Subject: [PATCH 5/9] Update comment --- stdlib/unittest/mock.pyi | 4 ++-- stubs/mock/mock/mock.pyi | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/unittest/mock.pyi b/stdlib/unittest/mock.pyi index 3256461df907..af7ae390df31 100644 --- a/stdlib/unittest/mock.pyi +++ b/stdlib/unittest/mock.pyi @@ -102,8 +102,8 @@ class _CallList(list[_Call]): class Base: def __init__(self, *args: Any, **kwargs: Any) -> None: ... -# We subclass with "Any" because defining this and other mock classes exactly like the source -# causes many false positives with mypy and production code. +# We subclass with "Any" because mocks are explicitly designed to stand in for other types, +# something that can't be expressed with our static type system. class NonCallableMock(Base, Any): # type: ignore[misc] def __new__(__cls: type[Self], *args: Any, **kw: Any) -> Self: ... def __init__( diff --git a/stubs/mock/mock/mock.pyi b/stubs/mock/mock/mock.pyi index 9c6e8dc2fac0..35de8e01bf27 100644 --- a/stubs/mock/mock/mock.pyi +++ b/stubs/mock/mock/mock.pyi @@ -76,8 +76,8 @@ class _CallList(list[_Call]): class Base: def __init__(self, *args: Any, **kwargs: Any) -> None: ... -# We subclass with "Any" because defining this and other mock classes exactly like the source -# causes many false positives with mypy and production code. +# We subclass with "Any" because mocks are explicitly designed to stand in for other types, +# something that can't be expressed with our static type system. class NonCallableMock(Base, Any): # type: ignore[misc] def __new__( cls: type[Self], From 826532fc7fa3ad2224217bae06533cea8c2c098a Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 11 Jan 2023 20:05:59 -0500 Subject: [PATCH 6/9] SQLAlchemy's _DeclarativeBase(Any) --- stubs/SQLAlchemy/sqlalchemy/orm/decl_api.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/SQLAlchemy/sqlalchemy/orm/decl_api.pyi b/stubs/SQLAlchemy/sqlalchemy/orm/decl_api.pyi index 5970c26d7f70..9b6dff8d5a90 100644 --- a/stubs/SQLAlchemy/sqlalchemy/orm/decl_api.pyi +++ b/stubs/SQLAlchemy/sqlalchemy/orm/decl_api.pyi @@ -12,7 +12,7 @@ _DeclT = TypeVar("_DeclT", bound=type[_DeclarativeBase]) # Dynamic class as created by registry.generate_base() via DeclarativeMeta # or another metaclass. This class does not exist at runtime. -class _DeclarativeBase(Any): # super classes are dynamic +class _DeclarativeBase(Any): # type: ignore[misc] # super classes are dynamic registry: ClassVar[registry] metadata: ClassVar[MetaData] __abstract__: ClassVar[bool] From e5f0d3dbede308073d0d6ece307c9440ad913ec4 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 11 Jan 2023 21:58:30 -0500 Subject: [PATCH 7/9] Suppress any subclassing error in distutils --- stdlib/distutils/command/check.pyi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/distutils/command/check.pyi b/stdlib/distutils/command/check.pyi index cdbe40fff71d..3644c401b2ab 100644 --- a/stdlib/distutils/command/check.pyi +++ b/stdlib/distutils/command/check.pyi @@ -6,7 +6,9 @@ from ..cmd import Command _Reporter: TypeAlias = Any # really docutils.utils.Reporter # Only defined if docutils is installed. -class SilentReporter(_Reporter): +# Depends on a third-party stub. Since distutils is deprecated anyway, +# it's easier to just suppress the "any subclassing" error. +class SilentReporter(_Reporter): # type: ignore[misc] messages: Any def __init__( self, From 12538abf4a9104323f5e5028c06c4bce7d973fc2 Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 7 Feb 2023 20:36:56 -0500 Subject: [PATCH 8/9] Fix leftover underscore from bad merge --- stubs/Pillow/PIL/ImageQt.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stubs/Pillow/PIL/ImageQt.pyi b/stubs/Pillow/PIL/ImageQt.pyi index e9f58f3a4bb1..aab3b36067a4 100644 --- a/stubs/Pillow/PIL/ImageQt.pyi +++ b/stubs/Pillow/PIL/ImageQt.pyi @@ -14,12 +14,12 @@ qt_is_installed: bool qt_version: Any def rgb(r: int, g: int, b: int, a: int = ...) -> int: ... -def fromqimage(im: ImageQt | _QImage) -> Image: ... -def fromqpixmap(im: ImageQt | _QImage) -> Image: ... +def fromqimage(im: ImageQt | QImage) -> Image: ... +def fromqpixmap(im: ImageQt | QImage) -> Image: ... def align8to32(bytes: bytes, width: int, mode: Literal["1", "L", "P"]) -> bytes: ... class ImageQt(QImage): # type: ignore[misc] def __init__(self, im: Image) -> None: ... def toqimage(im: Image) -> ImageQt: ... -def toqpixmap(im: Image) -> _QPixmap: ... +def toqpixmap(im: Image) -> QPixmap: ... From c6d0037459af033b248f0eccc67d989db52585b6 Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 7 Feb 2023 20:44:23 -0500 Subject: [PATCH 9/9] other way around --- stubs/Pillow/PIL/ImageQt.pyi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stubs/Pillow/PIL/ImageQt.pyi b/stubs/Pillow/PIL/ImageQt.pyi index aab3b36067a4..6eb6287327dc 100644 --- a/stubs/Pillow/PIL/ImageQt.pyi +++ b/stubs/Pillow/PIL/ImageQt.pyi @@ -6,20 +6,20 @@ from .Image import Image # imported from either of {PyQt6,PySide6,PyQt5,PySide2}.QtGui # These are way too complex, with 4 different possible sources (2 deprecated) # And we don't want to force the user to install PyQt or Pyside when they may not even use it. -QImage: TypeAlias = Any -QPixmap: TypeAlias = Any +_QImage: TypeAlias = Any +_QPixmap: TypeAlias = Any qt_versions: Any qt_is_installed: bool qt_version: Any def rgb(r: int, g: int, b: int, a: int = ...) -> int: ... -def fromqimage(im: ImageQt | QImage) -> Image: ... -def fromqpixmap(im: ImageQt | QImage) -> Image: ... +def fromqimage(im: ImageQt | _QImage) -> Image: ... +def fromqpixmap(im: ImageQt | _QImage) -> Image: ... def align8to32(bytes: bytes, width: int, mode: Literal["1", "L", "P"]) -> bytes: ... -class ImageQt(QImage): # type: ignore[misc] +class ImageQt(_QImage): # type: ignore[misc] def __init__(self, im: Image) -> None: ... def toqimage(im: Image) -> ImageQt: ... -def toqpixmap(im: Image) -> QPixmap: ... +def toqpixmap(im: Image) -> _QPixmap: ...