Commits

Konstantine Rybnikov committed ff9f2cb

add double-call-hack post

Comments (0)

Files changed (4)

source/en_posts/2012-07-12-regular-python-engineer.rst

  Regular Python Engineer Skills
 ================================
 
+.. warning::
+
+   This post is still in-progress. Please be patient and don't read it
+   if you are lazy to re-read it when it's ready. When I'm finished, I
+   will publish it on hacker news, reddits and so on, and if you are
+   interested -- you will probably won't miss it.
+
+.. note::
+
+   On my current I was asked to write a document that would cover some
+   basic knowledge areas for people who want to become regular python
+   programmer, since there was often a situation when people were
+   hided with comments "but still has lack of knowledge in XXX". So,
+   based on that comments, I'll try to write that doc. I believe it's
+   best to show it to community, otherwise it would be just a wase of
+   time.
+
 This document aims to cover different gaps that regular engineer might
 have, and ways to improve them in most common aspects. This document
 is incomplete and should be updated with missing common needs for

source/en_posts/2012-07-13-django-bad-parts.rst

+=====================
+ Bad parts of django
+=====================
+
+.. warning::
+
+   This post is still in-progress. Please be patient and don't read it
+   if you are lazy to re-read it when it's ready. When I'm finished, I
+   will publish it on hacker news, reddits and so on, and if you are
+   interested -- you will probably won't miss it.
+
+* There's no ``session`` in ORM, so if you want flexibility in some
+  situations not to call ``save()``, and in some -- do call, then you
+  need to implement ``save=True`` parameter in your business-logic.
+* There's no identity-map in ORM, so you need to make sure by yourself
+  that if you modified some object (and did not save it), all later
+  code will use the same object. Also you get additional queries
+  everywhere, and if you have "by id" external links, you can't do
+  quicks optimization by just selecting "most hot" objects once, and
+  be sure they'll be loaded from memory (or do it by hands).

source/en_posts/2012-07-13-double-call-hack.rst

+:Date: 2011-05-13
+
+=================================================
+ Pyton double-call hack in for mocking date/time
+=================================================
+
+In unit- and functional-testing you want to somehow deal with dates,
+and when you run test you want to set your own date (result of
+``utcnow()``) call.
+
+With `Mock <http://mock.readthedocs.org/>`_ module you have great
+``patch`` method to patch things (or ``p`` method from my `mockstar
+<http://mockstar.readthedocs.org/>`_ enhances on ``Mock``
+library). How it works is basically it goes to module you tell it to
+and replaces ``__dict__["member"]`` to your var. Consider an example:
+
+.. code-block:: python
+
+    # proj/foo/bar.py
+
+    import datetime
+
+    def my_great_function():
+        current_time = datetime.datetime.utcnow()
+        if current_time.year > 2012:
+            return 'new'
+        else:
+            return 'old'
+
+To test this function with your own time, what you can do with
+``patch`` is:
+
+.. code-block:: python
+
+    # tests/proj/foo/bar_test.py
+
+    from mock import patch
+    from proj.foo.bar import my_great_function
+    from proj.core.test import BaseTestCase
+
+    class TestMyGreatFunction(BaseTestCase):
+        @patch('proj.foo.bar.datetime')
+        def test_should_get_new_for_year_2013(self, datetime_mock):
+            datetime_mock.datetime.utcnow.return_value = (
+                datetime.datetime(2013, 01, 01))
+
+            result = my_great_function()
+
+            self.assertEquals(result, 'new')
+
+        @patch('proj.foo.bar.datetime')
+        def test_should_get_old_for_year_2012(self, datetime_mock):
+            datetime_mock.datetime.utcnow.return_value = (
+                datetime.datetime(2012, 01, 01))
+
+            result = my_great_function()
+
+            self.assertEquals(result, 'old')
+
+That's cool, but there are some problems with this code:
+
+1. Most of the time you don't want to replace the whole ``datetime``
+   module to mock since you want to have ``datetime.datetime``
+   instances to keep being real.
+2. On functional tests, you want to patch all the ``utcnow()``
+   occurrences (in your code at least), and you don't want to patch
+   all the modules that use ``utcnow``.
+3. Consider this example:
+
+   .. code-block:: python
+
+       # proj/foo/models.py
+
+       class MyModel(models.Model):
+           my_field = models.DateTimeField(default=utcnow)
+
+   If you try to test it by mocking ``proj.foo.models.utcnow`` -- you
+   will fail. The reason for this is that, when you do usual function
+   call (``utcnow()``) in your code -- python searches for ``utcnow``
+   symbol in ``__dict__`` of your module, and then returns instance
+   for that (which can be already patched), while with this example,
+   you "lock-out" link to your ``utcnow`` function object at
+   import-time, and python won't look for it in ``__dict__`` anymore.
+
+So, what's the end-solution?
+============================
+
+The solution (hack?) is to have something like this:
+
+.. code-block:: python
+
+    # proj/util/date.py
+
+    import datetime
+
+    def _utcnow():
+        return datetime.datetime.utcnow()
+    
+    def utcnow():
+        return _utcnow()
+
+You should use ``proj.util.date.utcnow`` function everywhere in your
+project where you want to get current time, and patching it would look
+something like this:
+
+.. code-block:: python
+
+    class TestCurrentTime(FunctionalTestCase):
+        @patch("proj.util.date._utcnow")
+        def test_should_get_current_time(self, utcnow_mock):
+            utcnow_mock.return_value = datetime.datetime(2011, 01, 02)
+            result = self.client.get("/api/time/current")
+            self.assertEquals(result.content,
+                              "2011-01-02")
+
+Basically, every time ``proj.util.date.utcnow`` is called, it calls
+``proj.util.date._utnow``, which makes python to look into
+``proj.util.date.__dict__`` for it, and it find your patched version!

source/posts/2011-07-13-django-bad-parts.rst

-=====================
- Bad parts of django
-=====================
-
-* There's no ``session`` in ORM, so if you want flexibility in some
-  situations not to call ``save()``, and in some -- do call, then you
-  need to implement ``save=True`` parameter in your business-logic.
-* There's no identity-map in ORM, so you need to make sure by yourself
-  that if you modified some object (and did not save it), all later
-  code will use the same object. Also you get additional queries
-  everywhere, and if you have "by id" external links, you can't do
-  quicks optimization by just selecting "most hot" objects once, and
-  be sure they'll be loaded from memory (or do it by hands).