Skip to content

Instantly share code, notes, and snippets.

@ttsiodras
Last active August 21, 2017 09:48
Show Gist options
  • Save ttsiodras/88f0afc712b4bfc3fe16 to your computer and use it in GitHub Desktop.
Save ttsiodras/88f0afc712b4bfc3fe16 to your computer and use it in GitHub Desktop.
I just did something that depending on your viewpoint on coding, is either an insult to God and humanity, or absolutely brilliant.
#
# I inherited a large code base, where hundreds of code paths end up
# calling "common_function_called_in_gazillion_places".
#
# And the need arose for this function to access the HTTP request's
# headers...
#
# What to do? Refactor all the places leading up to here? In a dynamically
# typed language, with no compiler to tell us the places to refactor?
#
# NO - let's hack the universe instead.
def get_the_request():
"""
Look up the call stack to see if one of our callers has "self.request"
(i.e. the Pyramid request) and if so, return it
"""
for f in inspect.stack():
if 'self' in f[0].f_locals:
self = f[0].f_locals['self']
if hasattr(self, 'request'):
return self.request
else:
return None
def common_function_called_in_gazillion_places( variables, action ):
...
# Get the request from our callers, and extract the IP from it
request = get_the_request()
if request is not None:
ip_addr = request.remote_addr
if 'X-Forwarded-For' in request.headers:
ip_addr = request.headers['X-Forwarded-For']
if ip_addr not in ['127.0.0.1', 'localhost'] and \
isinstance(ip_addr, (str,unicode)):
event_data['context'] = {'ip': ip_addr}
....
# TADA!!!
#
# OK, now I can burn in programmer Hell - for all eternity.
@keturn
Copy link

keturn commented Jul 24, 2015

Yes, Python is a dynamically typed language, but PyCharm can usually help refactor anyway.

@cache-rules
Copy link

@ttsiodras you could probably implement this method as the author has, but add some logging to it, then attempt a find/replace, then periodically check your logs. You should be able to find any calls you missed and replace them over time while retaining some form of backwards compatibility.

@seanjensengrey
Copy link

@ttsiodras I had to do something similar in 2009 for a trac extension, for the same exact reason, so definitely absolutely brilliant, ;) . https://gist.github.com/seanjensengrey/84beab9d6f907c0dc433

Frame hacks are what make Python, Python. See http://farmdev.com/src/secrets/framehack/

Another option would be to put in a fixme into function_called_everywhere that logs all the invocation points.

def fixme(msg):
        print >>sys.stderr,"fixme:" + sys._getframe().f_code.co_filename + ":" + \
                sys._getframe().f_back.f_code.co_name + ":" + \
                str(sys._getframe().f_back.f_lineno) + ":" + msg

Effectively what you have done is enable dynamic scope for a lexically scoped language. One could also think of dynamic scope as a form of execution context or lightweight dependency injection.

@ttsiodras
Copy link
Author

@bertjwregeer Thanks - good to know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment