In my endless search for overcomplicating simple things, I study the most “Putin's” way of providing global configuration variables inside the typical “ config.py ” found in Python egg packages.
The traditional way (aah, good ol '#define!) Is as follows:
MYSQL_PORT = 3306 MYSQL_DATABASE = 'mydb' MYSQL_DATABASE_TABLES = ['tb_users', 'tb_groups']
Therefore, global variables are imported in one of the following ways:
from config import * dbname = MYSQL_DATABASE for table in MYSQL_DATABASE_TABLES: print table
or
import config dbname = config.MYSQL_DATABASE assert(isinstance(config.MYSQL_PORT, int))
This makes sense, but can sometimes be a little messy, especially when you are trying to remember the names of certain variables. In addition, providing the 'configuration of an object as attributes can be more flexible. So, taking the lead from the bpython config.py configuration file, I came up with:
class Struct(object): def __init__(self, *args): self.__header__ = str(args[0]) if args else None def __repr__(self): if self.__header__ is None: return super(Struct, self).__repr__() return self.__header__ def next(self): """ Fake iteration functionality. """ raise StopIteration def __iter__(self): """ Fake iteration functionality. We skip magic attribues and Structs, and return the rest. """ ks = self.__dict__.keys() for k in ks: if not k.startswith('__') and not isinstance(k, Struct): yield getattr(self, k) def __len__(self): """ Don't count magic attributes or Structs. """ ks = self.__dict__.keys() return len([k for k in ks if not k.startswith('__')\ and not isinstance(k, Struct)])
and "config.py", which imports the class and reads as follows:
from _config import Struct as Section mysql = Section("MySQL specific configuration") mysql.user = 'root' mysql.pass = 'secret' mysql.host = 'localhost' mysql.port = 3306 mysql.database = 'mydb' mysql.tables = Section("Tables for 'mydb'") mysql.tables.users = 'tb_users' mysql.tables.groups = 'tb_groups'
and used this way:
from sqlalchemy import MetaData, Table import config as CONFIG assert(isinstance(CONFIG.mysql.port, int)) mdata = MetaData( "mysql://%s:%s@%s:%d/%s" % ( CONFIG.mysql.user, CONFIG.mysql.pass, CONFIG.mysql.host, CONFIG.mysql.port, CONFIG.mysql.database, ) ) tables = [] for name in CONFIG.mysql.tables: tables.append(Table(name, mdata, autoload=True))
What seems to be a more readable, expressive, and flexible way to store and retrieve global variables within a package.
Best known idea? What is the best practice for dealing with these situations? What is your way of storing and choosing global names and variables inside your package?