In the following code
def func(flag):
# some operations...
if flag:
variable = 1
# some other operations...
if flag:
if variable:
pass
else:
pass
# more operations about variable
variable will be reported unbound no matter whether flag is not changed. I know it's not simple for pylance to find variable are defined because of the unchanged condition, but I didn't found a satisfying workaround.
As discussed in #216 , a workaround is defining variable in the outer space, like:
variable = None
However, I'd like to see errors if the code accidentally uses undefined variable outside if flag. It can be dangerous if None has meanings, such as passing variable to another function where None means default value.
Another workaround is disabling warnings on a specific line:
if variable: # type: ignore
pass
However, variable can be used in multiple lines and it is impossiblee to mark all of them.
I hope there are some ways to solve the problem without changing the program behavior.
One possible way is adding hint manually and telling pylance it is defined, which is compatitable with mypy for type checking.
def func(flag):
# some operations...
if flag:
variable = 1
# some other operations...
if flag:
variable: int
if variable:
pass
else:
pass
# more operations about variable
Another possible solution is disabling warnings in a block, rather than in a line or a file. (Update: a better way maybe follow pylint message control)
def func(flag):
# some operations...
if flag:
variable = 1
# some other operations...
if flag:
#pylance: disable-reportUnboundVariable
if variable:
pass
else:
pass
# more operations about variable
#pylance: enable-reportUnboundVariable
Leaving variables unbound on some code paths in your code is not a good idea. You can always work around this by pre-initializing your variable.
You mentioned that it can be dangerous if None has meanings. Can you give a concrete example of this?
Thanks for your quick response. My example is doing neural network prototyping using numpy:
The code is starting from
x = ... # x is np.ndarray
y = some_network(x)
loss = ...
And I want to add a patch before and after the codes (to try a new idea)
x = ... # x is np.ndarray
if config.extra_input_type == "a*b":
x = x + ... # x.shape: [a * b]
target_dim = 1 # store some variable for latter use
elif config.extra_input_type == "a*b*c":
x = np.expand_dims(x, -1) + ... # x.shape: [a * b * c]
target_dim = 2 # store some variable for latter use
y = some_network(x)
loss = ...
if config.extra_input_type:
loss += np.mean(np.sum(y, axis=target_dim))
I can add target_dim=None before the patched code, but it is not very safe if I mistaken the latter condition:
x = ... # x is np.ndarray
target_dim = None
if config.extra_input_type == "a*b":
x = ... # modify x for new input
target_dim = 1 # store some variable for latter use
elif config.extra_input_type == "a*b*c":
x = ... # modify x for new input
target_dim = 2 # store some variable for latter use
y = some_network(x)
loss = ...
# if config.extra_input_type: # I may forgot to add this line
loss += np.mean(np.sum(y, axis=target_dim))
# if ``target_dim`` is None, this code gets the sum of y.
# if ``target_dim`` is 1, this code first gets average of y along the first dim, and then gets the sum.
See the numpy documentation for numpy.sum. Here is a quick result:
>>> import numpy as np
>>> np.mean(np.sum([[1,2,3], [4,5,6]], axis=None))
21.0
>>> np.mean(np.sum([[1,2,3], [4,5,6]], axis=1))
10.5
In this case, I hope to see errors if I forgot if config.extra_input_type:, instead of no errors but wrong answers. (It doesn't worth hiding a bug just because I want to suppress the warning.)
I know your point that it is safe and tidy to bound variables at all code paths like C, but some of users are doing prototyping, and it is not always efficient to do that.
From my point of view,
I don't buy the argument that it's unsafe to initialize the variable to None. If it's unbound, the program will crash if you reference it.
If you really don't want to fix this in your code, you can suppress the diagnostic (the warning or error message) using a # type: ignore comment on the line where the diagnostic is being reported. Or you can suppress this diagnostic rule within the file by placing this comment at the top "# pyright: reportUnboundVariable=false", or you can disable the diagnostic rule entirely using the python.analysis.diagnosticSeverityOverrides setting.
If a variable is unbound, the program will crash and you will know it immediately so that you can go and fix the problem. If it is initialized with None instead, in the example @hzhwcmhf provided, the program would execute with unexpected behaviors without throwing any errors and it'll be much harder to figure out.
If a variable is unbound, the program will crash and you will know it immediately so that you can go and fix the problem. If it is initialized with
Noneinstead, in the example @hzhwcmhf provided, the program would execute with unexpected behaviors without throwing any errors and it'll be much harder to figure out.
Yes, that is exactly what I mean. It allows the program crash and let me figure out the problem.
I just hope pylance consider (1) providing a fine-grained message control, like pylint (Existing methods of supressing only works in a line, a file, and the whole project); or (2) providing a hint to solve the unbound issue in some paths, like mypy. (The examples of prefered solution are in my first comment.)
@erictraut
# pyright: reportUnboundVariable=false
Can it be extended so rather than being processed once per document/file scope it is processed per block with last value overriding for the rest of the document?
For example:
# pyright: reportUnboundVariable=false
some code
no unbound errors or warnings reported here
more code
inner scope
still no reports
# pyright: reportUnboundVariable=true
now it is reported, type default to global definition of severity for reportUnboundVariable
See #699, facing similar issue
These comments are designed to be at the document level, not used in inner scopes.
The workaround is to ensure that you don't have uninitialized variables in your code.
The workaround is to ensure that you don't have uninitialized variables in your code.
I wasn't really talking about this specific issue, because the problem discussed here and possible solutions (as proposed in OP) are more of general use than this specific case of unbound variables.
These comments are designed to be at the document level, not used in inner scopes.
Yes, and I'm suggesting to promote them to their own 'flat' block-scopes. If it is at the top of the document then it works for whole document until it meets counterpart that disables it. And then re-enables. Pyright already respect pylint's-alike #type: ignore for each line. What exactly is the problem to adapt Pyright-specific directives to be more flexible?
Static typing analysis will never work as properly or as comfortable in Python as in a language that designed around such feature (i.e.C#) so, inevitably, there is a need for more flexible workarounds.
I would take this:
# disable some pylance type check
code
code
code
code
code
long code line long code line long code line long code line long code line long code line
# enable some pylance type check
over
code #type ignore
code #type ignore
code #type ignore
code #type ignore
code #type ignore
now even longer code because we have comment at the end long code line long code line long code line #type ignore
any day a week
I have quite a few try: except blocks. Variables used in try are ignored and reportUnboundVariable problem messages create a mess. How does one resolve this?
@reservoirinvest, can you provide a minimal code sample that demonstrates the issue you're seeing?
Here is a sample...
Pylance is doing the right thing here. It's telling you that you have a bug in your code. If an exception occurs within the try block, s500 will be unbound after the except block is executed, and an exception will be generated (outside of the try/except statement) when you attempt to use s500.
You can easily fix this bug by assigning s500 an empty value (like []) before the try block. If you choose not to fix the bug, you can either ignore the warning or disable it.
Most helpful comment
Yes, that is exactly what I mean. It allows the program crash and let me figure out the problem.
I just hope pylance consider (1) providing a fine-grained message control, like pylint (Existing methods of supressing only works in a line, a file, and the whole project); or (2) providing a hint to solve the unbound issue in some paths, like mypy. (The examples of prefered solution are in my first comment.)