File: plugin_info.py

package info (click to toggle)
errbot 6.1.7+ds-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 3,712 kB
  • sloc: python: 13,831; makefile: 164; sh: 97
file content (112 lines) | stat: -rw-r--r-- 3,921 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import inspect
import sys
from configparser import ConfigParser
from configparser import Error as ConfigParserError
from dataclasses import dataclass
from importlib._bootstrap import module_from_spec
from importlib._bootstrap_external import spec_from_file_location
from pathlib import Path
from typing import List, Tuple, Type

from errbot.utils import version2tuple

VersionType = Tuple[int, int, int]


@dataclass
class PluginInfo:
    name: str
    module: str
    doc: str
    core: bool
    python_version: VersionType
    errbot_minversion: VersionType
    errbot_maxversion: VersionType
    dependencies: List[str]
    location: Path = None

    @staticmethod
    def load(plugfile_path: Path) -> "PluginInfo":
        with plugfile_path.open(encoding="utf-8") as plugfile:
            return PluginInfo.load_file(plugfile, plugfile_path)

    @staticmethod
    def load_file(plugfile, location: Path) -> "PluginInfo":
        cp = ConfigParser()
        cp.read_file(plugfile)
        pi = PluginInfo.parse(cp)
        pi.location = location
        return pi

    @staticmethod
    def parse(config: ConfigParser) -> "PluginInfo":
        """
        Throws ConfigParserError with a meaningful message if the ConfigParser doesn't contain the minimal
         information required.
        """
        name = config.get("Core", "Name")
        module = config.get("Core", "Module")
        core = config.get("Core", "Core", fallback="false").lower() == "true"
        doc = config.get("Documentation", "Description", fallback=None)

        python_version = config.get("Python", "Version", fallback=None)
        # Old format backward compatibility
        if python_version:
            if python_version in ("2+", "3"):
                python_version = (3, 0, 0)
            elif python_version == "2":
                python_version = (2, 0, 0)
            else:
                try:
                    python_version = tuple(
                        version2tuple(python_version)[0:3]
                    )  # We can ignore the alpha/beta part.
                except ValueError as ve:
                    raise ConfigParserError(
                        f"Invalid Python Version format: {python_version} ({ve})"
                    )

        min_version = config.get("Errbot", "Min", fallback=None)
        max_version = config.get("Errbot", "Max", fallback=None)
        try:
            if min_version:
                min_version = version2tuple(min_version)
        except ValueError as ve:
            raise ConfigParserError(
                f"Invalid Errbot min version format: {min_version} ({ve})"
            )

        try:
            if max_version:
                max_version = version2tuple(max_version)
        except ValueError as ve:
            raise ConfigParserError(
                f"Invalid Errbot max version format: {max_version} ({ve})"
            )
        depends_on = config.get("Core", "DependsOn", fallback=None)
        deps = [name.strip() for name in depends_on.split(",")] if depends_on else []

        return PluginInfo(
            name, module, doc, core, python_version, min_version, max_version, deps
        )

    def load_plugin_classes(self, base_module_name: str, baseclass: Type):
        # load the module
        module_name = base_module_name + "." + self.module
        spec = spec_from_file_location(
            module_name, self.location.parent / (self.module + ".py")
        )
        modu1e = module_from_spec(spec)
        spec.loader.exec_module(modu1e)
        sys.modules[module_name] = modu1e

        # introspect the modules to find plugin classes
        def is_plugin(member):
            return (
                inspect.isclass(member)
                and issubclass(member, baseclass)
                and member != baseclass
            )

        plugin_classes = inspect.getmembers(modu1e, is_plugin)
        return plugin_classes