Repository: spulec/freezegun

Issue:
as_arg adds frozen_time argument to front of list of arguments
Currently the following code does not work with test classes (Django, etc). 

https://github.com/spulec/freezegun/blob/master/freezegun/api.py#L669

That line does the following
```python
result = func(time_factory, *args, **kwargs)
```

That is incompatible with `self` being the first param to class methods and it behaves differently than most other decorators, which add their argument to the end, not the beginning.  Here is an example that doesn't work

```python
from django.test import TestCase
from freezegun import freeze_time


class MyTest(TestCase):

  @freeze_time('2016-11-01', as_arg=True)
  def test_something_with_time(self, time_factory):
    # freeze_time overrides self instead of time_factory
    # self is now the time_factory variable
    print(self) # <TimeFactory>
    print(time_factory) # <MyTestCase>
```



It should do this instead.

```python
result = func(*args, time_factory, **kwargs)
```

This is a backwards incompatible change, but will allow the decorator to be used with class based tests. Would you be open to accepting a PR that makes this breaking change?

Comments:

Patch:
diff --git a/README.rst b/README.rst
index a95059e..6997939 100644
--- a/README.rst
+++ b/README.rst
@@ -43,6 +43,23 @@ Decorator
         def test_the_class(self):
             assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
 
+    # Or method decorator, might also pass frozen time object as kwarg
+
+    class TestUnitTestMethodDecorator(unittest.TestCase):
+        @freeze_time('2013-04-09')
+        def test_method_decorator_works_on_unittest(self):
+            self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today())
+
+        @freeze_time('2013-04-09', as_kwarg='frozen_time')
+        def test_method_decorator_works_on_unittest(self, frozen_time):
+            self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today())
+            self.assertEqual(datetime.date(2013, 4, 9), frozen_time.time_to_freeze.today())
+
+        @freeze_time('2013-04-09', as_kwarg='hello')
+        def test_method_decorator_works_on_unittest(self, **kwargs):
+            self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today())
+            self.assertEqual(datetime.date(2013, 4, 9), kwargs.get('hello').time_to_freeze.today())
+
 Context manager
 ~~~~~~~~~~~~~~~
 
diff --git a/freezegun/api.py b/freezegun/api.py
index 06450e1..738016d 100644
--- a/freezegun/api.py
+++ b/freezegun/api.py
@@ -513,8 +513,7 @@ class StepTickTimeFactory(object):
 
 class _freeze_time(object):
 
-
-    def __init__(self, time_to_freeze_str, tz_offset, ignore, tick, as_arg, auto_tick_seconds):
+    def __init__(self, time_to_freeze_str, tz_offset, ignore, tick, as_arg, as_kwarg, auto_tick_seconds):
         self.time_to_freeze = _parse_time_to_freeze(time_to_freeze_str)
         self.tz_offset = _parse_tz_offset(tz_offset)
         self.ignore = tuple(ignore)
@@ -523,6 +522,7 @@ class _freeze_time(object):
         self.undo_changes = []
         self.modules_at_start = set()
         self.as_arg = as_arg
+        self.as_kwarg = as_kwarg
 
     def __call__(self, func):
         if inspect.isclass(func):
@@ -693,7 +693,7 @@ class _freeze_time(object):
                         continue
                     elif mod_name.startswith(self.ignore) or mod_name.endswith('.six.moves'):
                         continue
-                    elif (not hasattr(module, "__name__") or module.__name__ in ('datetime', 'time')):
+                    elif not hasattr(module, "__name__") or module.__name__ in ('datetime', 'time'):
                         continue
                     for module_attribute in dir(module):
 
@@ -729,8 +729,13 @@ class _freeze_time(object):
     def decorate_callable(self, func):
         def wrapper(*args, **kwargs):
             with self as time_factory:
-                if self.as_arg:
+                if self.as_arg and self.as_kwarg:
+                    assert False, "You can't specify both as_arg and as_kwarg at the same time. Pick one."
+                elif self.as_arg:
                     result = func(time_factory, *args, **kwargs)
+                elif self.as_kwarg:
+                    kwargs[self.as_kwarg] = time_factory
+                    result = func(*args, **kwargs)
                 else:
                     result = func(*args, **kwargs)
             return result
@@ -743,7 +748,8 @@ class _freeze_time(object):
         return wrapper
 
 
-def freeze_time(time_to_freeze=None, tz_offset=0, ignore=None, tick=False, as_arg=False, auto_tick_seconds=0):
+def freeze_time(time_to_freeze=None, tz_offset=0, ignore=None, tick=False, as_arg=False, as_kwarg='',
+                auto_tick_seconds=0):
     acceptable_times = (type(None), _string_type, datetime.date, datetime.timedelta,
              types.FunctionType, types.GeneratorType)
 
@@ -782,7 +788,7 @@ def freeze_time(time_to_freeze=None, tz_offset=0, ignore=None, tick=False, as_ar
         'gi',
     ])
 
-    return _freeze_time(time_to_freeze, tz_offset, ignore, tick, as_arg, auto_tick_seconds)
+    return _freeze_time(time_to_freeze, tz_offset, ignore, tick, as_arg, as_kwarg, auto_tick_seconds)
 
 
 # Setup adapters for sqlite


Test patch:
diff --git a/tests/test_datetimes.py b/tests/test_datetimes.py
index b9b8685..994fb6c 100644
--- a/tests/test_datetimes.py
+++ b/tests/test_datetimes.py
@@ -485,6 +485,22 @@ def test_isinstance_without_active():
     assert isinstance(today, datetime.date)
 
 
+class TestUnitTestMethodDecorator(unittest.TestCase):
+    @freeze_time('2013-04-09')
+    def test_method_decorator_works_on_unittest(self):
+        self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today())
+
+    @freeze_time('2013-04-09', as_kwarg='frozen_time')
+    def test_method_decorator_works_on_unittest(self, frozen_time):
+        self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today())
+        self.assertEqual(datetime.date(2013, 4, 9), frozen_time.time_to_freeze.today())
+
+    @freeze_time('2013-04-09', as_kwarg='hello')
+    def test_method_decorator_works_on_unittest(self, **kwargs):
+        self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today())
+        self.assertEqual(datetime.date(2013, 4, 9), kwargs.get('hello').time_to_freeze.today())
+
+
 @freeze_time('2013-04-09')
 class TestUnitTestClassDecorator(unittest.TestCase):