Skip to content

Instantly share code, notes, and snippets.

@SavinaRoja
Last active August 29, 2015 14:05
Show Gist options
  • Save SavinaRoja/6b9e65970dc0db522d59 to your computer and use it in GitHub Desktop.
Save SavinaRoja/6b9e65970dc0db522d59 to your computer and use it in GitHub Desktop.
Some experiments with decorators that smooth API style changes transition
#Theoretical exercises in patterns to make API style changes smoother
from functools import wraps
def deprecated_in_favor_of(new_name):
def wrap(f):
@wraps
def wrapped_f(*args, **kwargs):
if not wrapped_f._called:
print(f.__name__ + ' deprecated in favor of ' + new_name)
try:
f(*args, **kwargs)
finally:
wrapped_f._called = True
wrapped_f._called = False
return wrapped_f
return wrap
def replace_method_if_overridden(*methods):
def wrap(f):
def check_methods(self, *args, **kwargs):
"""
Subroutine for inspecting methods for override flag.
"""
all_methods = [self.__getattribute__(m) for m in methods]
for method in all_methods:
#Look for overridden flag value
if not hasattr(method, '_overridden'):
return method
return f
@wraps
def wrapped_f(*args, **kwargs):
if wrapped_f._func is None:
wrapped_f._func = check_methods(*args, **kwargs)
wrapped_f._func(*args, **kwargs)
wrapped_f._func = None
return wrapped_f
return wrap
def override_flag(f):
f._overridden = False # Value unimportant, only presence as attribute
return f
class Spam(object):
deprecation = deprecated_in_favor_of
replace = replace_method_if_overridden
def __init__(self):
pass
#This represents a simple alias-over change
@deprecation('do_foo')
def doFoo(self):
#I suppose I could technically build this alias functionality into the
#decorator.
self.do_foo()
def do_foo(self):
print('foo')
#In cases where users are expected to override methods called during run
#We would like a simple pattern to allow changes to either method to impact
#the way the interface works. Simply aliasing would not allow this.
@override_flag
def whileWaiting(self):
"""
The old API users expect to be able to change behavior using this method
"""
print('whileWaiting')
@override_flag
def while_waiting(self):
"""
The new API users expect to be able to change behavior using this method
"""
print('while_waiting')
@replace('while_waiting', 'whileWaiting')
def _while_waiting(self):
"""
This is the meta method for waiting action, the behavior of which may
be defined in either `while_waiting` or `whileWaiting`.
"""
print('unreplaced')
#The iterating function which calls whileWaiting/while_waiting
#This remains static; decorator handles the dynamic check
def waiting_iterator(self):
for _ in range(5):
self._while_waiting()
#Supposing that the old API had a class named OldSpam, we can alias it
OldSpam = Spam
#Let's test the use of both Old and New API sub-classing
class MyOldSpam(OldSpam):
def __init__(self):
super(MyOldSpam, self).__init__()
def whileWaiting(self): # No override decorator
print('I am old school')
class MyNewSpam(Spam):
def __init__(self):
super(MyNewSpam, self).__init__()
def while_waiting(self): # Again, no override decorator
print('Riding the new wave')
if __name__ == '__main__':
old = MyOldSpam()
new = MyNewSpam()
old.waiting_iterator()
new.waiting_iterator()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment