Finding execution path leading to exception - python

My IDE is warning me against a possible variable reference before assignment. But I'm having trouble spotting it. I'm ready to turn in a bug report, but maybe you can see what I'm not seeing.
The variable in question is a named check_dialog and the reference before assignment warning happens at the end of the following code snippet in the line I marked for you:
if dialog:
validate = None
string = False
if dialog == Prompt.YES | Prompt.NO:
check_dialog = lambda c: chr(c) in 'yn'
elif dialog == Prompt.CONTINUE | Prompt.QUIT:
check_dialog = lambda c: chr(c) in 'cq'
elif dialog == Prompt.YES | Prompt.NO | Prompt.QUIT:
check_dialog = lambda c: chr(c) in 'ynq'
else:
raise ValueError('Invalid dialog argument.')
answer = None
while not answer:
self.addstr(0, 1, prompt)
if string:
curses.curs_set(True)
curses.echo()
answer = self.getstr(1, 3)
curses.noecho()
curses.curs_set(False)
elif dialog:
while not check_dialog(answer): # warning here!
answer = self.getch()
else:
answer = self.getch()

Your IDE is not "thinking" about every possible value of your variables (in most cases, this would be impossible) and instead is using heuristics to prevent common mistakes. In this case, it has noticed that check_dialog is defined within an if condition, but not in every case. Yet it is used below this condition. That might be an UnboundLocalError!
As programmers, we can reason this out and see that the code paths it has noticed are protected. The else case raises a ValueError which will not be caught, and the usage is protected by (el)if dialog in both cases, so this will not be a problem.
It is not a bug in your IDE, because it is doing what it is supposed to. If it really bothers you and you can't otherwise silence the warning, you can unnecessarily define something like check_dialog = None over the top of the first if dialog, and it will shut up. However, it is also not a bug with your program/code, which as reasoned above will not cause an UnboundLocalError. This is safe to ignore, and because of how your IDE probably works a bug report would just be closed.

Related

How can I use multiple things to check in an else statement?

I am trying to create a machine learning program. So far, I have stored each of the 'learned' meanings in a text file in a list of all things that are attribute to that meaning. The text file is read by a python file that generates class objects that attribute the data in the text file to them. Then, in a master python file, the main prompt is where I am having trouble.
else:
try:
for obj in gc.get_objects():
try:
if isinstance(obj, LearnedClasses.learned):
if str(user.lower()) in obj.keys:
exec(obj.exstring)
chat()
break
except:
raise Exception
except Exception:
user = user.split()
for x in user:
learnlist.append(x)
learnch = random.choice(learnlist)
learnp = input("What does '{}' mean?".format(learnch))
learn(learnch, learnp)
chat()
This code is what follows the basic 'built-in' responses that I made. This is what happens after it can not find any keywords in the built-in section. I am using GC to collect all of the class objects that were generated from the text file. Then, if the prompt matches any keywords with any of its 'learned' keywords, I intend for it to respond with the responses set for that class. However, I can not get it to move on to the if ALL ELSE fails part, which starts at except Exception. How could I arrange this so that it can do what I described? Thank you.
It's a bit hard to follow this code; a couple of suggestions for improvement:
raise Exception has a typo: it should be raise Exception()
There's not much point having an except block which just raises an exception with no additional information; omit it?
The garbage collector is not a good place to store information that you will, in fact, need later; can you change the code so that the learned classes are stored in a list in a variable, which is returned or passed around somehow?
As a general rule, you should never touch the garbage collector unless you're running out of memory, and then only to figure out how to help it discard things.
As others have suggested in the comments, rather than try/except, use something like a flag variable to keep track of whether you've already answered, or the for/break/else construct:
for lc in learned_class_list:
if str(user.lower()) in lc.keys:
exec(lc.exstring)
chat()
break
else:
user = user.split()
for x in user:
learnlist.append(x)
learnch = random.choice(learnlist)
learnp = input("What does '{}' mean?".format(learnch))
learn(learnch, learnp)

Indentation Problems Pycharm

I'm new to coding and python so i'm doing an online free course. There's one task that i'm supposed to do that is not working properly, and apparently the problem is identation. Here's the code:
c = 0
while c < 5:
c += 1
if c == 3:
continue
print (c)
So the last line is aligned with the previous one, and the code only runs properly after I delete one identation from the last line. How do I configure this to be automatic so I won't have to delete it all the time?
TLDR: In general, no, you cannot configure this automatically. However, there are some specific cases where we can say a statement is definitely in the wrong scope with the help of a linter. The onus is still on the programmer to actually correct the program, though, even with the help of the linter.
General Case:
No programming language can know what scope you'd like a statement to be in. That is for you, the programmer, to express in the language. Python's scoping happens to be determined by whitespace, not curly braces, like in some other popular languages (namely C/C++/Java/Perl).
Consider:
if x:
do_y()
do_z()
vs.
if x:
do_y()
do_z()
Both are legal Python programs, and both are (potentially) logically correct, depending on the application. Maybe you only want to call do_z() if x is true (first example). Or maybe you always want to call do_z() regardless of x (second example). Only the application developer can decide which they want. And which one you want might actually change over time, depending on circumstance. So it should be very clear that this decision (in general) cannot be made automatically.
Using pylint:
However, sometimes what we can say is that some statement is definitely in the wrong scope (like in your example above, a statement immediately after a continue can never be reached).
You can use a linter (like pylint) to help with this:
In test.py I've placed your question code and gave it a quick pylint:
(so) URSA-MattM-MacBook:stackoverflow mmessersmith$ cat test.py
c = 0
while c < 5:
c += 1
if c == 3:
continue
print(c)
(so) URSA-MattM-MacBook:stackoverflow mmessersmith$ pylint test.py
************* Module test
test.py:1:0: C0111: Missing module docstring (missing-docstring)
test.py:1:0: C0103: Constant name "c" doesn't conform to UPPER_CASE naming style (invalid-name)
test.py:6:8: W0101: Unreachable code (unreachable)
------------------------------------------------------------------
Your code has been rated at 5.00/10 (previous run: 5.00/10, +0.00)
Note that this line: test.py:6:8: W0101: Unreachable code (unreachable). That's telling you that line 6 can never be executed, regardless of program state.
Furthermore, note that any linter still cannot automatically correct indent. There are two legal possibilities for where the print(c) statement should be indented:
c = 0
while c < 5:
c += 1
if c == 3:
continue
print (c)
and
c = 0
while c < 5:
c += 1
if c == 3:
continue
print (c)
Both are legal (and reasonable) Python programs! The first will print c in every iteration of the while loop, the second will only print c after the loop has finished. Only you, the programmer, can decide which one you'd like. All the linter can say is "you almost certainly didn't mean to put a statement immediately after a continue, since that code will never be executed".
Again, it is up to you where you'd like the statement. No automated tool can automatically place the statement for you, because it can't possibly know what you want to accomplish.
In your code, print(c) is after continue. But statements after continue is not executed. continue works as ignore the statements after it and go for the next iteration. So print(c) doesn't work. Your code should be like this :-
c = 0
while c < 5:
c += 1
if c == 3:
continue
print (c)
This will print
1
2
4
5

"Using variable X before assignment" error pops up for no obvious reason

I've read about this error, but the thing is that I do not think I should be getting this error here, and I've never seen anyone have this problem. Here is my code:
def substituie(caractere):
lista_optiuni = genereaza_lista_substitutii(caractere)
global sir
sir = sterge_lambda(sir)
while lista_optiuni != []:
index = randrange(0,len(lista_optiuni))
if len(sir) + len(lista_optiuni[index])-1 > 60:
lista_optiuni.remove(lista_optiuni[index])
else:
sir = sir.replace(caractere,lista_optiuni[index],1)
sir = sterge_lambda(sir)
return True
return False
The problem is with my variable "Sir". I have an outside variable called "Sir" and I want my function to change certain aspects of it without passing it as a parameter. So I declare it as global inside the function, yet I still get an error as if my function has no idea who this variable is. Also, the code works fine and does exactly what I want it to do, but the red line below it and the "1 problem" warning in the Source Control tab really bothers me. This happened in more places in my code, for example:
global sir
sir = sir.replace(caractere,lista_optiuni[index],1)
This line of code also gave the same error in the same function but for some reason it no longer does, instead it focuses on the 3rd line of code from my function. Also, sterge_lambda(sir) simply replaces a certain caracter with '', not sure if a one line function is worth making, but whatever.
P.S.: The code is written in Python 3.7 inside VSCode.
The default linter used by Visual Studio, pylint, is complaining because it hasn't found any code that would assign something to sir before substituie() is called and the expression sterge_lambda(sir) in the statement sir = sterge_lambda(sir) is run.
This could indicate a problem with your code, because there is no global name sir set anywhere by the time subtituie() really is executed, then you'd get a NameError: name 'sir' is not defined exception.
If you are sure that this will never happen in your project, you can tell pylint to ignore this specific issue by adding a # pylint: disable=... entry on the same line, where ... is replaced with the error code that's visible in the dialog box when you hover over the red line:
sir = sterge_lambda(sir) # pylint: disable=E0601
or you can use the symbolic name, for E0601 that's used-before-assignment:
sir = sterge_lambda(sir) # pylint: disable=used-before-assignment
You can see the full list of message names and their error codes by running pylint --list-msgs on the command line, or you can access information on individual messages with pylint --help-msg=<code-or-name>.
It seems that the order in which I define functions and declare global variables used inside said functions matter. While:
def substituie(caractere):
lista_optiuni = genereaza_lista_substitutii(caractere)
global sir
sir = sterge_lambda(sir)
while lista_optiuni != []:
index = randrange(0,len(lista_optiuni))
if len(sir) + len(lista_optiuni[index])-1 > 60:
lista_optiuni.remove(lista_optiuni[index])
else:
sir = sir.replace(caractere,lista_optiuni[index],1)
sir = sterge_lambda(sir)
return True
return False
sir = start
would cause an error message to pop up, although the execution still proceeds normally, this code:
sir = start
def substituie(caractere):
lista_optiuni = genereaza_lista_substitutii(caractere)
global sir
sir = sterge_lambda(sir)
while lista_optiuni != []:
index = randrange(0,len(lista_optiuni))
if len(sir) + len(lista_optiuni[index])-1 > 60:
lista_optiuni.remove(lista_optiuni[index])
else:
sir = sir.replace(caractere,lista_optiuni[index],1)
sir = sterge_lambda(sir)
return True
return False
does not complain about any problem. The only difference is that in the first version the variable used in the function is declared after the function definition. This is not an execution problem, just the way it is interpreted.

If statement executes when it's false

I am getting "Knob already attached to a node" when i try to add a knob
i get this when i try to run my code from menu.py button.. if i run the script from the script editor i don't get the error.. why is that?
for i in nuke.allNodes():
if not i.knob("tempMb"):
if sum0 == 0:
nuke.message("first tmp knob created")
i.addKnob(t)
elif sum0 != 0:
nuke.message("second tmp knob created")
else:
nuke.message("no nob created")
Even though i check if there is a knob named tempMb .. it still executes it as if there was not, when there is..
edit: "t" is earlier defined as Int_Knob...
Thanks!
Try the following solution:
import nuke
t = nuke.Int_Knob( 'tempMb', 'tempMb' )
for i in nuke.allNodes():
if not i.knob("tempMb"):
if nuke.exists("sum0"):
nuke.message("First tmp knob created")
i.addKnob(t)
else:
nuke.message("Second tmp knob created")
else:
nuke.message("No knob created")
First I'm going to change the elif to just else because your if condition is already testing the elif condition and I don't see how that would be changing while in this code.
for i in nuke.allNodes():
if not i.knob("tempMb"):
if sum0 == 0:
nuke.message("first tmp knob created")
i.addKnob(t)
else:
nuke.message("second tmp knob created")
else:
nuke.message("no nob created")
Second I'm assuming that i.knob(string) doesn't check for the existence of a knob by that name, or your code would behave more as you expected. So when I read the docs it seems like a couple of things may happen:
The nodes might or might not be knobs in the list returned. If you know you only want knobs you could filter by class type. I think that might look like nuke.allNodes(nuke.Knob).
I don't think a nuke.Knob.knob(str) is a test for its name or label. I read the docs as implying that your test should be: if i.name != "tempMb": or possibly if i.label != "tempMb" it depends on how you created t.
Moving on though, I think there may be a logical error here. If you have 2 nodes (and if you make the above changes, let's assume they're both knobs), and as you loop over all nodes the first one is the tempMb, then when you check the second one it won't be that and you'll try to add t, which I assume is named tempMb too. So that's why it looks to you as though the negative condition is always occurring.
You need to restructure in one of two ways:
Before the loop, set a false boolean, in the loop set it to true when the knob is tempMb is found; you may as well exit the loop as soon as this occurs. After the loop check the boolean to see if it's safe to add t.
I see a possible function nuke.exists(s) which tells you if you have any "item" named s.
Maybe remove the loop and write the following:
if not nuke.exists("tempMb"):
# Add your knob. I'm actually not seeing `addKnob` in the docs.
nuke.exists("Name of Knob") will check if your knob exists. Try using that in your if statement.
More details on Nuke forum.
See also Documentation on nuke.exists

Python - What's the use of if True:?

I just came accross the following code in an existent project, which I'm working on:
if True:
x = 5
y = 6
return x+y
else:
return 'Something
Inside the if True are lots of conditions and some will also return the function already.
Why would somebody write in that way? The code contained some other bugs also, but was just wondering about the if True: statement as it didn't make any sense to me. Probably also pretty stupid to ask it, but was wondering hehe.
It might be a remnant of debugging or refactoring. It may be that instead of True, there was orginally a condition or variable there but it has now been replaced by True. The developer perhaps left it there without refactoring or cleaning it up all the way.
If you're free to edit the code as you wish and you're sure that the else is no longer needed, then you can remove it. It indeed makes no sense to have code in your codebase that will never run.
True doesn't necessarily mean True
True = False
if not True :
print "True is false" # This prints ok
Honestly, I don't think anyone would code like this.
Does not make any sense to me, my guess is that someone wanted to have two distinct code paths that he could alternate between a'la using #if 1 .. #else -> #if 0 ... for debugging or such purposes.
Other possibility was that, as #SimeonVisser suggested, the original developer was refactoring or cleaning up the code (and did not have an emulator that allows one to easily remove 1 step of indentation from a block of code)
It could be a flag used for debugging.
It's simply used to ensure that the else: block is never executed.
I have used if True: for some blocks to ensure that my code really does what I want. Usage for debugging or refactoring.
All in all it makes no real sense to use this in an application but for testing or debugging it's somehow acceptable.

Categories

Resources