1"""pyang plugin handling"""
2
3import os
4import sys
5import pkg_resources
6
7plugins = []
8"""List of registered PyangPlugin instances"""
9
10def init(plugindirs=[]):
11    """Initialize the plugin framework"""
12
13    # initialize the builtin plugins
14    from .translators import yang,yin,dsdl
15    yang.pyang_plugin_init()
16    yin.pyang_plugin_init()
17    dsdl.pyang_plugin_init()
18
19    # initialize installed plugins
20    for ep in pkg_resources.iter_entry_points(group='pyang.plugin'):
21        plugin_init = ep.load()
22        plugin_init()
23
24    # search for plugins in std directories (plugins directory first)
25    basedir = os.path.split(sys.modules['pyang'].__file__)[0]
26    plugindirs.insert(0, basedir + "/transforms")
27    plugindirs.insert(0, basedir + "/plugins")
28
29    # add paths from env
30    pluginpath = os.getenv('PYANG_PLUGINPATH')
31    if pluginpath is not None:
32        plugindirs.extend(pluginpath.split(os.pathsep))
33
34    syspath = sys.path
35    for plugindir in plugindirs:
36        sys.path = [plugindir] + syspath
37        try:
38            fnames = os.listdir(plugindir)
39        except OSError:
40            continue
41        for fname in fnames:
42            if not fname.startswith(".#") and fname.endswith(".py") and \
43               fname != '__init__.py' and not fname.endswith("_flymake.py"):
44                pluginmod = __import__(fname[:-3])
45                try:
46                    pluginmod.pyang_plugin_init()
47                except AttributeError as s:
48                    print(pluginmod.__dict__)
49                    raise AttributeError(pluginmod.__file__ + ': ' + str(s))
50        sys.path = syspath
51
52def register_plugin(plugin):
53    """Call this to register a pyang plugin. See class PyangPlugin
54    for more info.
55    """
56    plugins.append(plugin)
57
58def is_plugin_registered(name):
59    for plugin in plugins:
60        if plugin.name == name:
61            return True
62    return False
63
64class PyangPlugin(object):
65    """Abstract base class for pyang plugins
66
67    A pyang plugin is a module found in the plugins directory of the
68    pyang installation, or in the dynamic pluginpath.
69
70    Such a module must export a function 'pyang_plugin_init()', which
71    may call pyang.plugin.register_plugin() with an instance of a class
72    derived from this class as argument.
73
74    A plugin can extend the base pyang library functions, or the pyang
75    front-end program, or both.
76    """
77
78    def __init__(self, name=None):
79        self.name = name
80        self.multiple_modules = False
81        self.handle_comments = False
82
83    ## pyang front-end program methods
84
85    def add_output_format(self, fmts):
86        """Add an output format to the pyang program.
87
88        `fmts` is a dict which maps the format name string to a plugin
89        instance.
90
91        Override this method and update `fmts` with the output format
92        name.
93        """
94        return
95
96    def add_transform(self, xforms):
97        """Add a transform to the pyang program.
98
99        `xforms` is a dict which maps the transform name string to a plugin
100        instance.
101
102        Override this method and update `xforms` with the transform name.
103        """
104        return
105
106    def add_opts(self, optparser):
107        """Add command line options to the pyang program.
108
109        Override this method and add the plugin related options as an
110        option group.
111        """
112        return
113
114    ## library methods
115
116    def setup_ctx(self, ctx):
117        """Modify the Context at setup time.  Called for all plugins.
118
119        Override this method to modify the Context before the module
120        repository is accessed.
121        """
122        return
123
124    def setup_fmt(self, ctx):
125        """Modify the Context at setup time.  Called for the selected
126        output format plugin.
127
128        Override this method to modify the Context before the module
129        repository is accessed.
130        """
131        return
132
133    def setup_xform(self, ctx):
134        """Modify the Context at setup time.  Called for the selected
135        transform plugin.
136
137        Override this method to modify the Context before the module
138        repository is accessed.
139        """
140        return
141
142    def pre_load_modules(self, ctx):
143        """Called for the selected plugin, before any modules are loaded"""
144        return
145
146    def pre_validate_ctx(self, ctx, modules):
147        """Called for all plugins, before the modules are validated"""
148        return
149
150    def pre_validate(self, ctx, modules):
151        """Called for the selected plugin, before the modules are validated"""
152        return
153
154    def post_validate(self, ctx, modules):
155        """Called for the selected plugin, after the modules
156        have been validated"""
157        return
158
159    def post_validate_ctx(self, ctx, modules):
160        """Called for all plugins, after the modules
161        have been validated"""
162        return
163
164    def emit(self, ctx, modules, writef):
165        """Produce the plugin output.
166
167        Override this method to perform the output conversion.
168        `writef` is a function that takes one string to print as argument.
169
170        Raise error.EmitError on failure.
171        """
172        return
173
174    def transform(self, ctx, modules):
175        """Transform the modules (called after modules have been validated).
176
177        Override this method to modify the modules.
178        Return `True` to indicate either that none of the modifications
179        require modules to be re-validated or that the modules have already
180        been re-validated.
181
182        Raise error.TransformError on failure."""
183