profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/erictraut/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
Eric Traut erictraut Microsoft Corp. Redmond, WA Eric is a Technical Fellow at Microsoft. He has worked on many projects including HyperV, Windows Core OS, Skype, Pyright, and AI for Autonomous Systems

microsoft/pyright 6991

Static type checker for Python

microsoft/ReSub 605

A library for writing React components that automatically manage subscriptions to data sources simply by accessing them

microsoft/NoSQLProvider 90

A cross-browser/platform indexeddb-like client library

microsoft/SyncTasks 32

An explicitly non-A+ Promise library that resolves promises synchronously

microsoft/bonsai-simulink 13

Microsoft Bonsai Simulink Toolbox and sample models

microsoft/FMU-bonsai-connector 1

A connector for integrating FMU models with Microsoft Project Bonsai.

erictraut/attrs 0

Python Classes Without Boilerplate

erictraut/bonsaiai.github.io 0

Bonsai's Slate Developer Documentation

erictraut/microsoft-bonsai-api 0

A collection of libraries for interfacing simulators with the Bonsai platform.

erictraut/NoSQLProvider 0

A cross-browser/platform indexeddb-like client library

issue commentmicrosoft/pyright

Subtly incorrect import resolution with relative imports

Yes, the Python import mechanism leaves much to be desired. The runtime logic for import has order-dependent side effects that are difficult or impossible to model statically (and difficult for any programmer to reason about!). Python code that relies on such side effects is arguably buggy, or at least extremely fragile and likely to break when seemingly innocuous changes are made in other source files or libraries.

In pyright, we don't attempt to model such side effects, since we'd end up being wrong as often as we're right. Instead, we always assume that all import-related effects across files are independent of each other (i.e. we assume that module imports may be resolved in any arbitrary order at runtime). And we assume that import-related effects within a file are applied in file order with the last one "winning". That's why you're seeing in the example above that the type of ambiguous is resolved as a module rather than a str. If you swap the order of the last two lines in a/__init__.py, you'll see that the result changes accordingly.

One could argue that in this particular example, the behavior is deterministic and statically determinable, but this quickly falls apart with a small change to the code. For example, what is the correct behavior in this case?

### a/__init__.py
if some_condition:
    import a.ambiguous
from .b import ambiguous
from .ambiguous import foo

Interestingly, it appears that mypy always determines the type to be str regardless of the ordering, which also does not match the runtime behavior. So mypy also falls back on a heuristic, but I haven't been able to determine what rules it uses.

My inclination is to stick with our current heuristic rather than attempting to do something more complicated, which will also be wrong some of the time — but harder to predict and reason about.

With the current simple heuristic in place, you can get the intended behavior simply by reordering the statements in the file. Of course, it would be even better to eliminate the dependency on import side effects regardless of statement ordering.

smackesey

comment created time in 6 hours

issue commentmicrosoft/pyright

Exhaustive return points fails for two union typed variables

The challenge here is that pyright's type analysis engine is a just-in-time evaluator. When it is asked to evaluate the type of a parse tree node, it can satisfy that request by evaluating a minimal number of other dependent types. It hops around (even between files) as necessary to satisfy the original request, and it caches the type of evaluated nodes in memory to satisfy subsequent requests.

By contrast, mypy always analyzes entire files, and it performs multiple passes until all types in the file converge.

Pyright's approach performs much better, and it is key to providing interactive language service features like completion suggestions, which must return results to the user with minimal delay.

Pyright is able to do a just-in-time analysis by building the control flow graph before evaluating types. It doesn't perform multiple passes, so it cannot discover changes to the code flow graph based on new type analysis information. The code flow graph necessarily needs to be built first.

In the code sample above, determining whether the else portion of the code flow graph can be executed requires the evaluation of types. In other words, the evaluation of types affects the code flow graph in this instance. That makes this feature very difficult to implement given pyright's architecture.

I'll continue to think about possible solutions, but I wanted to share with you some details about why this isn't a straightforward enhancement request.

ianliu

comment created time in 12 hours

issue commentmicrosoft/pyright

Exhaustive return points fails for two union typed variables

The "implied elif" logic works with a single variable but not with multiple variables. That's why your first example bar works when you remove the else statements but the second does not.

Yes, I agree there's value in retaining the elif statements for the reason you noted.

ianliu

comment created time in 12 hours

issue commentmicrosoft/pyright

Exhaustive return points fails for two union typed variables

This is behaving as intended. Pyright supports a feature called "narrowing for implied else", which handles typical exhaustive return patterns. In your case, you're including the else clauses even though they are provably never needed. If you enable pyright's reportUnnecessaryIsInstance check (which is on by default in strict mode), it will highlight the fact that some of your isinstance calls are unnecessary. If you remove the unnecessary code, it will type check fine, and it will be robust to change in the code (e.g. if you expand the union to include more types in the future).

from typing import Union

def bar(a: Union[str, int]) -> bool:
    if isinstance(a, str):
        return False
    else:
        return False

def foo(a: Union[str, int], b: Union[str, int]) -> bool:
    if isinstance(a, str):
        return True
    else:
        if isinstance(b, str):
            return False
        else:
            return False

I will also explore adding logic to handle the exhaustive return usage pattern in your code sample above. I'll see if I can find a way to add it without introducing too much performance overhead or false positives.

ianliu

comment created time in 13 hours

issue commentpython/mypy

Implicit re-exports should not be disabled for installed PEP 561 packages

Implicit re-exports should always be allowed in PEP 561 packages, but that's not the case right now.

This contradicts what (I thought) we had agreed to in the typing-sig discussion on this topic. If a package claims to be py.typed, then it needs to follow typing rules and be explicit about what symbols are exported from each submodule. Pyright assumes that py.typed packages never use implicit re-exports. Here is the guidance we have been providing to package authors, and pyright's implementation is consistent with this guidance. I would encourage mypy to default to the same behavior to provide consistency for package authors.

GPHemsley

comment created time in 13 hours

push eventmicrosoft/pyright

Eric Traut

commit sha bfc4fc3a133f5aad9f58bb362259fab46fd68cac

Updated typeshed stubs to the latest version

view details

push time in 2 days

issue commentmicrosoft/pylance-release

Symbol renaming not working in open file mode

This will be fixed in the next release. When in "single-file mode", the rename will be performed only within the current file. In "workspace mode", the rename will be performed across the workspace.

NemoYuan2008

comment created time in 2 days

push eventmicrosoft/pyright

Eric Traut

commit sha 1d015d2db3b0e17840eaa1ef6293dda01df14051

Fixed bug that prevented "rename symbol" from working when in single-file mode. Rather than failing, it will now perform a rename only within a single file.

view details

push time in 2 days

push eventerictraut/typeshed

Eric Traut

commit sha 5007a768fc9859f3fde06e996a014e853641ba23

Added dataclasses.KW_ONLY to the allowlist to make CI pass

view details

push time in 2 days

issue commentmicrosoft/pylance-release

Syntax check is not correct.

If I run that code in the python interpreter, I receive the following runtime exception:

  File "test.py", line 6
    '''
IndentationError: expected an indented block

That matches the error provided by pylance. So I think it's doing the right thing here.

kgflying

comment created time in 2 days

issue commentmicrosoft/pylance-release

Mutable objects(e.g. list) cannot be dictionary keys

Technically, it's not mutability that makes a key illegal. Rather, a key must be hashable (i.e. it must support the Hashable protocol).

There's nothing in the current typeshed type definitions that indicate this requirement. It would be possible for pylance to hard-code this knowledge specifically for dictionary expressions, but it would be an expensive check. Given how frequently dictionary expressions appear in most code, it would slow down analysis.

Mypy does not report an error in this case either.

For these reasons, I'm reluctant to add this check.

CodeCrazy-ywt

comment created time in 2 days

issue commentmicrosoft/pyright

Add support for new `kw_only` and `KW_ONLY` symbols in `dataclass` (Python 3.10 new feature)

This change is dependent on typeshed change https://github.com/python/typeshed/pull/5826.

erictraut

comment created time in 2 days

issue commentmicrosoft/pyright

Narrow type in negative case when class is declared as final

Thanks for the suggestion. This will be addressed in the next release.

ethframe

comment created time in 2 days

push eventmicrosoft/pyright

Eric Traut

commit sha a7df47407502283eb3ac80f31831d9a76dd53462

Updated the "type(x) is y" type narrowing logic to handle the negative case when the class type is "final".

view details

push time in 2 days

issue commentmicrosoft/pylance-release

Allow line level suppression of import errors

The "#type: ignore" at the end of the line is not working on a single line only as described earlier in this issue.

I'm not able to repro this. Could you provide a code sample that exhibits this bug? Or if the code in question is available in a public github repo, I can take a look at it there.

Another technique you might find useful... you can disable specific classes of errors at the project or file level.

MrJoosh

comment created time in 2 days

issue commentmicrosoft/pylance-release

Suppress reportUnboundVariable for a variable or a code block

Apologies if I came across as condescending. Many Python developers have relatively little experience with static type checking, and I've found that they are not familiar with ways to address type errors. If you're happy with the approach you've taken, the by all means continue to use it. I'm just sharing my experience and advice based on my own use of static type checkers in multiple programming languages. I would not recommend using the approach you're advocating above.

hzhwcmhf

comment created time in 2 days

issue commentmicrosoft/pylance-release

Suppress reportUnboundVariable for a variable or a code block

Pyright (which is the type checker upon which pylance is built) doesn't support # type: ignore[code]. That's a mypy-specific extension. Pyright does allow you to specify a code in brackets, but it ignores the code. Any such codes would be type-checker-specific anyway, since there's no standardization in terms of error codes or error names.

The best way to handle reportUnboundVariable violations is to fix the issue that's causing it in the first place. Often times that means initializing the variable prior to a loop or within an else block. Most programming languages wouldn't allow this sort of an error to exist because variables need to be declared, but Python is a bit lax here, which leads to some common errors.

I strongly recommend against using # type: ignore to suppress errors. As you said, it makes the code look bad, and it's really just covering up the problem rather than addressing it. This leaves your code vulnerable as is continues to evolve. I use # type: ignore only as an absolute last resort. I can't remember the last time when I needed to resort to it. There's almost always a better choice.

hzhwcmhf

comment created time in 2 days

issue commentmicrosoft/pylance-release

Allow line level suppression of import errors

Pylance isn't a "linter", at least by the traditional definition. Linters are concerned with code style issues like line length and naming conventions. Pylance doesn't concern itself with code style issues. If you want to enforce those types of issues, then pylint is a good choice.

Pylance is built on pyright, which is a static type checker, so it follows the type checking standards laid out in PEP 484 and related specifications. PEP 484 specifies that # type: ignore at the end of a line is the way to silence type-related errors.

I'll point out that there's almost always a better way to eliminate a type error than silencing it with a # type: ignore comment. Pyright even supports a mode where # type: ignore is disallowed, and I use that mode in my team's code base because I don't want anyone to use # type: ignore. IMO, it should be a choice of last resort when all other options fail. If you need suggestions for how to address specific type errors, feel free to post questions in the discussion section, and we can provide advice.

MrJoosh

comment created time in 2 days

issue commentmicrosoft/pyright

Pyright should not report "obscured def" errors for conditionally defined functions

If you're not sure whether a particular behavior is intended, feel free to post an issue or a question in the discussion section.

Pyright attempts to conform with all of the typing standards. However, these standards leave freedom for individual type checkers in terms of specific errors, inference behaviors, type narrowing behaviors, constraint solving heuristics, etc. We've intentionally deviated from mypy's behaviors in cases where we think it makes sense. It is not a goal to be 100% compatible with the behavior of mypy. When we receive a bug report or feature request, we take into account mypy's behavior, but we don't consider ourselves beholden to it. Of the four most popular non-mypy Python type checkers (pyright, pyre, pytype and PyCharm), pyright is closest to mypy in terms of behavior (or at least I have been told as such by others).

If you like the way mypy works and you have already accommodated its behaviors (and eccentricities) in a large code base, then you may find that it's not worth using pyright.

If you are using VS Code, then I recommend you use pylance rather than pyright. Pylance is a superset of pyright and adds a bunch more useful language server features. By default, type checking is disabled in pylance, so you will not see any type errors by default. You can use pylance alongside mypy if you want.

gwk

comment created time in 3 days

issue closedmicrosoft/pyright

Type guard regression on 1.1.159

Not sure how to properly describe the error itself, but I just notice this regression:

from typing import Optional

d: dict[str, int] = {}
mapper: dict[str, Optional[str]] = {}

def foo(bar: list[str]):
    return sum(d[mapper[i]] for i in bar if mapper[i] is not None)

When running pyright I get:

❯ pyright foo.py
No configuration file found.
No pyproject.toml file found.
stubPath /tmp/xxx/typings is not a valid directory.
Assuming Python platform Linux
Searching for source files
Found 1 source file
/tmp/xxx/foo.py
  /tmp/xxx/foo.py:7:16 - error: Argument of type "str | None" cannot be assigned to parameter "k" of type "str" in function "__getitem__"
    Type "str | None" cannot be assigned to type "str"
      Type "None" cannot be assigned to type "str" (reportGeneralTypeIssues)
1 error, 0 warnings, 0 infos 
Completed in 0.653sec

Which is not considering the if mapper[i] is not None which makes it for sure a str and not None

This was not an error on 1.1.158

closed time in 3 days

bellini666

issue commentmicrosoft/pyright

Type guard regression on 1.1.159

This should not have worked in previous versions. If it did, that was a bug.

You are using a subscript expression mapper[i] with a dynamic index. Pyright performs assignment type narrowing for subscript expressions only when the subscript is an integer or string literal (like mapper[1]). When the subscript is dynamic, it's unsafe to perform narrowing.

If you want to use the above pattern, the recommended workaround is to use the walrus operator, like this:

def foo(bar: list[str]):
    return sum(d[x] for i in bar if (x := mapper[i]) is not None)

Not only will this address the type issue, it will also make your code run faster.

bellini666

comment created time in 3 days

issue closedmicrosoft/pyright

Pyright should not report "obscured def" errors for conditionally defined functions

Consider the following:

  if cond:
    def f() -> str:
      return "YES"
  else:
    def f() -> str:
      return "NO"

Pyright issues an error: Function declaration "f" is obscured by a declaration of the same name.

However this is perfectly valid, well typed and useful code. For example, conditional defs like this can be used to lift a conditional test out of a performance-sensitive function. Mypy accepts this code without errors.

Here is a complete repro:

from sys import argv

def main() -> None:
  _, cond = argv

  if bool(cond):
    def f() -> str:
      return "YES"
  else:
    def f() -> str:
      return "NO"

  print(f())

if __name__ == '__main__': main()

closed time in 3 days

gwk

issue commentmicrosoft/pyright

Pyright should not report "obscured def" errors for conditionally defined functions

This is by design. You are redefining a symbol in the same namespace in two different ways. It is equivalent to the following:

if cond:
    f: str = ""
else:
    f: int = 2

I think you would agree that this should generate an error.

I would recommend against using the pattern of declaring functions within conditional statements, but if there's no way around it, then here's the recommended workaround:

if 1 + 1:
    def f_yes() -> str:
        return "YES"
    f = f_yes
else:
    def f_no() -> str:
        return "NO"
    f = f_no
gwk

comment created time in 3 days

pull request commentpython/typeshed

Stubs for google.cloud.ndb the Google Cloud Datastore ndb client library

When you say "pointing to my local typeshed checkout", how are you configuring that in pyright? Are you specifying the "typeshedPath" in your pyrightconfig.json file? That's the best way to replace the bundled typeshed stubs with a private copy.

There's another thing that might be happening here. Some versions of google's protobuf library shipped with a py.typed file in the google directory, and the py.typed was not marked partial. According to PEP 561, this overrides typeshed and all other submodules that start with google.. Look in your site-packages directory to see if you have a google subdirectory with a py.typed file in it. If so, make sure that py.typed file contains the line partial.

romanofski

comment created time in 3 days

issue closedmicrosoft/pyright

inference on setitem produces incorrect typing (pandas example)

Describe the bug Using pandas, if you create a new column pyright is inferring the type of that column as whatever it was set to, as opposed to using __getitem__ signature to infer the type

To Reproduce

import pandas as pd

df = pd.DataFrame({"x": [1, 2, 1], "y": [3, 4, 5]})

df["z"] = [7, 8, 9]
df["a"] = 3

reveal_type(df["z"])
reveal_type(df["a"])
reveal_type(df["x"])

produces

Type of "df["z"]" is "list[int]"
Type of "df["a"]" is "Literal[3]"
Type of "df["x"]" is "Series[Dtype@__getitem__]"

Expected behavior

Type of "df["z"]" is "Series[Dtype@__getitem__]"
Type of "df["a"]" is "Series[Dtype@__getitem__]"
Type of "df["x"]" is "Series[Dtype@__getitem__]"

VS Code extension or command-line VSCode pylance 2021.7.3

Additional context I don't think this is a pandas stubs problem, because the error occurs when setting a constant or a list, and in both cases, it's valid pandas code, and pandas converts the expression to a Series

closed time in 3 days

Dr-Irv

issue commentmicrosoft/pyright

inference on setitem produces incorrect typing (pandas example)

This is now addressed in pyright 1.1.159, which I just published. It will also be included in the next release of pylance.

Dr-Irv

comment created time in 3 days

issue closedmicrosoft/pyright

Enhance protocol matching support to take into consideration final attributes

Currently, pyright doesn't take into consideration a class or instance variable that is annotated as Final within a Protocol class. Nor does it take into account a final=True dataclass protocol.

See: https://github.com/python/mypy/issues/10850

closed time in 3 days

erictraut

issue commentmicrosoft/pyright

Enhance protocol matching support to take into consideration final attributes

This is now addressed in pyright 1.1.159, which I just published. It will also be included in the next release of pylance.

erictraut

comment created time in 3 days

issue closedmicrosoft/pyright

Type narrowing issue

Describe the bug Type narrowing doesn't work as expected in cases such as this:

from typing import Union


def bar (a: str):
    pass

def foo(a: Union[str,None]):
    if a == "1" or a=="2":
        bar(a)

Pyright give the error:

test.py:9:13 - error: Argument of type "Literal['1', '2'] | None" cannot be assigned to parameter "a" of type "str" in function "bar"
    Type "Literal['1', '2'] | None" cannot be assigned to type "str"
      Type "None" cannot be assigned to type "str" (reportGeneralTypeIssues)

Expected behavior Type narrowing should infer type of a inside the if block as Literal[1,2], not Literal[1,2] | None.

VS Code extension or command-line Tested with latest pylance version in VSCode and latest pyright version (1.1.158) on the command line.

closed time in 3 days

mariusmuja

issue commentmicrosoft/pyright

Type narrowing issue

This is now addressed in pyright 1.1.159, which I just published. It will also be included in the next release of pylance.

mariusmuja

comment created time in 3 days