Wednesday, November 28, 2012

How to ship eggs with pyo files only in Python

There is sometimes a need to ship python egg distribution with pyo files only. There is confusion using bdist_egg command since it doesn't have any options to do that; instead, you can instruct install_lib command. Here is what you need in setup.cfg file:
[install_lib]
compile = 0
optimize = 2

[bdist_egg]
exclude-source-files = 1
Issue the following command to build egg.
python setup.py -q bdist_egg
Note, the egg file has only python byte code optimized module files (look at dist directory). You can read about compiled modules here.

Tuesday, November 20, 2012

Python Web Frameworks Excessive Complexity

Cyclomatic (or conditional) complexity is a metric used to indicate the complexity of a source code. In this post we will take a look at web frameworks source code and estimate excessive complexity, something that is beyond recommended level of 10 (threshold that points to the fact the source code is too complex and refactoring is suggested). Here is a list of web frameworks examined:
  1. bottle
  2. cherrypy
  3. circuits
  4. django
  5. falcon
  6. flask
  7. pyramid
  8. pysi
  9. tornado
  10. turbogears
  11. web.py
  12. web2py
  13. webapp2
  14. wheezy.web
The source code is hosted on bitbucket, let clone it into some directory and setup virtual environment (this will download source code per framework listed above).
hg clone https://bitbucket.org/akorn/helloworld
cd helloworld/04-pep8 && make env up
The make file has a target for mccabe metric, so issue make mccabe.

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.