def patch(): """ The CursorWrapper is a pretty small wrapper around the cursor. If you are NOT in debug mode, this is the wrapper that's used. Sadly if it's in debug mode, we get a different wrapper for version earlier than 1.6. """ def execute(orig_execute, self, *args, **kwargs): with statsd.timer(key(self.db, 'execute')): return orig_execute(self, *args, **kwargs) def executemany(orig_executemany, self, *args, **kwargs): with statsd.timer(key(self.db, 'executemany')): return orig_executemany(self, *args, **kwargs) def callproc(orig_callproc, self, *args, **kwargs): with statsd.timer(key(self.db, 'callproc')): return orig_callproc(self, *args, **kwargs) if django.VERSION > (1, 6): # In 1.6+ util.CursorDebugWrapper just makes calls to CursorWrapper # As such, we only need to instrument CursorWrapper. # Instrumenting both will result in duplicated metrics patch_method(util.CursorWrapper, 'execute')(execute) patch_method(util.CursorWrapper, 'executemany')(executemany) patch_method(util.CursorWrapper, 'callproc')(callproc) else: util.CursorWrapper.__getattr__ = pre_django_1_6_cursorwrapper_getattr patch_method(util.CursorDebugWrapper, 'execute')(execute) patch_method(util.CursorDebugWrapper, 'executemany')(executemany)
def test_late_patching(self): """ Objects created before patching should get patched as well. """ def patch_fn(original_fn, self, *args, **kwargs): return original_fn(self, *args, **kwargs) + 10 obj = self.cls() self.assertEqual(obj.sumargs(1, 2, 3, 4), 10) utils.patch_method(self.cls, 'sumargs')(patch_fn) self.assertEqual(obj.sumargs(1, 2, 3, 4), 20)
def test_args_kwargs_are_honored(self): """ Args and kwargs must be honored between calls from the patched to the original version. """ def patch_fn(original_fn, self, *args, **kwargs): return original_fn(self, *args, **kwargs) utils.patch_method(self.cls, 'sumargs')(patch_fn) obj = self.cls() self.assertEqual(obj.sumargs(1, 2), 10) self.assertEqual(obj.sumargs(1, 1, d=1), 6) self.assertEqual(obj.sumargs(1, 1, 1, 1), 4)
def test_patched_fn_can_receive_arbitrary_arguments(self): """ Args and kwargs can be received arbitrarily with no contraints on the patched fn, even if the original_fn had a fixed set of allowed args and kwargs. """ def patch_fn(original_fn, self, *args, **kwargs): return args, kwargs utils.patch_method(self.cls, 'badfn')(patch_fn) obj = self.cls() self.assertEqual(obj.badfn(1, d=2), ((1, ), {'d': 2})) self.assertEqual(obj.badfn(1, d=2), ((1, ), {'d': 2})) self.assertEqual(obj.badfn(1, 2, c=1, d=2), ((1, 2), {'c': 1, 'd': 2}))
def test_doesnt_call_original_implicitly(self): """ Original fn must be called explicitly from patched to be executed. """ def patch_fn(original_fn, self, *args, **kwargs): return 10 with self.assertRaises(ValueError): obj = self.cls() obj.badfn(1, 2) utils.patch_method(self.cls, 'badfn')(patch_fn) self.assertEqual(obj.badfn(1, 2), 10)
def test_patched_fn_can_receive_arbitrary_arguments(self): """ Args and kwargs can be received arbitrarily with no contraints on the patched fn, even if the original_fn had a fixed set of allowed args and kwargs. """ def patch_fn(original_fn, self, *args, **kwargs): return args, kwargs utils.patch_method(self.cls, 'badfn')(patch_fn) obj = self.cls() self.assertEqual(obj.badfn(1, d=2), ((1,), {'d': 2})) self.assertEqual(obj.badfn(1, d=2), ((1,), {'d': 2})) self.assertEqual(obj.badfn(1, 2, c=1, d=2), ((1, 2), {'c': 1, 'd': 2}))
def patch(): """ The CursorWrapper is a pretty small wrapper around the cursor. If you are NOT in debug mode, this is the wrapper that's used. Sadly if it's in debug mode, we get a different wrapper for version earlier than 1.6. """ if django.VERSION > (1, 6): # In 1.6+ util.CursorDebugWrapper just makes calls to CursorWrapper # As such, we only need to instrument CursorWrapper. # Instrumenting both will result in duplicated metrics patch_method(util.CursorWrapper, "execute")(patched_execute) patch_method(util.CursorWrapper, "executemany")(patched_executemany) patch_method(util.CursorWrapper, "callproc")(patched_callproc) else: util.CursorWrapper.__getattr__ = pre_django_1_6_cursorwrapper_getattr patch_method(util.CursorDebugWrapper, "execute")(patched_execute) patch_method(util.CursorDebugWrapper, "executemany")(patched_executemany)
def patch(): """ The CursorWrapper is a pretty small wrapper around the cursor. If you are NOT in debug mode, this is the wrapper that's used. Sadly if it's in debug mode, we get a different wrapper for version earlier than 1.6. """ if django.VERSION > (1, 6): # In 1.6+ util.CursorDebugWrapper just makes calls to CursorWrapper # As such, we only need to instrument CursorWrapper. # Instrumenting both will result in duplicated metrics patch_method(util.CursorWrapper, 'execute')(patched_execute) patch_method(util.CursorWrapper, 'executemany')(patched_executemany) patch_method(util.CursorWrapper, 'callproc')(patched_callproc) else: util.CursorWrapper.__getattr__ = pre_django_1_6_cursorwrapper_getattr patch_method(util.CursorDebugWrapper, 'execute')(patched_execute) patch_method( util.CursorDebugWrapper, 'executemany')(patched_executemany)
def patch(): """ The CursorWrapper is a pretty small wrapper around the cursor. If you are NOT in debug mode, this is the wrapper that's used. """ patch_method(util.CursorWrapper, 'execute')(patched_execute) patch_method(util.CursorWrapper, 'executemany')(patched_executemany) patch_method(util.CursorWrapper, 'callproc')(patched_callproc)