在buildbot master.cfg文件的上下文中,如何使用本机模块基于字符串实例化类

时间:2015-03-12 16:24:14

标签: python twisted introspection buildbot

在库存python中,我可以执行以下操作,根据包含其名称的字符串实例化一个类:

#!/usr/bin/env python2.7
import sys
class Foo(object): pass
cls = getattr(sys.modules[__name__], 'Foo')
instance = cls()
print repr(instance)

其中输出以下内容:

ahammond@af6119›~⁑ ./test.py
<__main__.Foo object at 0x1095b0a10>

我想在buildbot master.cfg文件中做类似的事情,但是以下(简化)

class BaseBuild(object): pass

class BuildStashToSrpmsToRpmsToDepot(BaseBuild):

    def init(name, build_type):
        self.name = name

    def setup():
        pass  # actually does stuff here, but...

for build_name, build_options in config['builds'].iteritems():
    cls = getattr(sys.modules[__name__], build_options['build_type'])
    build = cls(name=build_name, **build_options)
    build.setup()

可生产

2015-03-11 18:39:24-0700 [-] error while parsing config file:
    Traceback (most recent call last):
      File "/opt/buildbot_virtualenv/lib/python2.7/site-packages/twisted/internet/defer.py", line 577, in _runCallbacks
        current.result = callback(current.result, *args, **kw)
      File "/opt/buildbot_virtualenv/lib/python2.7/site-    packages/twisted/internet/defer.py", line 1155, in gotResult
        _inlineCallbacks(r, g, deferred)
      File "/opt/buildbot_virtualenv/lib/python2.7/site-packages/twisted/internet/defer.py", line 1099, in _inlineCallbacks
        result = g.send(result)
      File "/opt/buildbot_git/master/buildbot/master.py", line 189, in startService
        self.configFileName)
    --- <exception caught here> ---
      File "/opt/buildbot_git/master/buildbot/config.py", line 156, in loadConfig
        exec f in localDict
      File "/home/buildbot/master.cfg", line 208, in <module>
        cls = getattr(sys.modules[__name__], build_options['build_type'])
    exceptions.AttributeError: 'module' object has no attribute 'BuildStashToSrpmsToRpmsToDepot'

2015-03-11 18:39:24-0700 [-] Configuration Errors:
2015-03-11 18:39:24-0700 [-]   error while parsing config file: 'module' object has no attribute 'BuildStashToSrpmsToRpmsToDepot' (traceback in logfile)

另一种说法,我想我真正想问的是加载新master.cfg时使用的临时模块是什么,我该如何引用它?

我目前正在使用{'class name':class_object}的字典映射,但我更喜欢更原生的东西。

2 个答案:

答案 0 :(得分:1)

你的问题是:运行buildbot时,你的dunder名称(__ name __没有空格......)当buildbot exec你的配置是buildbot.config时,这就是为什么&#39;模块对象没有属性.. 。&#39;

我认为你可以做你想要的东西,使你的build_options字典的值成为类本身,而不是带有类名的字符串。像这样:

class BaseBuild(object): pass

class BuildStashToSrpmsToRpmsToDepot(BaseBuild):

    def init(name, build_type):
        self.name = name

    def setup():
        pass  # actually does stuff here, but...

# here the dict goes with key/class not key/class name
build_options = {'build_type': BuildStashToSrpmsToRpmsToDepot}

for build_name, build_options in config['builds'].iteritems():
    cls = build_options['build_type']
    build = cls(name=build_name, **build_options)
    build.setup() 

以防万一,这就是buildbot exec master.cfg(module buildbot.config):

# ...
# execute the config file
localDict = {
    'basedir': os.path.expanduser(basedir),
    '__file__': os.path.abspath(filename),
}

# from here on out we can batch errors together for the user's
# convenience
global _errors
_errors = errors = ConfigErrors()

old_sys_path = sys.path[:]
sys.path.append(basedir)
try:
    try:
        exec f in localDict
    except ConfigErrors, e:
        for err in e.errors:
            error(err)
        raise errors
    except:
        log.err(failure.Failure(), 'error while parsing config file:')
        error("error while parsing config file: %s (traceback in logfile)" % (sys.exc_info()[1],),)
        raise errors
finally:
    f.close()
    sys.path[:] = old_sys_path
        _errors = None
# ...

答案 1 :(得分:1)

好的,问题出在这里:

cls = getattr(sys.modules[__name__], build_options['build_type'])

这不起作用,因为exec使__name__具有值"__builtin__"。但是,您可以使用globals()来获取当前的全局变量:

cls = globals()[build_options['build_type']]

例如,如果我将以下代码添加到全新的master.cfg文件(由buildbot create-master master自动创建的文件,从master.cfg.sample重命名):

# Load the configuration from somewhere.
import json
config = json.load(open("./config.json"))

class BaseBuild(object):
    pass

class Something(BaseBuild):

    def __init__(self, name, build_type):
        self.name = name

    def setup(self):
        print self.name, "something setup called"

class SomethingElse(BaseBuild):

    def __init__(self, name, build_type):
        self.name = name

    def setup(self):
        print self.name, "something else setup called"

for build_name, build_options in config['builds'].iteritems():
    cls = globals()[build_options['build_type']]
    build = cls(name=build_name, **build_options)
    build.setup()

我在config.json所在的目录中创建了以下master.cfg文件:

{
    "builds": {
        "one": {
            "build_type": "Something"
        },
        "two": {
            "build_type": "SomethingElse"
        }
    }
}

然后当我运行buildbot start master时,我会在日志中找到这些行:

2015-03-13 12:11:05-0400 [-] two something else setup called
2015-03-13 12:11:05-0400 [-] one something setup called