Thursday, November 15, 2012

Lazy Attribute in Python

A lazy attribute is an attribute that is calculated on demand and only once. Here we will see how you can use lazy attribute in your Python class. Setup environment before you proceed:
$ virtualenv env
$ env/bin/pip install wheezy.core
Let assume we need an attribute that is display name of some person Place the following code snippet into some file and run it:
from wheezy.core.descriptors import attribute

class Person(object):
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
        self.calls_count = 0
        
    @attribute
    def display_name(self):
        self.calls_count += 1
        return '%s %s' % (self.first_name, self.last_name)

if __name__ == '__main__':
    p = Person('John', 'Smith')
    print(p.display_name)
    print(p.display_name)
    assert 1 == p.calls_count
Notice display_name function is decorated with @attribute. The first call promotes function to attribute with the same name. The source is here.

Thursday, November 8, 2012

Duck Typing Assert in Python

People who come from strongly typed languages that offer interfaces often are confused by lack of one in Python. Python, being dynamic typing programming language, follows duck typing principal. Here we will see how programmer can assert duck typing between two Python classes. Setup environment before proceed:
$ virtualenv env
$ env/bin/pip install wheezy.core
Let play a bit with duck test `looks like`. Place the following code snippet into file `test.py`:
from wheezy.core.introspection import looks

class IFoo(object):
    def foo(self):
        pass

class Foo(object):
    def bar(self):
        pass

assert looks(Foo).like(IFoo)

Friday, November 2, 2012

How to generate account number?

Requirements for `account number` generator:
  1. Issue pseudo random consistent number (must be unique for dozen millions of records)
  2. Easy check validity (without a need to make a database call)
We will use Feistel cipher to generate pseudo random number (positive only) from a sequential numbers (e.g. returned by nextval() for a posgresql sequence). This algorithm is taken as basis for the `make_feistel_number` function available in wheezy.core package. Setup environment before proceed:
$ virtualenv env
$ env/bin/easy_install wheezy.core
$ env/bin/python
Let play a bit how numbers are generated (notice, the function is reversal and consistent, all numbers are unique, no collision):
>>> from wheezy.core.feistel import make_feistel_number
>>> from wheezy.core.feistel import sample_f
>>> feistel_number = make_feistel_number(sample_f)
>>> feistel_number(1)
573852158
>>> feistel_number(2)
1788827948
>>> feistel_number(1788827948)
2
We will use Luhn algorithm to generate a single digit checksum. This algorithm is taken as basis for the module available in wheezy.core package.
>>> from wheezy.core.luhn import luhn_checksum
>>> luhn_checksum(123456789)
7
There are two more useful functions to sign a number and also check it validity:
>>> from wheezy.core.luhn import luhn_sign
>>> from wheezy.core.luhn import is_luhn_valid
>>> luhn_sign(123456789)
1234567897
>>> is_luhn_valid(1234567897)
True
>>> is_luhn_valid(34518893)
False
Now let just make account number looking nice (pad with zeros, prefix with something meaningful, etc):
>>> account_number = lambda n: 'Z%011d' % luhn_sign( \
...     feistel_number(n))
>>> account_number(1)
'Z05738521581'
>>> account_number(2)
'Z17888279480'
>>> account_number(3)
'Z07395350007'
Per discussion in python mail list, there was discovered alternative, human readable representation of the same number:
>>> from base64 import b32encode
>>> human_format = lambda n: 'Z%s-%s' % (b32encode( \
...     chr((n >> 24) & 255) + \
...     chr((n >> 16) & 255))[:4], \
...     b32encode(chr((n >> 8) & 255) + \
...     chr(n & 255))[:4])
>>> human_format(5738521581)
'ZKYFA-4PWQ'
>>> human_format(17888279480)
'ZFI4Q-PO4A'
>>> human_format(7395350007)
'ZXDGA-CX3Q'
Side by side:
Z05738521581 = ZKYFA-4PWQ
Z17888279480 = ZFI4Q-PO4A
Z07395350007 = ZXDGA-CX3Q
Yes, it optimized for speed, you must provide your own `sample_f` implementation for security reasons. Source code available here.

Friday, October 26, 2012

wheezy web: introduction

The key of success for any medium to high complexity system is in separation of domain concerns. Given that choice in architectural design for web framework, the development activities are split by distinct, non-dependent parts. The wheezy.web is a lightweight WSGI web framework and serves a glue purpose between various other packages developed under wheezy.* umbrella in loosely coupled way, it combines things essential for web application developer (presentation slides are here):

Thursday, October 25, 2012

Python Web Caching Benchmark

Content caching is the most effective type of cache. This way your web handler is not executed to determine a valid response to user, instead one returned from cache. Since the operation is that simple, it should be the maximum possible speed your `real world` application capable to provide. There are several use cases when content caching is applicable:
  • Site: the site content is changed rarely. The best case scenario is nginx+wsgi caching facilities, see more here.
  • Handler: the site content caching policy vary, there are only few handlers were content caching is applicable.
  • Managed (semi-real time): the site is dynamic, it is not permissible to cache a given output unless there is a way to invalidate content since some data changed, e.g. item price, new message arrived, etc. Read more here or give it a try.
How python web frameworks respond to this challenge? Only few (per documentation on official web site):
  1. django 1.4.2
  2. flask 0.9
  3. wheezy.web 0.1.307
Web content caching benchmark is provided for two types of caching: memory and distributed. The cached content size is 8890 bytes (with applied gzip transform the content size down to 1086 bytes).