-
-
Save ttsiodras/88f0afc712b4bfc3fe16 to your computer and use it in GitHub Desktop.
| # | |
| # 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. |
@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.
@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) + ":" + msgEffectively 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.
@bertjwregeer Thanks - good to know!
Yes, Python is a dynamically typed language, but PyCharm can usually help refactor anyway.