Asked By – Rigel Di Scala
In my endless quest in over-complicating simple stuff, I am researching the most ‘Pythonic’ way to provide 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
import config dbname = config.MYSQL_DATABASE assert(isinstance(config.MYSQL_PORT, int))
It makes sense, but sometimes can be a little messy, especially when you’re trying to remember the names of certain variables. Besides, providing a ‘configuration’ object, with variables as attributes, might be more flexible. So, taking a lead from bpython config.py file, I came up with:
class Struct(object): def __init__(self, *args): self.__header__ = str(args) 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 a ‘config.py’ that 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 is used in 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))
Which seems a more readable, expressive and flexible way of storing and fetching global variables inside a package.
Lamest idea ever? What is the best practice for coping with these situations? What is your way of storing and fetching global names and variables inside your package?
Now we will see solution for issue: Most Pythonic way to provide global configuration variables in config.py? [closed]
I did that once. Ultimately I found my simplified basicconfig.py adequate for my needs. You can pass in a namespace with other objects for it to reference if you need to. You can also pass in additional defaults from your code. It also maps attribute and mapping style syntax to the same configuration object.