1.1 --- a/moinformat/__init__.py Thu Aug 17 23:33:18 2023 +0200
1.2 +++ b/moinformat/__init__.py Thu Aug 17 23:34:10 2023 +0200
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 Moin wiki format tools.
1.6
1.7 -Copyright (C) 2017, 2018, 2019 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2017, 2018, 2019, 2023 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -26,6 +26,7 @@
1.13 from moinformat.parsers import get_parser, make_parser, parse
1.14 from moinformat.serialisers import get_serialiser, make_serialiser, serialise
1.15 from moinformat.themes import make_theme
1.16 +from moinformat.translators import get_translator, make_translator, translate
1.17 from moinformat.utils.copying import copy_attachments
1.18 import moinformat.errors as errors
1.19
2.1 --- a/moinformat/metadata.py Thu Aug 17 23:33:18 2023 +0200
2.2 +++ b/moinformat/metadata.py Thu Aug 17 23:34:10 2023 +0200
2.3 @@ -25,6 +25,7 @@
2.4 from moinformat.parsers import get_parser, parsers
2.5 from moinformat.serialisers import get_serialiser, serialisers
2.6 from moinformat.themes import get_theme
2.7 +from moinformat.translators import get_translator, translators
2.8
2.9 class Metadata:
2.10
2.11 @@ -44,11 +45,11 @@
2.12
2.13 effects = {
2.14 "input_context" : ["input"],
2.15 - "input_format" : ["parser", "serialiser"],
2.16 + "input_format" : ["parser", "serialiser", "translator"],
2.17 "input_separator" : ["input"],
2.18 "link_format" : ["linker"],
2.19 "output_context" : ["output"],
2.20 - "output_format" : ["serialiser"],
2.21 + "output_format" : ["serialiser", "translator"],
2.22 "theme_name" : ["theme"],
2.23 }
2.24
2.25 @@ -221,4 +222,18 @@
2.26
2.27 return self.make_object("theme", cls)
2.28
2.29 + def get_translator(self, name=None):
2.30 +
2.31 + """
2.32 + Make a translator using any given 'name' or otherwise using the
2.33 + "output_format" setting which will be replaced by any given 'name'.
2.34 + """
2.35 +
2.36 + cls = get_translator(self.get_update("output_format", name),
2.37 + self.get("input_format"))
2.38 +
2.39 + translator = self.make_object("translator", cls)
2.40 + translator.translators = translators
2.41 + return translator
2.42 +
2.43 # vim: tabstop=4 expandtab shiftwidth=4
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/moinformat/translators/__init__.py Thu Aug 17 23:34:10 2023 +0200
3.3 @@ -0,0 +1,52 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +"""
3.7 +Document format translators.
3.8 +
3.9 +Copyright (C) 2017, 2018, 2023 Paul Boddie <paul@boddie.org.uk>
3.10 +
3.11 +This program is free software; you can redistribute it and/or modify it under
3.12 +the terms of the GNU General Public License as published by the Free Software
3.13 +Foundation; either version 3 of the License, or (at your option) any later
3.14 +version.
3.15 +
3.16 +This program is distributed in the hope that it will be useful, but WITHOUT
3.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
3.19 +details.
3.20 +
3.21 +You should have received a copy of the GNU General Public License along with
3.22 +this program. If not, see <http://www.gnu.org/licenses/>.
3.23 +"""
3.24 +
3.25 +from moinformat.translators.manifest import translators
3.26 +
3.27 +# Top-level functions.
3.28 +
3.29 +def get_translator(name, doctype=None):
3.30 +
3.31 + """
3.32 + Return a translator class producing nodes whose document type has the given
3.33 + 'name'. If 'doctype' is indicated, obtain a translator class specific to
3.34 + that document type. Otherwise, a general translator for Moin content is
3.35 + obtained.
3.36 + """
3.37 +
3.38 + return translators["%s.%s" % (name, doctype or "moin")]
3.39 +
3.40 +def make_translator(metadata, doctype=None):
3.41 +
3.42 + """
3.43 + Return a translator instance using the given 'metadata' and optional output
3.44 + 'doctype'.
3.45 + """
3.46 +
3.47 + return metadata.get_translator(doctype)
3.48 +
3.49 +def translate(doc, translator):
3.50 +
3.51 + "Translate 'doc' using the given 'translator' instance."
3.52 +
3.53 + return doc.visit(translator)
3.54 +
3.55 +# vim: tabstop=4 expandtab shiftwidth=4
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/moinformat/translators/common.py Thu Aug 17 23:34:10 2023 +0200
4.3 @@ -0,0 +1,75 @@
4.4 +#!/usr/bin/env python
4.5 +
4.6 +"""
4.7 +Document translator support.
4.8 +
4.9 +Copyright (C) 2017, 2018, 2019, 2021, 2023 Paul Boddie <paul@boddie.org.uk>
4.10 +
4.11 +This program is free software; you can redistribute it and/or modify it under
4.12 +the terms of the GNU General Public License as published by the Free Software
4.13 +Foundation; either version 3 of the License, or (at your option) any later
4.14 +version.
4.15 +
4.16 +This program is distributed in the hope that it will be useful, but WITHOUT
4.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
4.19 +details.
4.20 +
4.21 +You should have received a copy of the GNU General Public License along with
4.22 +this program. If not, see <http://www.gnu.org/licenses/>.
4.23 +"""
4.24 +
4.25 +class Translator:
4.26 +
4.27 + "General translator support."
4.28 +
4.29 + input_formats = None # defined by subclasses
4.30 + formats = None # defined by subclasses
4.31 +
4.32 + def __init__(self, metadata):
4.33 +
4.34 + """
4.35 + Initialise the translator with the given 'metadata'.
4.36 + """
4.37 +
4.38 + self.metadata = metadata
4.39 +
4.40 + # Initialisation of any other state.
4.41 +
4.42 + self.init()
4.43 +
4.44 + def init(self):
4.45 +
4.46 + "Initialisation method to be overridden by subclasses."
4.47 +
4.48 + pass
4.49 +
4.50 + def __repr__(self):
4.51 + return "%s(%r)" % (self.__class__.__name__, self.metadata)
4.52 +
4.53 + # Translation visitor methods.
4.54 +
4.55 + def visit(self, node):
4.56 +
4.57 + """
4.58 + Visit the 'node', invoking the appropriate serialisation handler, and
4.59 + returning the result of the handler.
4.60 + """
4.61 +
4.62 + return node.visit(self)
4.63 +
4.64 + def container(self, container):
4.65 +
4.66 + "Visit all nodes in 'container', returning a list of translated nodes."
4.67 +
4.68 + nodes = []
4.69 +
4.70 + if container.nodes:
4.71 + for node in container.nodes:
4.72 + n = self.visit(node)
4.73 + if n:
4.74 + nodes.append(n)
4.75 +
4.76 + return nodes
4.77 +
4.78 +# vim: tabstop=4 expandtab shiftwidth=4
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/moinformat/translators/manifest.py Thu Aug 17 23:34:10 2023 +0200
5.3 @@ -0,0 +1,48 @@
5.4 +#!/usr/bin/env python
5.5 +
5.6 +"""
5.7 +Document format translator manifest.
5.8 +
5.9 +Copyright (C) 2017, 2018, 2021, 2023 Paul Boddie <paul@boddie.org.uk>
5.10 +
5.11 +This program is free software; you can redistribute it and/or modify it under
5.12 +the terms of the GNU General Public License as published by the Free Software
5.13 +Foundation; either version 3 of the License, or (at your option) any later
5.14 +version.
5.15 +
5.16 +This program is distributed in the hope that it will be useful, but WITHOUT
5.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
5.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
5.19 +details.
5.20 +
5.21 +You should have received a copy of the GNU General Public License along with
5.22 +this program. If not, see <http://www.gnu.org/licenses/>.
5.23 +"""
5.24 +
5.25 +from moinformat.imports import get_extensions, get_mapping, get_modules
5.26 +
5.27 +# Define an attribute mapping names to modules.
5.28 +
5.29 +modules = get_modules(__file__, __name__)
5.30 +
5.31 +# Obtain all translators.
5.32 +
5.33 +# Use module paths to register the handlers:
5.34 +# output_format.input_format -> translator
5.35 +
5.36 +def get_formats(n, m):
5.37 +
5.38 + """
5.39 + Given module name 'n', inspect the translator in module 'm', returning a
5.40 + list of format names.
5.41 + """
5.42 +
5.43 + l = []
5.44 + for output_format in m.translator.formats:
5.45 + for input_format in m.translator.input_formats:
5.46 + l.append("%s.%s" % (output_format, input_format))
5.47 + return l
5.48 +
5.49 +translators = get_mapping(modules, get_formats, lambda m: m.translator)
5.50 +
5.51 +# vim: tabstop=4 expandtab shiftwidth=4
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/moinformat/translators/moin/__init__.py Thu Aug 17 23:34:10 2023 +0200
6.3 @@ -0,0 +1,22 @@
6.4 +#!/usr/bin/env python
6.5 +
6.6 +"""
6.7 +A package of translators producing Moin documents.
6.8 +
6.9 +Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk>
6.10 +
6.11 +This program is free software; you can redistribute it and/or modify it under
6.12 +the terms of the GNU General Public License as published by the Free Software
6.13 +Foundation; either version 3 of the License, or (at your option) any later
6.14 +version.
6.15 +
6.16 +This program is distributed in the hope that it will be useful, but WITHOUT
6.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
6.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
6.19 +details.
6.20 +
6.21 +You should have received a copy of the GNU General Public License along with
6.22 +this program. If not, see <http://www.gnu.org/licenses/>.
6.23 +"""
6.24 +
6.25 +# vim: tabstop=4 expandtab shiftwidth=4
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/moinformat/translators/moin/html.py Thu Aug 17 23:34:10 2023 +0200
7.3 @@ -0,0 +1,99 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +"""
7.7 +HTML-to-Moin translator.
7.8 +
7.9 +Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk>
7.10 +
7.11 +This program is free software; you can redistribute it and/or modify it under
7.12 +the terms of the GNU General Public License as published by the Free Software
7.13 +Foundation; either version 3 of the License, or (at your option) any later
7.14 +version.
7.15 +
7.16 +This program is distributed in the hope that it will be useful, but WITHOUT
7.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
7.19 +details.
7.20 +
7.21 +You should have received a copy of the GNU General Public License along with
7.22 +this program. If not, see <http://www.gnu.org/licenses/>.
7.23 +"""
7.24 +
7.25 +from moinformat.translators.common import Translator
7.26 +from moinformat.tree.moin import Block, Heading, Region, Text
7.27 +
7.28 +def int_or_default(s, default):
7.29 + if not s:
7.30 + return default
7.31 + try:
7.32 + return int(s)
7.33 + except ValueError:
7.34 + return default
7.35 +
7.36 +class HTMLToMoinTranslator(Translator):
7.37 +
7.38 + "Translation of HTML document nodes to Moin document nodes."
7.39 +
7.40 + input_formats = ["html"]
7.41 + formats = ["moin"]
7.42 +
7.43 + def _get_attribute(self, element, name):
7.44 + for attribute in element.attributes:
7.45 + if attribute.name == name:
7.46 + return attribute.value and attribute.value.value
7.47 + return None
7.48 +
7.49 + def _get_class_values(self, element):
7.50 + class_value = self._get_attribute(element, "class")
7.51 + if not class_value:
7.52 + return {}
7.53 +
7.54 + d = {}
7.55 + for token in class_value.split():
7.56 + if token and token.startswith("region-"):
7.57 + _region, name, value = token.split("-", 2)
7.58 + d[name] = value
7.59 + return d
7.60 +
7.61 + def element(self, element):
7.62 + if not element.name:
7.63 + return None
7.64 + elif element.name[0] == "h" and element.name[1:].isdigit():
7.65 + return Heading(self.container(element), int(element.name[1:]),
7.66 + start_pad=" ", end_pad=" ", end_extra="\n",
7.67 + identifier=self._get_attribute(element, "id"))
7.68 + elif element.name == "p":
7.69 + return Block(self.container(element))
7.70 + elif element.name == "span":
7.71 + d = self._get_class_values(element)
7.72 + if d.has_key("type"):
7.73 + return Region(self.container(element),
7.74 + int_or_default(d.get("level"), 0),
7.75 + int_or_default(d.get("indent"), 0),
7.76 + d.get("type"),
7.77 + extra="\n")
7.78 + else:
7.79 + return Block(self.container(element))
7.80 + else:
7.81 + return None
7.82 +
7.83 + def fragment(self, fragment):
7.84 + return self.container(fragment)
7.85 +
7.86 + def text(self, text):
7.87 + return Text(text.value)
7.88 +
7.89 + # Some nodes are not directly translated.
7.90 +
7.91 + def node(self, node):
7.92 + return None
7.93 +
7.94 + attribute = node
7.95 + attribute_value = node
7.96 + comment = node
7.97 + directive = node
7.98 + inclusion = node
7.99 +
7.100 +translator = HTMLToMoinTranslator
7.101 +
7.102 +# vim: tabstop=4 expandtab shiftwidth=4