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

Pylance didn't check return type from __new__ #1092

Closed
pasunx opened this issue Mar 26, 2021 · 6 comments
Closed

Pylance didn't check return type from __new__ #1092

pasunx opened this issue Mar 26, 2021 · 6 comments
Labels
bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version

Comments

@pasunx
Copy link

pasunx commented Mar 26, 2021

Environment data

  • Language Server version: 2021.3.3
  • OS and version: win32 x64
  • Python version (and distribution if applicable, e.g. Anaconda):
  • python.analysis.indexing: undefined
  • python.analysis.typeCheckingMode: strict

Expected behaviour

class HelloWorld:

    def __new__(cls) -> str:
        return "Hello World"

def func(text: str) -> None:
    print(text)

text = HelloWorld() # (variable) text: str
func(text)

Actual behaviour

class HelloWorld:

    def __new__(cls) -> str:
        return "Hello World"

def func(text: str) -> None:
    print(text)

text = HelloWorld() # (variable) text: HelloWorld <- "text" should be "str" not "HelloWorld"
func(text) # error:
           #   Argument of type "HelloWorld" cannot be assigned to parameter "text" of type "str" in function "func"
           #     "HelloWorld" is incompatible with "str" Pylance (reportGeneralTypeIssues)

Logs

Will provide if necessary.

@jakebailey
Copy link
Member

jakebailey commented Mar 26, 2021

See also: microsoft/pyright#625

It's my understanding that this is intentional, and you should be getting a type error for that __new__ function for returning the wrong thing. mypy on this code also says:

main.py:3: error: Incompatible return type for "__new__" (returns "str", but must return a subtype of "HelloWorld")
main.py:10: error: Argument 1 to "func" has incompatible type "HelloWorld"; expected "str"
Found 2 errors in 1 file (checked 1 source file)

Which matches our behavior.

@jakebailey
Copy link
Member

What is your use case for this pattern, as opposed to using a function?

@jakebailey jakebailey added the waiting for user response Requires more information from user label Mar 26, 2021
@github-actions github-actions bot removed the triage label Mar 26, 2021
@erictraut
Copy link
Contributor

While it is unusual for a __new__ method to return an object that is not an instance of the class, a type checker should handle this case. Pyright tries to handle this case, but there must be a bug in the logic. I'll investigate.

@erictraut erictraut added the bug Something isn't working label Mar 26, 2021
@pasunx
Copy link
Author

pasunx commented Mar 26, 2021

I want to making sub function, like this

from typing import Optional
from dataclasses import dataclass

@dataclass
class Package:

    @dataclass
    class Author:

        name: str
        mail: Optional[str]

    name: str
    version: Optional[str]
    author: Optional[Author]

class package:

    class author:

        def __new__(cls, *,
                name: str,
                mail: Optional[str] = None) -> Package.Author:
            return Package.Author(name, mail)

    def __new__(cls, *,
            name: str,
            version: Optional[str] = None,
            author: Optional[Package.Author] = None) -> Package:
        return Package(name, version, author)

def setup(package: Package) -> None: ...

setup(package(
    name = "package",
    author = package.author(
        name = "PASUNX"
    )
))
# error:
#   Argument of type "author" cannot be assigned to parameter "author" of type "Author | None" in function "__new__"
#     Type "author" cannot be assigned to type "Author | None"
#       "author" is incompatible with "Author"
#       Cannot assign to "None" Pylance (reportGeneralTypeIssues)

I know that I can create "author" function but I don't like to

This option work fine but I like the first option

from typing import Optional

class Package:

    class Author:

        name: str
        mail: Optional[str]

        def __init__(self, *,
                name: str,
                mail: Optional[str] = None):
            self.name = name
            self.mail = mail

    name: str
    version: Optional[str]
    author: Optional[Author]

    def __init__(self, *,
            name: str,
            version: Optional[str] = None,
            author: Optional[Author] = None) -> None:
        self.name = name
        self.version = version
        self.author = author

def setup(package: Package) -> None: ...

setup(Package(
    name = "package",
    author = Package.Author(
        name = "PASUNX"
    )
))

any idea or should I use second option ?

@erictraut
Copy link
Contributor

This bug will be fixed in the next release of pylance.

Python constructor calls are complicated for a type evaluator because they involve calls to both the __new__ method and __init__ method, and either or both of these methods can be present, and either or both can specialize the type of a generic class. There was one combination that was not properly handled. I've fixed the bug and improved our unit tests to cover this case.

Thanks for the bug report!

@erictraut erictraut added fixed in next version (main) A fix has been implemented and will appear in an upcoming version and removed waiting for user response Requires more information from user labels Mar 26, 2021
@jakebailey
Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working 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