Created
July 18, 2010 11:49
-
-
Save rwilcox/480347 to your computer and use it in GitHub Desktop.
Python null handling pattern exploration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import with_statement # not needed in 2.6 I think? WD-rpw 07-17-2010 | |
# This file/gist explores nil handling in Python | |
# | |
# Hey look, there's a stackoverload question about the nill pattern in Python | |
# <http://stackoverflow.com/questions/2014105/null-pattern-in-python-underused> | |
# | |
# Where the vote seems to be that the exceptions are good (I agree), | |
# and the pattern should look like: | |
# if foo.bar is not None and foo.bar.baz: | |
# .... | |
# OR: | |
# try: | |
# foo.bar.baz | |
# except AttributeError, a: | |
# ... | |
# | |
# Which is OK, but a lot of code (and kinda smells). Especially if/when you have | |
# to make that check often in your problem domain | |
# | |
# | |
# So this file will look at the alternatives. (They certainly smell a little, | |
# I know.) | |
# | |
# | |
# | |
# Hmm, can a context manager solve our problems? | |
# ################################################################################### | |
from contextlib import contextmanager | |
@contextmanager | |
def out_nil_exceptions(squash_attribute_errors=False): | |
"""This generator will squash errors associated with | |
programming systems where: | |
#1: You might accidentally call a method on None | |
#2: You might accidentally mispell a method name, | |
- or it's been changed behind your back - | |
and want that stiffled (yes, a bad idea, and yes, | |
I've been on projects that have wanted this, on purpose) | |
TODO: maybe pass a logger instance here, so we can log this stuff... ??? | |
""" | |
try: | |
yield | |
except AttributeError, e: | |
if "'NoneType' object has no attribute" in str(e): | |
raise StopIteration | |
else: | |
if ("object has no attribute "in str(e)) and squash_attribute_errors: | |
# TODO: this if doesn't work... | |
raise StopIteration | |
else: | |
raise e | |
######## AND HERE'S WHERE WE USE IT, LADIES AND GENTS: | |
res = None | |
a = None | |
with out_nil_exceptions(): | |
res = a.bob() | |
print res or "was nil" | |
# SLIGHTLY ANNOYING THING - have to add an extra line each time | |
# it's only slightly better than nil checking yourself | |
# and since it's a generate you can't return stuff from it | |
# THE GOOD THING: I think it's better than Ruby/Rails alternative: | |
# | |
# fname = student.andand.first_name | |
# or | |
# fname = student.try(:first_name) | |
# | |
# The ruby alternative gets hairy when it's deeply chained... this Python solution doesn't | |
# | |
# student.andand.school.andand.principal.andand.firstname | |
# | |
# or | |
# student.try(:school).try(:principal).try(:firstname) | |
# | |
# Our Python alternative doesn't have nil checking baked into the code: | |
# | |
# principal_firstname = "Mr." | |
# with out_nil_exceptions(): | |
# student.school.principal.first_name | |
# | |
# Which IS better, actually. And better that it will catch ALL the nil errors in the code | |
# instead of salting the chain with (is it nil here?), and maybe forgetting one. | |
student = None | |
fname = "The Default" | |
lname = "Default Last name" | |
with out_nil_exceptions(): | |
fname = student.first_name | |
with out_nil_exceptions(squash_attribute_errors=True): | |
lname = student.last_name | |
print "fname = %s" % (fname or "nil") | |
print "lname = %s" % (lname or "nil") | |
############################################################################ | |
# A: maybe. Still slightly ugly, but at least it's explicit that we are squashing errors | |
# I'm not convinced that a context manager does solve our problems, but it's... interesting | |
# and I'm out of time. Have a better idea? Comment away |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment