学习Sanic遇到的问题系列之一:app.update_config()

前言

从Flaskl转到Sanic,在学习过程中遇到一些问题,我将尽力把它说明清楚,预计会形成一个系列文章。

学习Sanic遇到的问题系列之一:app.update_config()

在使用Flask,教程中使用配置类如下:

# config.py
import os

class Config:
    BASE_DIR = os.path.dirname(os.path.dirname(__file__))

    @staticmethod
    def init_app(app):
        pass

class DevConfig(Config):
    DEBUG = True
    DB_URL = 'mysql://test:Tes@*.*.*.*:3306/test'


class ProConfig(Config):
    DEBUG = False
    DB_URL = 'mysql://production:production@*.*.*.*:3306/production'


config = {
    'development': DevConfig,
    'production': ProConfig,

    'default': DevConfig
}

使用的时候如下:

app = Flask(__name__)
app.config.from_object(DevConfig) 

不同环境使用不同的配置,一些相同的配置可以放在Config基类中(比如BASE_DIR)。

在Sanic中,app.update_config()也可以传入一个类,但是却出了意外:

app = Sanic(__name__)
app.update_config(Devconfig)	# 这时配置中没有父类的BASE_DIR

看看sanic中update_config()的代码,它是通过__dict__来获取属性,这时父类的属性是读不到:

def update_config(self, config: Union[bytes, str, dict, Any]):
		# config是bytes,str,Path类型的处理
	if isinstance(config, (bytes, str, Path)):
		config = load_module_from_file_location(location=config)
	# 不管是传入类,还是对象,都是通过__dict__来获取属性,这时其父类的属性是读不到的
    	if not isinstance(config, dict):
    		cfg = {}
          	if not isclass(config):
              cfg.update(
                  {
                      key: getattr(config, key)
                      for key in config.__class__.__dict__.keys()	# 通过对象的类的__dict__得到类属性
                  }	
              )

          config = dict(config.__dict__)	# 直接使用__dict__,得到属性
          config.update(cfg)

      config = dict(filter(lambda i: i[0].isupper(), config.items()))

      self.update(config)

而flask的app.config.from_object(),是采用dir(object)来获得属性,它会把父类的属性也读出来,然后用getattr(obj, key)读到属性值:

def from_object(self, obj):
     if isinstance(obj, string_types):
         obj = import_string(obj)
     for key in dir(obj):	# 使用dir(obj),会把父类的属性也读出来
         if key.isupper():	# 判断一下key是否是大写
             self[key] = getattr(obj, key)

问题就出现在是使用__dict__还是使用dir(object)。这两者的区别如下:

__dict__:查看类中所有属性,是一个字典。

  • 需要注意的一点是,该属性可以用类名或者类的实例对象来调用,用类名直接调用__dict__,会输出该由类中所有类属性组成的字典;而使用类的实例对象调用__dict__,会输出由类中所有实例属性组成的字典。
  • 对于具有继承关系的父类和子类来说,父类有自己的__dict__,同样子类也有自己的__dict__,它不会包含父类的__dict__

dir()函数:查看对像内所有属性及方法的名称列表。

  • 如果dir(类):返回类属性、方法名称。
  • dir(对象):返回类属性、实例属性和方法名称。

怎么解决这个问题呢,参照Flask的办法,使用dir(object)来获取config类(对象)的属性,并转换成字典后做为参数传入update_config()。

  1. 在app.update_config()时做转换
conf = config[config_name]
app.update_config({key: getattr(conf, key) for key in dir(conf) if key.isupper()})
print(app.config.BASE_DIR)
  1. 在基类Config中加入类方法to_dict()
# config.py
import os

class Config:
    BASE_DIR = os.path.dirname(os.path.dirname(__file__))

    @staticmethod
    def init_app(app):
        pass

    # 加入一个类方法,返回类的所有名称为大写的类属性
    @classmethod
    def to_dict(cls):
        return {key: getattr(cls, key) for key in dir(cls) if key.isupper()}


class DevConfig(Config):
    DEBUG = True
    ACCESS_LOG = False
    DB_URL = 'mysql://test:Tes@*.*.*.*:3306/test'


class ProConfig(Config):
    DEBUG = False
    ACCESS_LOG = False
    DB_URL = 'mysql://production:production@*.*.*.*:3306/production'


config = {
    'development': DevConfig,
    'production': ProConfig,

    'default': DevConfig
}

# app.py
from sanic import Sanic
form config import config

app = Sanic(__name__)
conf_name = 'development'
# 调用config类的类方法to_dict()
app.update_config(config[conf_name].to_dict())

另外,在sanic的update_config()中,使用了isclass()方法来判断传入的是一个类,还是对象。

def isclass(object):
    return isinstance(object, type)

很简单的只使用了isinstance()来判断。在python中,【一切皆对象(object)】。

  conf = DevConfig()
  print(isinstance(conf, object))	# True
  print(isinstance(conf(), object))	# True
  print(isinstance(conf, type)) 	# True
  print(isinstance(conf(), type))	# False

conf、conf()都是object,但conf(类)是type,conf()(对象)不是type。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值