设计python插件系统

设计python插件系统,python,plugins,Python,Plugins,我目前正在开发一个系统,该系统处理用户指定的指标列表(在文件中);度量是关于如何执行特定计算的一些字符串和一些逻辑的集合。python脚本定期在linux服务器上运行,并查阅该文件 度量应该很容易扩展,所以我正在寻找一些模块化的方法来将计算代码封装到度量规范中。除了创建一个配置解析系统(这将严重限制计算的复杂性)之外,我正在寻找一个插件系统,其中每个度量的计算都可以在python代码中指定 例如,以下是度量的核心单位 db = "database0" mes = "measurement0" t

我目前正在开发一个系统,该系统处理用户指定的
指标列表(在文件中)
;度量是关于如何执行特定计算的一些字符串和一些逻辑的集合。python脚本定期在linux服务器上运行,并查阅该文件

度量应该很容易扩展,所以我正在寻找一些模块化的方法来将计算代码封装到度量规范中。除了创建一个配置解析系统(这将严重限制计算的复杂性)之外,我正在寻找一个插件系统,其中每个度量的计算都可以在python代码中指定

例如,以下是度量的核心单位

db = "database0"
mes = "measurement0"
tags = ["alpha", "beta", "delta"]
calculation: (accepts parameters a and b)
    for item in a:
        b.add(a)
    return b
python脚本需要从文件中获取每个度量,访问它们的字符串字段并执行它们的计算代码,将相同的参数传递给每个度量

我现在有一个可怕的方法;在一个单独的python文件中,我定义了一个基类,该基类将根据每个新度量进行扩展。这就是它看起来的样子

class Metric(object):
    def __init__(self, database_name, measurement_name, classad_tags, classad_fields=[]):
        """
        A specification of a metric.
        Arguments:
            database_name    - name of the influx DB (created if doesn't exist)
            measurement_name - measurement name with which to label metric in DB
            classad_tags     - list of classad fields (or mock ads) which will
                               segregate values at a time for this metric, becoming
                               tags in the influxDB measurement
            classad_fields   - any additional job classad fields that this metric will
                               look at (e.g. for metric value calculation).
                               These must be declared so that the daemon can fetch any
                               needed classads from condor
        """
        self.db = database_name
        self.mes = measurement_name
        self.tags = classad_tags
        self.fields = classad_fields

    def calculate_at_bin(self, time_bin, jobs):
        """
        """

        raise ReferenceError("")


"""
--------------------------------------------------------------------------------------
"""


class metric0(Metric):
    def __init__(self):

        db = 'testdb'
        mes = 'testmes0'
        tags = ['Owner']
        fields = []
        super(metric0, self).__init__(db, mes, tags, fields)

    def calculate_at_bin(self, time_bin, jobs):

        for job in jobs:
            if job.is_idle_during(time_bin.start_time, time_bin.end_time):
                time_bin.add_to_sum(1, job.get_values(self.tags))
        return time_bin.get_sum()
要创建新的度量,您需要创建一个新的
metric
子类,将字符串字段传递给超级构造函数并重写
calculate\u at\u bin
方法。在主python脚本中,我创建了每个度量的一个实例,其中包含以下内容

metrics = [metric() for metric in vars()['Metric'].__subclasses__()]
然而,这显然是可怕的。 它涉及完全任意但要求唯一的子类名称(
metric0
)和与度量不相关的代码(例如类定义、调用构造函数等)

我还可以想象另一种糟糕的方法,比如一个配置文件,其中包含一些代码,这些代码会得到
eval
'd,但我也想避免这种情况

注意,没有关于安全性或代码注入的顾虑;这个系统只会被我能亲自指导的少数人使用。
所以我该怎么做?

您是否考虑过将度量的代码作为一个可导入模块,提供一个
calculate\u at\u bin
函数作为“接口”,将所有此类模块放在一个目录中,然后动态导入它们?将消除可能出现的名称冲突问题,如果不考虑安全性,至少处理起来非常简单…

在stackoverflow标题中包含“最佳实践”会使人容易受到DownVotes的攻击您是否建议将每个指标放入单独的文件中,只创建一些实例变量,如db、mes。。定义了计算函数?我想我希望将度量值放在一个文件中(这似乎是不可靠和特别的;肯定有一些优雅的方法可以做到这一点),每个度量值都放在它自己的文件中,如果可能的话,还可以共享数据库等。这将很容易扩展(添加新的模块文件,无需修改,即使在语法错误上也可以进行简单的异常处理),以及内省(本质上,调用
os.listdir()
会很优雅。它也解决了你提到的所有糟糕的事情。很抱歉误解了你。我不能每个度量都有一个文件,除非它们都在一个文件夹中,这将在扩展时遇到问题(它们都需要添加到中)。不确定你的意思是什么“如果可能的话,共享数据库等。”你是说我可以使用
os.listdir()
?我如何使用它在一个独立的名称空间中执行python文件?是的,都在一个文件夹中。不,它们不必添加到
\u init\uuuuuuuuuuuuuuuy
os.listdir()
的目的是收集所有(度量-)来自该文件夹的模块。再次抱歉,如果该方法不适合您的需要。这只是一个解决问题中列出的缺陷的建议。文件名仍然是完全任意的,因此不直观。因此,为了澄清,您建议使用
os.lostdir()
,字符串操纵python模块名称,以便能够使用
importlib
导入它们?我相信还有一种更优雅的方法