Sunday, April 20, 2008

Python logger config thread safety

Yes, the python logger is thread safe, but don't configure it programmatically (i.e. not via config file). Adding handlers to a logger with getLogger().addHandler(handler) does just that - and it will add handlers ad infinitum, even if they are already added.

So if you want to set logger options, in say, a Django settings.py file:

import logging.config
logging.config.fileConfig("logging.conf")

Your logging.conf might look like this:

[loggers]
keys=root

[handlers]
keys=errhandler,filehandler

[formatters]
keys=formatter

[logger_root]
level=NOTSET
handlers=errhandler,filehandler
formatters=formatter

[handler_errhandler]
class=StreamHandler
level=NOTSET
formatter=formatter
args=(sys.stdout,)

[handler_filehandler]
class=handlers.RotatingFileHandler
level=NOTSET
formatter=formatter
args=("/var/log/django/my.log", "a", 1024*1024)

[formatter_formatter]
format=[%(asctime)s] - %(levelname)-8s - "%(message)s" (%(filename)s: %(funcName)s - %(lineno)s)
datefmt=%Y-%m-%d %a %H:%M:%S
class=logging.Formatter

Labels: , ,

Monday, November 20, 2006

ISBN Check Methods

Note: From now on, I'm likely going to be posting code on here in addition to personal blog entries. Don't get scared off.

I wrote some Python code to handle ISBN verification and ISBN-13 conversions. ISBN-13 is the mandatory update to all ISBN numbers that takes effect January 1, 2007. In general, an ISBN-13 - which is, at its core, EAN-13 - can be computed from an ISBN-10 number by dropping the end check digit, adding "978" to the beginning (the EAN code for "Bookland" in the US), and recalculating the check digit, then adding it to the end.

So here's the code that does it, not the best I'm sure, but the algorithm is there:

"""
isbn.py
ISBN helper methods to handle validation and conversion

"""

def validateisbn(isbn):
isbn = isbn.replace("-", "")
return isbn.endswith(str(calc_checkdigit(isbn[:-1])))

def calc_checkdigit(isbn):
"""
Input an isbn without a checksum (or punctuation) and returns
correct check digit.

"""
if len(isbn) == 9:
# Do the old ISBN-style check generation
products = [(10 - n) * int(isbn[n]) for n in range(9)]
remainder = (sum(products) % 11)
if remainder == 0:
check = 0
else:
check = 11 - remainder
if check == 10:
check = "X"
return str(check)
elif len(isbn) == 12:
# Do EAN-style check
products = []
for digit in range(12):
if digit % 2 == 0:
# Even digit (zero-based) is weight factor 1
products.append(int(isbn[digit]))
else:
# Odd digit is weight factor 3
products.append(int(isbn[digit]) * 3)
remainder = (sum(products) % 10)
if remainder == 10:
check = 0
else:
check = 10 - remainder
return str(check)
else:
raise Exception("Length of input ISBN is incorrect.")

def convert_to_isbn13(isbn10, code="978"):
"""Assumes a valid isbn10 and EAN Bookland code (could also be 979)."""
isbn = isbn.replace("-", "")
isbn = code + isbn[:-1]
return isbn + calc_checkdigit(isbn) # Generate new check digit

def convert_to_isbn10(isbn13):
"""Assumes a valid isbn13."""
isbn = isbn.replace("-", "")
if isbn13.startswith("978") or isbn13.startswith("979"):
return isbn13[3:-1] + calc_checkdigit(isbn13[3:-1])
else:
raise Exception("ISBN13 not a Bookland ISBN.")

Labels: , ,