-
Notifications
You must be signed in to change notification settings - Fork 180
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
Global variables must be assigned to be used #666
base: main
Are you sure you want to change the base?
Conversation
pyflakes/checker.py
Outdated
self.name = name | ||
self.source = source | ||
self.used = False | ||
self.isUnassigned = isUnassigned |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new code should use snake_case
-- we're slowly migrating over the legacy code which uses the old casing
pyflakes/checker.py
Outdated
@@ -322,10 +322,11 @@ class Binding(object): | |||
the node that this binding was last used. | |||
""" | |||
|
|||
def __init__(self, name, source): | |||
def __init__(self, name, source, isUnassigned=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a bit of a double-negative and a boolean trap -- it would probably be best to change this to assigned=True
and make it keyword-only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure! should i wait for #660 to be merged before introducing a keyword-only arg?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I just merged it
pyflakes/checker.py
Outdated
in_global_scope = value.name in self.scopeStack[-1] | ||
if existing and in_global_scope and existing.isUnassigned: | ||
existing.isUnassigned = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is correct -- for instance a closure-scoped variable can shadow a global variable (and is a separate scope)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sweet, added a test case for that
pyflakes/checker.py
Outdated
except AttributeError: | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't be blindly catching AttributeError -- this is a programming error
def test_globalIsUndefinedIfNeverGivenValue(self): | ||
""" | ||
If a "global" is never given a value, it is undefined | ||
""" | ||
self.flakes(''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is necessarily true, a function later in the module could modify that variable and give it a value
def test_unusedGlobalAcrossFunctions(self): | ||
""" | ||
An unused "global" statement in another function does not define the name. | ||
""" | ||
self.flakes(''' | ||
def f1(): | ||
m | ||
global m | ||
|
||
def f2(): | ||
global m | ||
m | ||
''', m.UndefinedName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test shouldn't have been altered -- it was demonstrating a behaviour that should happen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at the moment, this is the one part of the diff i'm still iffy about. you're right, this should eventually just be un-skipped. pyflakes
at the moment only errors when:
def f1():
m
def f2():
global m
and it does not error if the global is defined in f1
for now i've just gone back to skipping it and reverted my changes, not sure if this is some bigger thing with pyflakes or if it's a simple fix i can do in here!
@@ -499,7 +515,7 @@ def fun2(): | |||
a | |||
a = 2 | |||
return a | |||
''', m.UndefinedLocal) | |||
''', m.UndefinedLocal, m.UndefinedName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this new error here is correct -- it seems to be correct on the main branch
bd0a4d6
to
a891547
Compare
a891547
to
bcf8194
Compare
pyflakes/checker.py
Outdated
@@ -1155,6 +1162,10 @@ def handleNodeLoad(self, node): | |||
continue | |||
|
|||
binding = scope.get(name, None) | |||
|
|||
if getattr(binding, 'assigned', None) is False: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah -- my point on this was more that this shouldn't be something we're guessing at -- it should always have this attribute or never have this attribute (it should be set unconditionally on construction)
global_scope = self.scopeStack[-1] | ||
if (existing and global_scope.get(value.name) == existing and | ||
not existing.assigned): | ||
# make sure the variable is in the global scope before setting as assigned | ||
existing.assigned = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pyflakes running on itself is showing this is probably not quite right -- or at least it's not happy about nonlocal
d0abe7d
to
5302f1f
Compare
Closes #590. This also adds a new
NoBindingForNonlocal
errorI'm pretty new to
pyflakes
so just let me know if I messed something up