unexpected unbound-name error on second walrus conditional assignment for same variable
Describe the Bug
I have a code sequence that's doing walrus assignments of regex matches within if statements, using the same variable name for the walrus assignment, and then using the walrus-assigned variable within the body of the if statement.
Due to it being part of the if condition itself, once inside the if statement body it is my understanding, there is no way that the walrus-assigned variable could ever be unbound.
Yet, this is an outcome I see with a specific code sequence.
Interestingly I'm doing two walrus assignments, if I remove the first once, the error goes away. And if I keep the first one but slightly tweak the if condition of the second one, it also goes away.
Full details are below.
Python code in question:
from re import compile
interface_re = compile(r"^foo")
ipv4_re = compile(r"bar$")
line = str()
if match := interface_re.match(line): # unbound-name error gone if this is removed
pass
# if match := ipv4_re.search(line):
if line and (match := ipv4_re.search(line)): # unbound-name error also gone if above alternate if is used instead
print(match)
Relevant software versions:
# pyrefly --version
pyrefly 0.39.1
# python3 --version
Python 3.14.0
Pyrefly error:
# pyrefly check walrus.py
WARN PYTHONPATH environment variable is set to `/myapp`. Checks in other environments may not include these paths.
ERROR `match` may be uninitialized [unbound-name]
--> walrus.py:12:11
|
12 | print(match)
| ^^^^^
|
INFO 1 error
Sandbox Link
No response
(Only applicable for extension issues) IDE Information
No response
Wonder if it might be the same RCA as #1378 ?
I don't know if my is exactly the same issue as OP, but I get unbound-name error even with a single walrus operator use
from typing import Any
def test(thing: Any) -> None:
if not (items := getattr(thing, "items")):
return
if not isinstance(items, tuple|list):
items = (items,)
for item in items: # <-- unbound-name error here
print(item)
While this works:
from typing import Any
def test(thing: Any) -> None:
items = getattr(thing, "items")
if not items:
return
if not isinstance(items, tuple|list):
items = (items,)
for item in items:
print(item)
cc @stroxler
Here's the most minimal example of walrus causing issues:
def f() -> None:
if a := True:
print(a) # no error here
print(a) # `a` may be uninitialized
But something that may break out of the scope (such as raise, or return) does not seem to cause issues:
def f() -> None:
if a := False:
raise
b = a # `Literal[False]` assigned if `False`, otherwise `Never`