Bug 680960 - adding a function redirection decorator. r=rmiller
authorToby Elliott <telliott@mozilla.com>
Tue, 23 Aug 2011 17:05:59 -0700
changeset 821 6c10afb90ca3
parent 820 e8192099d58b
child 822 b1f04316fe7b
push id328
push usertelliott@mozilla.com
push date2011-08-24 00:06 +0000
reviewersrmiller
bugs680960
Bug 680960 - adding a function redirection decorator. r=rmiller
services/tests/test_util.py
services/util.py
--- a/services/tests/test_util.py
+++ b/services/tests/test_util.py
@@ -60,16 +60,22 @@ from services.util import (function_move
 [some]
 stuff = True
 
 [other]
 thing = ok
 """
 
 
+def return2():
+    return 2
+
+def returnNum(num):
+    return num
+
 class FakeResult(object):
     headers = {}
     body = '{}'
 
     def getcode(self):
         return 200
 
     def read(self):
@@ -110,25 +116,52 @@ class TestUtil(unittest.TestCase):
             headers = req.headers.items()
             headers.sort()
             res.body = str(headers)
             return res
 
         raise ValueError(url)
 
     def test_function_move(self):
-        @function_moved('foobar')
-        def dummy():
+        @function_moved('services.tests.test_util.return2')
+        def return1():
             return 1
 
+        @function_moved('services.tests.test_util.return2',
+                        follow_redirect=False)
+        def return3():
+            return 3
+
+        @function_moved('foo.bar.baz', follow_redirect=True)
+        def bad_redirect():
+            pass
+
+        @function_moved('services.tests.test_util.returnNum')
+        def new_function_profile():
+            pass
+
+        @function_moved('services.tests.test_util.returnNum', False)
+        def return4():
+            return returnNum(4)
+
         with warnings.catch_warnings(record=True) as w:
-            result = dummy()
-            self.assertEqual(result, 1)
+            result = return1()
+            self.assertEqual(result, 2)
             self.assertEqual(len(w), 1)
-            self.assertTrue("has been moved to foobar" in str(w[-1].message))
+            self.assertTrue("moved to services.tests.test_util.return2"
+                            in str(w[-1].message))
+
+            result = return3()
+            self.assertEqual(result, 3)
+            self.assertTrue("moved to services.tests.test_util.return2"
+                            in str(w[-1].message))
+
+            self.assertRaises(ImportError, bad_redirect)
+            self.assertRaises(TypeError, new_function_profile)
+            self.assertEqual(return4(), 4)
 
     def test_convert_config(self):
         config = {'one': '1', 'two': 'bla', 'three': 'false'}
         config = convert_config(config)
 
         self.assertTrue(config['one'])
         self.assertEqual(config['two'], 'bla')
         self.assertFalse(config['three'])
--- a/services/util.py
+++ b/services/util.py
@@ -68,27 +68,35 @@ from services.config import Config, conv
 from services import logger
 from services.exceptions import BackendError, BackendTimeoutError  # NOQA
 
 
 random.seed()
 _RE_CODE = re.compile('[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}')
 
 
-def function_moved(new_loc):
+def function_moved(moved_to, follow_redirect=True):
     """This is a decorator that will emit a warning that a function has been
-    moved elsewhere"""
+    moved elsewhere
+
+    Arguments:
+        moved_to: the string representing the new function name
+        follow_redirect: if true, attempts to resolve the function specified in
+        moved_to and calls that instead of the original function
+    """
     def arg_wrapper(func):
         def moved_function(*args, **kwargs):
-            warnings.warn("%s has been moved to %s" % (func.__name__, new_loc),
+            from services.pluginreg import _resolve_name
+            warnings.warn("%s has moved to %s" % (func.__name__, moved_to),
                           category=DeprecationWarning, stacklevel=2)
-            moved_function.__name__ = func.__name__
-            moved_function.__doc__ = func.__doc__
-            moved_function.__dict__.update(func.__dict__)
-            return func(*args, **kwargs)
+            if follow_redirect:
+                new_func = _resolve_name(moved_to)
+                return new_func(*args, **kwargs)
+            else:
+                return func(*args, **kwargs)
         return moved_function
     return arg_wrapper
 
 
 def randchar(chars=string.digits + string.letters):
     """Generates a random char using urandom.
 
     If the system does not support it, the function fallbacks on random.choice
@@ -479,27 +487,24 @@ def email_to_idn(addr):
     # decode the string if passed as MIME (some MIME encodes @)
     addr = urllib2.unquote(addr).decode('utf-8')
     if '@' not in addr:
         return addr
     prefix, suffix = addr.split('@', 1)
     return "%s@%s" % (prefix.encode('idna'), suffix.encode('idna'))
 
 
-@function_moved('user.extract_username')
+@function_moved('services.user.extract_username')
 def extract_username(username):
     """Extracts the user name.
 
     Takes the username and if it is an email address, munges it down
     to the corresponding 32-character username
     """
-    #migration interstitials
-    from services.user import extract_username as extract_username_migrated
-    return extract_username_migrated(username)
-
+    pass
 
 class CatchErrorMiddleware(object):
     """Middleware that catches error, log them and return a 500"""
     def __init__(self, app, logger_name='root', hook=None,
                  type='application/json'):
         self.app = app
         self.logger = logging.getLogger(logger_name)
         self.hook = hook