Python: variable "tricking" try-exception, but works for if statement - python

I know the title seems crazy, but it is true. Here is my predicament. First, I am still a beginner at Python so please be considerate. I am trying to test if a variable exists. Now the variable comes from a file that is parsed in yaml. Whenever I try this code,
if not name['foo']:
print "No name given."
return False
Python does as you would expect and returns false. However, when I try to change it to this code,
try:
name['foo']
except:
print "ERROR: No name given."
raise
the exception is never risen. I have searched and searched and could not find any questions or sites that could explain this to me. My only thought is that the parser is "tricking" the exception handler, but that doesn't really make sense to me.
I have made sure that there were no white spaces in the name field of the document I am parsing. The field has the format:
*name: foo
*ver: bar
Like I have said, I made sure that foo was completely deleted along with any whitespace between lines. If anyone could help, it would be greatly appreciated.
EDIT:
And I apologize for the negative logic in the if statement. The function has to go through a series of checks. The best way I could think of to make sure all the checks were executed was to return true at the very end and to return false if any individual check failed.

A few things:
That shouldn't throw an exception! You're doing name['foo'] in two places and expecting different behavior.
The name doesn't behave like a dictionary, if it did you would NOT get a return of False in the first example, you'd get an exception. Try doing this:
name = {}
name['foo']
Then you'll get a KeyError exception
Don't ever have an except: block! Always catch the specific exception you're after, like IndexError or whatever

I don't understand why you think it would raise an exception. The key evidently exists, otherwise the first snippet would not work. The value for that key is obviously some false-y value like False, None, [] or ''.

Python does as you would expect and returns false.
The exception (KeyError) will be thrown only if there is no key in a dictionary (assuming name is a dictionary). If you do
if not name['foo']:
and it does not throw an exception, then it means that "foo" is in name but the value evaluates to boolean false (it can be False, None, empty string "", empty list [], empty dictionary {}, some custom object, etc. etc.). Thus wrapping name['foo'] with try:except: is pointless - the key is there.

Related

In case of an exception during a loop: How to return the intermediate result before passing on the exception?

def store(self) -> list:
result = []
for url in self.urls():
if url.should_store():
stored_url = self.func_that_can_throw_errors(url)
if stored_url: result.append(stored_url)
return result
Preface: not actual method names. Silly names chosen to emphasize
During the loop errors may occur. In that case, I desire the intermediate result to be returned by store() and still raise the original exception for handling in a different place.
Doing something like
try:
<accumulating results ... might break>
except Exception:
return result
raise
sadly doesn't do the trick, since trivially the raise stmt won't be reached (and thus an empty list get's returned).
Do you guys have recommendations on how not to lose the intermediate result?
Thanks a lot in advance - Cheers!
It is not possible as you imagine it. You can't raise an exception and return a value.
So I think what you are asking for is a work around. There, I see two possibilities:
return a Flag/Exception along the actual return value:
Return flag:
except Exception:
return result, False
where False is the Flag telling that something went wrong
Return Exception:
except Exception as e:
return result, e
Since it appears, that store is a method of some class, you could raise the exception and retrieve the intermediary result with a second call like so:
def store(self):
result = []
try:
# something
except Exception:
self.intermediary_result = result
raise
def retrieve_intermediary(self):
return self.intermediary_result
The best answer I can come up with given my limited knowledge of Python would be to always return a pair, where the first part of the pair is the result and the second part of the pair is an optional exception value.
def store(self) -> list:
'''
TODO: Insert documentation here.
If an error occurs during the operation, a partial list of results along with
the exception value will be returned.
:return A tuple of [list of results, exception]. The exception part may be None.
'''
result = []
for url in self.urls():
if url.should_store():
try:
stored_url = self.func_that_can_throw_errors(url)
except Exception as e:
return result, e
if stored_url: result.append(stored_url)
return result, None
That said, as you have mentioned, if you have this call multiple places in your code, you would have to be careful to change it in all relevant places as well as possibly change the handling. Type checking might be helpful there, though I only have very limited knowledge of Python's type hints.
Meanwhile I had the idea to just use an accumulator which appears to be the 'quickest' fix for now with the least amount of changes in the project where store() is called.
The (intermediate) result is not needed everywhere (let's say it's optional). So...
I'd like to share that with you:
def store(self, result_accu=None) -> list:
if result_accu is None:
result_accu = []
for url in self.urls():
if url.should_store():
stored_url = self.func(url)
if stored_url: result_accu.append(stored_url)
return result_accu
Still returning a list but alongside the intermediate result is accessible by reference on the accu list.
Making the parameter optional enables to leave most statements in project as they are since the result is not needed everywhere.
store() is rather some kind of a command where the most work on data integrity is done within already. The result is nice-to-have for now.
But you guys also enabled me to notice that there's work to do in ordner to process the intermediate result anyway. Thanks! #attalos #MelvinWM

Else not raising errors in try-except-else

I'm working with a special try-except-else block in python where I try to fetch an value and if I fail I try to create it to use later.
However, the creation process is not perfect (it actually fetches from the internet and might fail for several reasons) and I noticed that when this fails it is not raised. The only information I get is that the value is not defined
NameError: name 'value' is not defined
Since the error can be many, I would very much appreciate to see the full trackback for the error in the else clause
Please note: all tries can raise KeyError and the instantiation process cannot raise this error, it's always something else. Here is a sample code:
try:
value = _list[key]
except KeyError:
try:
number = kwargs['number'] # A number might be parsed via kwargs
except KeyError:
clean_everything()
value = None
raise KeyError('Value not found! ' +
'Need a number to create new instance!')
else:
value = Value(number=number) # This instantiation can raise other errors!
_list[homeTeam] = _listnumber[number] = value # Update lists for future reference.
finally:
print value
Anyone got any ideas on why the else clause is not raising? Is there a better way to write this?
Thanks,
EDIT: Added treatment for value inside the nested try.
This is leading me to believe that nested tries doesn't work as the nested errors will only be raised after the outer-most finally.
i.e. inner errors are raised only after the outer try is completed.
This might lead to a new question: how can I properly raise an error inside an except clause?
Trying to access value without first defining it indeed raises NameError. If KeyError is raised because key is not in _list, then value will never get defined in the try block. Because you redefine value in both of the except blocks, this should be OK there. However, your finally block does not redefine value before accessing it, so you would get a NameError there in the following cases:
value = _list[key] raises an error other than KeyError (e.g. NameError when _list is not defined)
clean_everything() raises any error
value = Value(number=number) raises any error
You can define value before the try block so that it is always defined no matter what happens inside the try block.

Do you have to check if an array element exists (not null string) in Python3?

If an associative array exists in Python3, is it a waste to check if an element of it exists rather than just using it?
Should you:
if 'name' in array and not array['name'].startswith('something'):
Or should you just:
if not array['name'].startswith('something'):
... And will Python3 handle it "for" you?
You can do -
if not array.get('name', 'something').startswith('something'):
get() function returns the second value by default if the key ( name ) is not found.
So in above case , this would return something , if key is not found in the dictionary, and because of the .startwith() and the not , the complete conditional expression would evaluate to False , as it would be doing in OP's first example .
The answer really depends on where does your array come from. Also on what can you do as a result of the check.
For example if it comes from the user, then yes, you definitely should check if name exists, so that you can respond with a better error message than some big stacktrace.... KeyError: "name".
If it's an internal structure you created and it's not exposed for user modifications, or if it's exposed via API but the expectation is that nobody should touch it, then don't bother checking. If it's missing, then it's an internal exception the developers should see and fix.
Of course there may be a situation where you don't really care if it was provided or not, because you have a reasonable fallback. Then a default value like in Anand's answer is a good solution too. array.get('name', some_default)
Python won't handle it for you. You'll have to do some work in both cases. Try to write code that makes sense to you later, because you'll write it once and read it (and maintain it) many times over.
You can do it two ways, pick the one that makes the most sense to you:
obj = array.get('name')
if obj and not obj.startswith('something'):
pass
In your second option, you'll have to catch the exception:
try:
if not array['name'].startswith('something'):
pass
except KeyError:
print('name does not exist in array')
Just do ...
if not array.get('name',"").startswith('something')
If name exists then it returns array['name'] else it will return empty string .

marching query does not exist in if statement

well go to the question. I make this query:
Puntuaciones.objects.filter(bar__id=b).get(usuario=v.usuario2)
This works perfect, but when i put this in a if statement, like this:
if(Puntuaciones.objects.filter(bar__id=b).get(usuario=v.usuario2)):
I have a problem, when the query returns a object Puntuaciones i haven't any problems, but when there isn't a result it throws me:
Exception Value: Puntuaciones matching query does not exist.
So my question is, How i can do a if statement for a query if this query can be unsuccessful.
I try with Q objects, with exists()(this just works for querysets... I dont know how make it work. Any suggestion? Thank you
get() is either returning an object or throws ObjectDoesNotExist exception, catch it with the help of try/except:
try:
Puntuaciones.objects.filter(bar__id=b).get(usuario=v.usuario2)
# do something if object was found
except Puntuaciones.DoesNotExist:
# do smth if nothing found
Another options is to use exists():
if Puntuaciones.objects.filter(bar__id=b, usuario=v.usuario2).exists():
# do something if object was found
I would suggest a try: except: pair. That way, when the exception is triggered, you will be in the appropriate location to perform the code. Note that you really should use the correct exception type in the setup. Note that if you want to skip processing in the except, you need to put in an explicit pass (python noop) statement. Otherwise you will get an indentation error.
try:
if(Puntuaciones.objects.filter(bar__id=b).get(usuario=v.usuario2)):
# put code in here
pass # dummy statement to be replaced by actual code.
except:
# Put exception code here.
pass

Raise error after unreached return statement in Python?

Relevant code:
def return_t_done(queryList):
for item in list:
if item[0] == "t_done":
return int(item[1])
raise Error("Attempted to get t_done's value, yet no t_done exists")
So basically this code runs through a nested list in the form of
[[spam,eggs],[ham,yum]]
and looks for a field ([[field,value],[field,value]]) of 't_done' and, when it finds it, returns the value associate with that field. There should always be a t_done field, but in case there isn't (this is parsing automatically generated log files using an app I didn't write and can't access the source code for, so who knows what could go wrong) I would like to elegantly raise an exception in the most appropriate way possible.
That said, this seems like the most elegant way to me, but I'm not particularly versed in Python, and my research into the docs confused me a bit. So, should I wrap the for loop in a try, except clause? Should I write a class for this error? Should I raise an exception instead of an error? Is it okay as written?
Thanks!
The way you have it is fine. But one other possibility is to use the little-known else cause for the for loop. That is evaluated if and only if the for loop completes successfully.
for item in list:
if item[0] == "t_done":
return int(item[1])
else:
raise Error(...)
Note the indentation: the else lines up with the for, not the if.

Categories

Resources