如何实现复合配置变量或“模块级属性?”

时间:2015-12-16 16:47:27

标签: python python-2.7

如果我有配置文件default.py

HOST = 'localhost'
PORT = 8080
PROTOCOL = 'http' if PORT != 443 else 'https'
ROOT_STR = '%s://%s:%s' % (PROTOCOL, HOST, PORT)

然后我有local.py覆盖默认变量以适应当地环境:

PORT = 443

我如何动态重新生成PROTOCOLROOT_STR,就像它们是类的属性一样,而是在模块的根级别?

通过更改一个变量,生成的配置将显示为:

HOST = 'localhost'
PORT = 443
PROTOCOL = 'https'
ROOT_STR = 'https://localhost:443'

预期的行为应该为类属性建模:

class Configuration(object):
    def __init__(self):
        self.host = 'localhost'
        self.port = 8080

    @property
    def protocol(self):
        return 'https' if self.port == 443 else 'http'

    @property
    def root_str(self):
        return '%s://%s:%s' % (self.protocol, self.host, self.port)

...
...

>>> c = Configuration()
>>> c.port = 443
>>> c.root_str
'https://localhost:443'

有没有比修改AST更简洁的方法?

思想?

2 个答案:

答案 0 :(得分:1)

我使用了自定义配置import re import types CONST_VARS = re.compile(r'[A-Z]+[_A-Z]*') # Configuration primitives that allow for dynamic # variable compilation for default/local/dev/etc settings files. class ConfigType(type): def __init__(cls, name, bases, odict): if not hasattr(cls, 'vars'): cls.vars = {} reg_vars = filter(lambda x: CONST_VARS.match(x[0]), odict.items()) for k, v in reg_vars: cls.vars[k] = v delattr(cls, k) super(ConfigType, cls).__init__(name, bases, cls.vars) class Configuration(object): __metaclass__ = ConfigType def __init__(self): pass def __setattr__(self, key, value): self.vars[key] = value def __getattr__(self, item): ret = self.vars.get(item) if isinstance(ret, types.FunctionType): return ret(self) return ret def as_module(self): mod_vars = {} for k, v in self.vars.items(): if isinstance(v, types.FunctionType) and callable(v): v = v(self) mod_vars[k] = v return type('Configuration', (object,), mod_vars) Configuration,粗略实现如下。

settings/default.py

然后,如果我创建了两个class DatabaseConfig(Configuration): MYSQL_DB = 'DEFAULT' MYSQL_USER = os.environ.get('MYSQL_USER') MYSQL_PASS = os.environ.get('MYSQL_PASS') MYSQL_HOST = os.environ.get('MYSQL_HOST', 'localhost') MYSQL_PORT = os.environ.get('MYSQL_PORT', 3306) def MYSQL_URI(self): return 'mysql://{user}:{password}@{server}:{port}'.format( user=self.MYSQL_USER, password=self.MYSQL_PASS, server=self.MYSQL_HOST, port=self.MYSQL_PORT ) def SQLALCHEMY_DATABASE_URI(self): return str(self.MYSQL_URI) + '/' + self.MYSQL_DB DatabaseConfig() 个对象:

<强> settings/local.py

class LocalConfig(Configuration):
    MYSQL_DB = 'mycoolstuff'
    MYSQL_USER = 'root'
    MYSQL_PASS = 'rootpass'
    MYSQL_HOST = '172.17.42.1'  # Docker
    MYSQL_PORT = 3306

Config = LocalConfig()

<强> LocalConfig

>>> import settings.default
>>> from settings.local import Config
>>> Config.MYSQL_PORT
3306
>>> Config.MYSQL_URI
mysql://root:rootpass@172.17.42.1:3306

当我导入app.get('/:user_id', function (req, res){ console.log('Test user_id param: ' + req.params.user_id); }); 时,我的变量将自动计算,所有函数都充当属性,所有类属性充当变量。

app.use('\(:user_id\)', function (req, res){ 
        console.log('Test param: ' + req.params.user_id);
    });

答案 1 :(得分:-1)

您可以将from default import *添加到local.py文件的顶部。这应该使default.py中的所有变量和函数都可用,就像它们位于local.py中一样。