Theju's tryst with life

Learning Python 1

Yesterday I learnt an interesting aspect of python called closures.

def a(aa1, aa2=10):
     def b(bb1, bb2=aa2):
             print aa1, aa2, bb1, bb2
     aa1+=100
     aa2+=100
     return b

>>> a(20)(5)

What is the output?

The answer by intuition would have been 20 10 5 10. The answer however is 120 110 5 10.

I was amazed by this result and wanted to figure out how this works. I ran pdb on this function using

$ python -m pdb test_fn.py

The result of it was:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
> /home/theju/test_fn.py(1)<module>()
-> def a(aa1, aa2=10):
(Pdb) s
> /home/theju/test_fn.py(8)<module>()
-> a(20)(5)
(Pdb)
--Call--
> /home/theju/test_fn.py(1)a()
-> def a(aa1, aa2=10):
(Pdb)
> /home/theju/test_fn.py(2)a()
-> def b(bb1, bb2=aa2):
(Pdb) dir()
['aa1','aa2']
(Pdb) s
> /home/theju/test_fn.py(4)a()
-> aa1+=100
(Pdb) dir()
['aa1', 'aa2', 'b']
(Pdb) dir(b)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__',
'__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure',
'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
(Pdb) p b.func_defaults
(10,)
(Pdb) s
> /home/theju/test_fn.py(5)a()
-> aa2+=100
(Pdb)
> /home/theju/test_fn.py(6)a()
-> return b
(Pdb)
--Return--
> /home/theju/test_fn.py(6)a()-><functio...b7c6adbc>
-> return b
(Pdb)
--Call--
> /home/theju/test_fn.py(2)b()
-> def b(bb1, bb2=aa2):
(Pdb)
> /home/theju/test_fn.py(3)b()
-> print aa1, aa2, bb1, bb2
(Pdb)
120 110 5 10
--Return--
> /home/theju/test_fn.py(3)b()->None
-> print aa1, aa2, bb1, bb2
(Pdb)
--Return--
> /home/theju/test_fn.py(8)<module>()->None
-> a(20)(5)
(Pdb)
--Return--
> <string>(1)<module>()->None
(Pdb)

The default parameter values are evaluated when the function definition is executed. See this for more details.

Thanks to Gopi for helping me learn this concept and more.

Launch of Safebrowsing-python

Today I launched my second project online after django-check-constraints, safebrowsing-python. The project is based on the Google Safebrowsing API. Check out the announcement on the google-safebrowsing-api mailing list and also the usage wiki page at the project home.

Wish you a happy and safe browsing on the Web.

An Idle Mind is a Devil's Workshop

On thursday, my previous semester results were out. I logged on to the site and checked out my result. My result was anything but surprising. Then I thought to myself, "Why not give people the freedom to compare their marks with their peers'".

This idea excited me and I wrote a python script that used BeautifulSoup to soup everyone's marks and then create an HTML table that would use YUI for the ajaxy effect.

Thanks to Python's ultra simplicity and sheer power, my script was done in one and half hour after the results were out.

Check out the results at http://puthraya.webfactional.com/results.html and http://puthraya.webfactional.com/consolidated_results.html

I blog in ReSt Part 3

This is the final part in the "I blog in ReSt" series. This part discusses about how to get the templates going for the blog.

  • Create a folder called templates in the project directory and add the absolute path to the directory in the settings.py TEMPLATE_DIRS
  • Create a file called blog.html in templates
    {% load custom_filters %}
    {% load comments %}
    {% load markup %}
    {% load tagging_tags %}

    {% block title %}Title of the Blog{% endblock %}

    {% block content %}
    {% for blog in blog_object_list %}
      {{ blog.blog_title }}
      {{ blog.blog_file|open_file|restructuredtext }}
      Published on {{ blog.blog_date_pub|date:"F j, Y" }}

    {% get_free_comment_list for blog_app.blog_model blog.id as comment_list %}
    {% get_free_comment_count for blog_app.blog_model blog.id as comment_count %}

    {% if comment_count %}

      {% for comment in comment_list %}
         <div class="comment_{% cycle odd,even %}" id="c{{ comment.id }}">
         <span class="comnum"><a id="c{{ comment.id }}"
         href="#c{{ comment.id }}">#{{ forloop.counter }}</a></span>
         <p><strong>{{ comment.person_name|escape }}</strong> commented,
         on {{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }}:</p>
         {{ comment.comment|restructuredtext }}
         </div>
      {% endfor %}

    {% endif %}

    <p>Post a comment</p>

    {% free_comment_form for blog_app.blog_model blog.id %}

   {% endfor %}

   Viewing page <strong>{{ page }} / {{ pages }}</strong>. Total number of posts are {{ hits }}.

   {% if has_previous %}

       {% if year %}

         {% if month %}
           <p><a href="/blog/{{ year }}/{{ month }}/page/{{ previous }}/">Newer Posts</a></p>
         {% else %}
           <p><a href="/blog/{{ year }}/page/{{ previous }}/">Newer Posts</a></p>
         {% endif %}

       {% else %}

         {% if tag %}
           <p><a href="/blog/tag/{{ tag }}/page/{{ previous }}/">Newer Posts</a></p>
         {% else %}
           <p><a href="/blog/page/{{ previous }}/">Newer Posts</a></p>
         {% endif %}

       {% endif %}

     {% endif %}

   {% if has_next %}

      {% if year %}

        {% if month %}
          <p><a href="/blog/{{ year }}/{{ month }}/page/{{ next }}/">Older Posts</a></p>
        {% else %}
          <p><a href="/blog/{{ year }}/page/{{ next }}/">Older Posts</a></p>
        {% endif %}

      {% else %}

        {% if tag %}
          <p><a href="/blog/tag/{{ tag }}/page/{{ next }}/">Older Posts</a></p>
        {% else %}
          <p><a href="/blog/page/{{ next }}/">Older Posts</a></p>
        {% endif %}

      {% endif %}

    {% endif %}
{% endblock %}

You might be wondering what {% load custom_filters %} is. As the name suggests it is a custom filter to read the contents of the file and return them to the restructuredtext filter.

To write your custom filter

  • Create a new application called mytags and add it to the INSTALLED_APPS in settings.py
  • In the mytags directory create a folder called templatetags which must have an __init__.py file and the python file containing your filter code (here it is custom_filters.py).
from django import template

register = template.Library()

def open_file(value):
"Opens the contents of the file given in value."
    try:
        f = open(value,"r")
        contents = f.read()
    except IOError:
        return None
    f.close()
    return contents

# Register the Filter
register.filter(open_file)

The restructuredtext is a filter from django.contrib.markup. The django.contrib.markup currently supports three markup languages Textile, Markdown and ReSt.

Comments are essential for a blog

Django has an in-built comment system that can be activated almost instantly. For activating comments add the django.contrib.comments to your INSTALLED_APPS in settings.py. There are two types of Comments Systems:

  • Comments: For sites that require only registered users to post comments. This system also has the facility to rate comments based on a concept called Karma Score.
  • FreeComments: For sites that require any user to post comments. This is the type of comments one usually places on a blog. But beware spam will soon catch up. For the people who want to take extra precaution, I would recommend James Bennett's (aka Ubernostrum) Django Comment Utils. Django Comment Utils provides lots of features like email on comment, Akismet based spam filtering, easy moderation etc.

The best part about Django's Comment System is that it is extremely easy to use but its biggest drawback is that it hasn't been documented yet. This is no lame excuse for not using Django Comments. In fact lots of users have blogged on how to use the Comment System. Feel free to search and you'll get hold of them. The one link that explains the comment system in detail would be this.

The rest of the code is regarding pagination. Refer here for more details regarding it.

Now you have a blog written in Django that has all the basic features that Blogger has. Also the blog is highly extensible. Extend the blog and let me know of some cool features that you built into your blog.

I blog in ReSt Part 2

Once done with setting up the project, we need to code our views and templates to complete the blog. This part deals with coding the views for the blog. The different views that we require are

  • For displaying the landing page
  • For displaying the blog filtered by year
  • For displaying the blog filtered by month
  • For displaying the blog filtered by day
  • Tags to categorize the blog. For this I make use of Django-Tagging

So let's edit the urls.py file in the project directory to reflect on the urlpatterns.

('^blog/$', get_blog_first_page),
('^blog/(?P<year>\d{4})/$', get_posts_by_year),
('^blog/(?P<year>\d{4})/page/(?P<page>\d)/$', get_blog_page_by_year),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/$', get_posts_by_month),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/page/(?P<page>\d)/$', get_blog_page_by_month),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', get_posts_by_day),
('^blog/tag/(?P<tag>[^/]+(?u))/$',tagged_object_list,dict(model=Blog_Model, paginate_by=1, \
allow_empty=True,template_object_name='blog_object', template_name='blog.html',\
extra_context={"tag":1,})),
('^blog/tag/(?P<tag>[^/]+(?u))/page/(?P<page>[0-9]+)/$',tagged_object_list,dict(model=Blog_Meta, \
 paginate_by=1, allow_empty=True, template_object_name='blog_object', template_name='blog.html',)),

By doing so we will be accepting all urls like

/blog/                       # the first page with latest items
/blog/2007/                  # the first page of all blog posts from 2007
/blog/2007/page/4/           # the fourth page of blog posts from 2007
/blog/2007/10/               # blog posts from October 2007
/blog/2007/09/page/2/        # the second page from November 2007
/blog/2007/11/15/            # the post on 15 November 2007
/blog/tag/django/            # Posts with tag django
/blog/tag/django/page/7/     # Seventh page with tag django

Django provides a date-based generic view that saves us from the writing views for date filtering etc. But date-based generic views do not provide pagination by default. But django is powerful and highly extensible. It provides an Object Paginator which can be used for paginations. So I will use a list-based generic view to mimic the date-based generic views with pagination. Open the views.py file in blog_app and paste the following code.

 from django.core.paginator import ObjectPaginator
 from blog_app.models import Blog_Model
 from django.http import HttpResponse, Http404
 from django.template import loader, Context
 from django.views.generic.date_based import archive_day
 from django.views.generic.list_detail import object_list
 import tagging

 # Number of Entries per page (paginate_by)
 num_entries_per_page = 7

 def get_blog_page(request,page):
     queryset = Blog_Model.objects.order_by("-blog_date_pub")
     paginator = ObjectPaginator(queryset, num_entries_per_page)
     cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
     paginator.pages
     if int(page) in paginator.page_range:
         return object_list(request,queryset,num_entries_per_page,page,template_name='blog.html',\
         template_object_name='blog_object',extra_context={'cloud_list': cloud_list,})
     else:
         raise Http404

 def get_blog_first_page(request):
     return get_blog_page(request,1)

 def get_blog_page_by_year(request,year,page):
     queryset = Blog_Model.objects.filter(**{"blog_date_pub__year":year}).order_by("-blog_date_pub")
     try:
         assert queryset
     except AssertionError:
         raise Http404
     paginator = ObjectPaginator(queryset, num_entries_per_page)
     paginator.pages
     cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
     if int(page) in paginator.page_range:
         return object_list(request,queryset,num_entries_per_page,page,template_name='blog.html',\
         template_object_name='blog_object',extra_context={"year":year,'cloud_list': cloud_list,})
     else:
         raise Http404

def get_posts_by_year(request, year):
    return get_blog_page_by_year(request,year,1)

def get_posts_by_day(request,year,month,day):
    month_dict = {1:"Jan",2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",\
                 9:"Sep",10:"Oct",11:"Nov",12:"Dec"}
    try:
       month_name = month_dict[int(month)]
       except ValueError:
          raise Http404
    cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
    return archive_day(request,year,month_name,day,Blog_Model.objects.all(),'blog_date_pub',\
    template_name='blog.html',template_object_name='blog_object',\
    extra_context={'cloud_list': cloud_list,})

I have not given the complete view. You can check it out here. The next part in this series talks about templates.

The number of posts are 10. The number of pages are 2. Next