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 only cares about the last assignment of type definitions #4260

Closed
PabloLION opened this issue Apr 19, 2023 · 5 comments
Closed

Pylance only cares about the last assignment of type definitions #4260

PabloLION opened this issue Apr 19, 2023 · 5 comments
Assignees

Comments

@PabloLION
Copy link

PabloLION commented Apr 19, 2023

Pylance only recognizes the last assignment of a type definition and reports an error on code that uses a different type value from previous assignments. This can occur with both simple types and complex types like Literal and Tuple.

Environment data

  • Language Server version: Pylance language server 2023.4.20 (pyright 64a33975)
  • OS and version: macOS Monterey 12.6 (21G115)
  • Python version (& distribution if applicable, e.g. Anaconda): 3.11.2 (installed via homebrew)

Code Snippet

from typing import Literal


T = int
T = str
a: T = "1"

U = Literal["1"]
b: U = "1"  # reporting an error due to the Literal type from next line
"""
Expression of type "Literal['1']" cannot be assigned to declared type "Literal[1]"
    "Literal['1']" cannot be assigned to type "Literal[1]"
"""

U = Literal[1]
c: U = 1
b: U = 1

# more errors with next lines

# class U(Enum):
#     ONE = 1
# d: U = U.ONE

# Expected: something like "Class declaration "Solution" is obscured by a declaration of the same name Pylance reportGeneralTypeIssues"
# Actual: Expression of type "Type[Literal[1]]" cannot be assigned to declared type "Type[U]"
#   "Type[Literal[1]]" is incompatible with "Type[U]"
#   Type "Type[Literal[1]]" cannot be assigned to type "Type[U]" Pylance reportGeneralTypeIssues

L = Literal[1]
M = tuple[L, L]
l: L = 1  # reporting an error due to the Literal type from the next assignment of "L"
"""
Expression of type "Literal[1]" cannot be assigned to declared type "Literal['1']"
    "Literal[1]" cannot be assigned to type "Literal['1']"
"""
m: M = (1, 1)

L = Literal["1"]
l: L = "1"
m: M = ("1", "1")
"""
Expression of type "tuple[Literal['1'], Literal['1']]" cannot be assigned to declared type "M"
    "Literal['1']" cannot be assigned to type "Literal[1]"
    "Literal['1']" cannot be assigned to type "Literal[1]"
"""

Repro Steps

  1. Paste the snippet to VS Code
  2. Check the error.

Expected behavior

I offer two options but there might be better ones:

  1. use correct type definition instead of the last one in the file.
  2. (compromise) report error when user reassigns a type definition

Actual behavior

Pyright only uses the last type definition

Logs

The 1858 line log is too long. I replaced my root dir path with ~, and removed indexing from numpy, sympy, networkx, pathspec.
After a lot of effort, github still says "There was an error creating your Issue: body is too long (maximum is 65536 characters)." So I share the log with an attachment

log.txt

@erictraut
Copy link
Contributor

This is working as designed. When you provide a type annotation for a symbol, you are declaring that any value assigned to that symbol must conform to the provided type. It therefore doesn't make sense to declare two or more conflicting types for a symbol. This is considered a coding error. In your code, you have multiple incompatible type declarations for a symbol.

To better understand static typing concepts including type declarations, refer to this documentation.

@erictraut
Copy link
Contributor

As I mentioned in the other issue, if you want to allow a symbol to accept values of different types, then you can omit the type declaration altogether. The type will then be inferred based on the most recent assignment. If you include a type declaration, the type checker will enforce that any assigned values conform to that type. And if you provide multiple incompatible type declarations, the type checker will report that as an error.

@rchiodo rchiodo closed this as completed Apr 19, 2023
@PabloLION
Copy link
Author

PabloLION commented Apr 19, 2023

In this case, can we report error when user reassigns a type annotation? I think it helps locating the error. As @erictraut said,

It therefore doesn't make sense to declare two or more conflicting types for a symbol. This is considered a coding error.

hence, the coding error should be reported on the line where it occurs.

@erictraut
Copy link
Contributor

Pylance will report the redefinition of a type alias if you use the typing.TypeAlias form introduced in PEP 613 or the soon-to-be-added type syntax introduced in PEP 695. It does not report the redefinition of a type alias if you use the traditional simple assignment form to define your type alias because this form has the same syntax as a variable reassignment, which is legal. This older form requires a bunch of heuristics in a static type checker to "guess" whether the intent was to define a variable or a type alias. For this reason, an error is not emitted at the point of redefinition when using this older form.

If you're interested in receiving errors like this for type alias definitions, you should use the explicit TypeAlias form.

U: TypeAlias = Literal["1"]

@PabloLION
Copy link
Author

PabloLION commented Apr 19, 2023

😆After seeing your response in #4262, I just tested and it did report the correct error at the correct line. like this: "U" is declared as a TypeAlias and can be assigned only once Pylance
Thanks again!

P.S.

I just found that erictr is the author of PEP 695.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants