diff --git a/mkdocs_macros/plugin.py b/mkdocs_macros/plugin.py index d3f5b66..729c687 100644 --- a/mkdocs_macros/plugin.py +++ b/mkdocs_macros/plugin.py @@ -66,6 +66,20 @@ def _fail_with_undefined_error(self, *args, **kwargs): # Plugin # ------------------------------------------ +# little utility for updating a dictionary from another +def register_items(category:str, ref:dict, additional:dict): + """ + Register outside items (additional) into a ref dictionary. + Fail with KeyError the key already exists. + + E.g: register_items('macro', self.macros, items) + """ + for key, value in additional.items(): + if key in ref: + raise KeyError("Registration error: " + "%s %s already exists" % (category, key)) + ref[key] = value + class MacrosPlugin(BasePlugin): """ @@ -113,6 +127,15 @@ class MacrosPlugin(BasePlugin): ('verbose', PluginType(bool, default=False)) ) + + + # these are lists of external items (loaded last) + # in case they are declared before on_config is run + # (i.e. other plugin is running before this one) + _add_macros = {} + _add_filters = {} + _add_variables = {} + def start_chatting(self, prefix: str, color: str = 'yellow'): "Generate a chatter function (trace for macros)" def chatter(*args): @@ -301,6 +324,51 @@ def raw_markdown(self, value): "as of 1.1.0; use env.markdown instead!") self.markdown = value + # ---------------------------------- + # Hooks for other applications + # ---------------------------------- + @property + def register_macro(self, items:dict): + """ + Register macros (hook for other plugins). + These will be added last, and raise an exception if already present. + """ + try: + # after on_config + self._macros + register_items('macro', self.macros, items) + except AttributeError: + # before on_config: store for later + self._add_macros += items + + @property + def register_filters(self, items:dict): + """ + Register filters (hook for other plugins). + These will be added last, and raise an exception if already present. + """ + try: + # after on_config + self._filters + register_items('filter', self.filters, items) + except AttributeError: + # before on_config: store for later + self._add_filters += items + + @property + def register_variables(self, items:dict): + """ + Register variables (hook for other plugins). + These will be added last, and raise an exception if already present. + """ + try: + # after on_config + self._variables + register_items('variables', self.variables, items) + except AttributeError: + # before on_config: store for later + self._add_variables += items + # ---------------------------------- # Function lists, for later events # ---------------------------------- @@ -632,6 +700,20 @@ def on_config(self, config): # by design, this MUST be the last step, so that programmers have # full control on what happened in the configuration files self._load_modules() + + + # place where variables/macros/filters are registered + # if they they were declared before Mkdocs-Macros in the config file. + # self._add_variables['foo'] = 5 + # def bar(x): + # "Dummy function" + # return x + 5 + # self._add_macros['bar'] = bar + # self._add_filters['baz'] = lambda s: s.upper() + register_items('variable', self.variables, self._add_variables) + register_items('macro' , self.macros , self._add_macros ) + register_items('filter' , self.filters , self._add_filters ) + # Provide information: debug("Variables:", list(self.variables.keys())) if len(extra): diff --git a/test/module/main.py b/test/module/main.py index 52117a3..8227523 100644 --- a/test/module/main.py +++ b/test/module/main.py @@ -31,7 +31,9 @@ def include_file(filename, start_line=0, end_line=None): @env.macro def doc_env(): "Document the environment" - return {name: getattr(env, name) for name in dir(env) if not name.startswith('_')} + return {name: getattr(env, name) + for name in dir(env) if not + (name.startswith('_') or name.startswith('register'))} # Optional: a special function for making relative urls point to root fix_url = env.macros.fix_url