Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type checking: super() does not pass class type in classmethods #1779

Closed
nuiva opened this issue Sep 8, 2021 · 4 comments
Closed

Type checking: super() does not pass class type in classmethods #1779

nuiva opened this issue Sep 8, 2021 · 4 comments
Labels
enhancement New feature or request fixed in next version (main) A fix has been implemented and will appear in an upcoming version

Comments

@nuiva
Copy link

nuiva commented Sep 8, 2021

Environment data

  • Language Server version: Pylance language server 2021.9.0 (pyright 21562654)
  • OS and version: Windows 10.0.19042
  • Python version (& distribution if applicable, e.g. Anaconda): Python 3.9.6

Code Snippet / Additional information

.vscode/settings.json

{
    "python.analysis.typeCheckingMode": "strict",
    "python.analysis.logLevel": "Trace",
}

main.py

from typing import TypeVar, Type

A_T = TypeVar("A_T", bound="A")

class A:
    @classmethod
    def construct(cls: Type[A_T]) -> A_T:
        return cls()

B_T = TypeVar("B_T", bound="B")

class B(A):
    @classmethod
    def construct(cls: Type[B_T]) -> B_T:
        return super().construct()

Actual behaviour

Pylance gives the following error on main.py:15

Expression of type "B" cannot be assigned to return type "B_T@construct"
Type "B" cannot be assigned to type "B_T@construct" Pylance(reportGeneralTypeIssues)

Hovering over return super().construct() says (method) construct: () -> B.

Expected behaviour

When super().construct() is called on line 15, the passed parameter cls has type B_T, not B. That means A.construct() should return an object of type B_T, not B.

Logs

[Info  - 5.08.50] Pylance language server 2021.9.0 (pyright 21562654) starting
[Info  - 5.08.50] Server root directory: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist
[Info  - 5.08.50] No configuration file found.
[Info  - 5.08.50] No pyproject.toml file found.
[Info  - 5.08.50] Setting pythonPath for service "pylance_tst": "C:\Program Files\Python39\python.exe"
[Warn  - 5.08.50] stubPath TESTDIRECTORY\typings is not a valid directory.
[Info  - 5.08.50] Assuming Python version 3.9
[Info  - 5.08.50] Assuming Python platform Windows
Search paths for TESTDIRECTORY
PATH VARIABLES OMITTED
[Info  - 5.08.50] Searching for source files
[Info  - 5.08.50] Found 1 source file
[Info  - 5.08.50] Background analysis(1) root directory: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist
[Info  - 5.08.50] Background analysis(1) started
Background analysis message: setConfigOptions
Background analysis message: setImportResolver
Background analysis message: ensurePartialStubPackages
[FG] parsing: TESTDIRECTORY\main.py (32ms)
[FG] parsing: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\builtins.pyi [fs read 3ms] (113ms)
[FG] binding: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\builtins.pyi (54ms)
[FG] binding: TESTDIRECTORY\main.py (1ms)
Background analysis message: setTrackedFiles
Background analysis message: markAllFilesDirty
Background analysis message: setFileOpened
Background analysis message: getSemanticTokens full
[BG(1)] getSemanticTokens full at TESTDIRECTORY\main.py ...
[BG(1)]   parsing: TESTDIRECTORY\main.py (27ms)
[BG(1)]   parsing: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\builtins.pyi [fs read 3ms] (114ms)
[BG(1)]   binding: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\builtins.pyi (50ms)
[BG(1)]   binding: TESTDIRECTORY\main.py (0ms)
[BG(1)]   parsing: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\typing.pyi [fs read 2ms] (31ms)
[BG(1)]   binding: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\typing.pyi (16ms)
[BG(1)]   parsing: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\_typeshed\__init__.pyi [fs read 0ms] (11ms)
[BG(1)]   binding: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\_typeshed\__init__.pyi (1ms)
[BG(1)]   parsing: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\typing_extensions.pyi [fs read 0ms] (2ms)
[BG(1)]   binding: c:\Users\USERNAME\.vscode\extensions\ms-python.vscode-pylance-2021.9.0\dist\typeshed-fallback\stdlib\typing_extensions.pyi (2ms)
[BG(1)] getSemanticTokens full at TESTDIRECTORY\main.py (296ms)
Background analysis message: getSemanticTokens range
[BG(1)] getSemanticTokens range 0:0 - 15:0 at TESTDIRECTORY\main.py (2ms)
Background analysis message: analyze
[BG(1)] analyzing: TESTDIRECTORY\main.py ...
[BG(1)]   checking: TESTDIRECTORY\main.py (8ms)
[BG(1)] analyzing: TESTDIRECTORY\main.py (8ms)
Background analysis message: resumeAnalysis
@github-actions github-actions bot added the triage label Sep 8, 2021
@erictraut
Copy link
Contributor

If you want to use the type of the cls parameter (which is Type[B_T] in your example) rather than the type of the containing class (which is Type[B] in your example), you need to pass the cls explicitly to the super call, like this:

class B(A):
    @classmethod
    def construct(cls: Type[B_T]) -> B_T:
        return super(B, cls).construct()

@nuiva
Copy link
Author

nuiva commented Sep 8, 2021

I see, that solves the issue. Thank you for the correction.

@nuiva nuiva closed this as completed Sep 8, 2021
@erictraut
Copy link
Contributor

After thinking about this a bit more, I think it's reasonable for a type checker to use the annotated type of cls (or self if it's within an instance method) as the implied second argument for super(). I've updated the type checker logic to handle this case. This will be included in the next release. The suggestion I provided above should serve as a workaround in the meantime. Thanks for filing the issue.

@erictraut erictraut reopened this Sep 8, 2021
@erictraut erictraut added enhancement New feature or request fixed in next version (main) A fix has been implemented and will appear in an upcoming version and removed triage labels Sep 8, 2021
@jakebailey
Copy link
Member

This issue has been fixed in version 2021.9.1, which we've just released. You can find the changelog here: https://github.com/microsoft/pylance-release/blob/main/CHANGELOG.md#202191-8-september-2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request fixed in next version (main) A fix has been implemented and will appear in an upcoming version
Projects
None yet
Development

No branches or pull requests

3 participants