from panflute import Doc, Div import os import warnings # This class is used to keep state while transforming the document using # transform.py. For the context to be available to the html and TeX generators, # individual keys must be manually assigned to the individual elements. This is # done in transform.py. # # The context is also aware of its parent contexts and relevant data (such as # metadata and commands) can be read from the closest parent context. Writing # only happens to the current one. # # This class is basically an extension to panflute's doc, this is why metadata # is read directly from it. class Context: def __init__(self, doc: Doc, path: str, parent: 'Context'=None, trusted: bool=True): self.parent = parent self._commands = {} self.doc = doc self.trusted = trusted self.path = path self.dir = os.path.dirname(path) if os.path.dirname(path) != "" else "." self.filename = os.path.basename(path) if self.get_metadata("flags", immediate=True) is None: self.set_metadata("flags", {}) def get_command(self, command: str): if command in self._commands: return self._commands[command] elif self.parent: return self.parent.get_command(command) else: return None def set_command(self, command: str, val): self._commands[command] = val def unset_command(self, command: str): del self._commands[command] def is_flag_set(self, flag: str): if self.get_metadata("flags."+flag): if self.get_metadata("flags."+flag): return True else: return False elif self.parent: return self.parent.is_flag_set(flag) else: return False def set_flag(self, flag: str, val: bool): self.set_metadata("flags."+flag, val) def unset_flag(self, flag: str): self.unset_metadata("flags."+flag) def get_metadata(self, key: str, simple: bool=True, immediate: bool=False): value = self.doc.get_metadata(key, None, simple) if value is not None: return value elif self.parent and not immediate: return self.parent.get_metadata(key) else: return None def set_metadata(self, key: str, value): if key == "language": warnings.warn("Setting language this way doesn't propagate to TeX. Either use the Front Matter or specify it additionally using the \\languagexx macro.", UserWarning) meta = self.doc.metadata keys = key.split(".") for k in keys[:-1]: meta = meta[k] meta[keys[-1]] = value def unset_metadata(self, key: str): meta = self.doc.metadata keys = key.split(".") for k in keys[:-1]: meta = meta[k] del meta.content[key[-1]] # A hack because MetaMap doesn't have a __delitem__ # This is a custom element which creates \begingroup \endgroup groups in TeX # and also causes KaTeX math blocks to be isolated in a similar way. # # Whenever a new context is created, its content should be eclosed in a group and vice-versa. class Group(Div): def __init__(self, *args, metadata={}, **kwargs): self.metadata = metadata super().__init__(*args, **kwargs)