# HG changeset patch # User Paul Boddie # Date 1532899643 -7200 # Node ID cd1788f92058413d63c92f8e5cbf37a91b02a7ea # Parent cc8b25a249cb453b53890b45fff7680bf7c94b7e Introduced output contexts for more general handling of serialised content. Simplified the instantiation of new serialisers when encountering different region formats. diff -r cc8b25a249cb -r cd1788f92058 convert.py --- a/convert.py Sun Jul 29 00:19:30 2018 +0200 +++ b/convert.py Sun Jul 29 23:27:23 2018 +0200 @@ -1,6 +1,7 @@ #!/usr/bin/env python -from moinformat import make_linker, make_parser, make_serialiser, parse, serialise +from moinformat import make_linker, make_output, make_parser, make_serialiser, \ + parse, serialise from os.path import split import sys @@ -12,6 +13,7 @@ formats = [] pagenames = [] mappings = [] + outputs = [] tree = False macros = False @@ -39,6 +41,12 @@ l = mappings continue + # Switch to collecting output locations. + + elif arg == "--output": + l = outputs + continue + # Switch to collecting page names. elif arg == "--pagename": @@ -50,6 +58,8 @@ else: l.append(arg) + # Collect multiple mappings. + if l is mappings: continue @@ -64,6 +74,10 @@ filename = filenames[0] pagename = pagenames and pagenames[0] or split(filename)[-1] + # Obtain an output context from any specified output details. + + output = outputs and make_output(outputs[0]) or None + # Derive a proper mapping from the given list of values. mapping = {} @@ -90,7 +104,7 @@ print d.prettyprint() else: l = make_linker(format, pagename, mapping) - s = make_serialiser(format, l) + s = make_serialiser(format, output, l) print serialise(d, s) finally: f.close() diff -r cc8b25a249cb -r cd1788f92058 moinformat/__init__.py --- a/moinformat/__init__.py Sun Jul 29 00:19:30 2018 +0200 +++ b/moinformat/__init__.py Sun Jul 29 23:27:23 2018 +0200 @@ -20,6 +20,7 @@ """ from moinformat.links import make_linker +from moinformat.output import make_output from moinformat.parsers import get_parser, make_parser, parse from moinformat.serialisers import get_serialiser, make_serialiser, serialise diff -r cc8b25a249cb -r cd1788f92058 moinformat/output/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/output/__init__.py Sun Jul 29 23:27:23 2018 +0200 @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Output contexts. + +Copyright (C) 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from moinformat.output.directory import DirectoryOutput + +def make_output(dirname): + + "Return a directory output context employing 'dirname'." + + return DirectoryOutput(dirname) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r cc8b25a249cb -r cd1788f92058 moinformat/output/common.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/output/common.py Sun Jul 29 23:27:23 2018 +0200 @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +Output context common functionality. + +Copyright (C) 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +class Output: + + "A common output context abstraction." + + def __init__(self): + + "Initialise the output context." + + self.output = [] + self.out = self.output.append + + def to_string(self): + + "Return the output as a string." + + return "".join(self.output) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r cc8b25a249cb -r cd1788f92058 moinformat/output/directory.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/output/directory.py Sun Jul 29 23:27:23 2018 +0200 @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +""" +Directory output context. + +Copyright (C) 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from moinformat.output.common import Output +from os.path import abspath, commonprefix, join + +def inside(filename, dirname): + + "Return whether 'filename' is inside 'dirname'." + + # Get the directory with trailing path separator. + + dirname = join(dirname, "") + return commonprefix((filename, dirname)) == dirname + + + +class DirectoryOutput(Output): + + "A directory output context." + + def __init__(self, filename): + + "Initialise the context with the given 'filename'." + + Output.__init__(self) + self.filename = abspath(filename) + + def get_filename(self, filename): + + "Return a file with the given 'filename' within the directory." + + # Get the absolute path for the combination of directory and filename. + + pathname = abspath(join(self.filename, filename)) + + if inside(pathname, self.filename): + return pathname + else: + raise ValueError, filename + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r cc8b25a249cb -r cd1788f92058 moinformat/serialisers/__init__.py --- a/moinformat/serialisers/__init__.py Sun Jul 29 00:19:30 2018 +0200 +++ b/moinformat/serialisers/__init__.py Sun Jul 29 23:27:23 2018 +0200 @@ -20,8 +20,10 @@ """ from moinformat.links import make_linker +from moinformat.output.directory import DirectoryOutput from moinformat.serialisers.manifest import serialisers from moinformat.serialisers.moin.moin import MoinSerialiser +from os.path import curdir # Top-level functions. @@ -31,17 +33,21 @@ return serialisers["%s.moin" % name] -def make_serialiser(name, linker=None): +def make_serialiser(name, output=None, linker=None): """ Return a serialiser instance for the format having the given 'name'. + The optional 'output' context is used to control where separate resources + are stored, with the default being the current directory. + The optional 'linker' is used to control which linking scheme is used with the serialiser, with the default having the same name as the serialiser. """ + output = output or DirectoryOutput(curdir) linker = linker or make_linker(name, "") - return get_serialiser(name)(formats=serialisers, linker=linker) + return get_serialiser(name)(output, serialisers, linker) def serialise(doc, serialiser=None): @@ -50,10 +56,11 @@ if omitted. """ - l = [] - serialiser = serialiser or MoinSerialiser(formats=serialisers) - serialiser.out = l.append + if not serialiser: + output = DirectoryOutput(curdir) + serialiser = MoinSerialiser(output, serialisers) + doc.to_string(serialiser) - return "".join(l) + return serialiser.get_output() # vim: tabstop=4 expandtab shiftwidth=4 diff -r cc8b25a249cb -r cd1788f92058 moinformat/serialisers/common.py --- a/moinformat/serialisers/common.py Sun Jul 29 00:19:30 2018 +0200 +++ b/moinformat/serialisers/common.py Sun Jul 29 23:27:23 2018 +0200 @@ -25,17 +25,24 @@ format = None # defined by subclasses - def __init__(self, out=None, formats=None, linker=None): + def __init__(self, output, formats=None, linker=None): """ - Initialise the serialiser with an 'out' callable, an optional 'formats' - mapping from names to serialiser classes, and an optional 'linker' - object for translating links. + Initialise the serialiser with an 'output' context, an optional + 'formats' mapping from names to serialiser classes, and an optional + 'linker' object for translating links. """ - self.out = out + self.output = output self.formats = formats self.linker = linker + + # Initialise a callable for use in serialisation. + + self.out = output.out + + # Initialisation of any other state. + self.init() def init(self): @@ -45,9 +52,40 @@ pass def __repr__(self): - return "%s(%r, %r, %r)" % (self.__class__.__name__, self.out, + return "%s(%r, %r, %r)" % (self.__class__.__name__, self.output, self.formats, self.linker) + def get_serialiser(self, format): + + """ + Return a serialiser for the given 'format'. Return self if no suitable + serialiser can be obtained. + """ + + cls = self.formats and self.formats.get(format) + if cls: + return self.instantiate(cls) + else: + return self + + def get_output(self): + + "Return the output as a string." + + return self.output.to_string() + + def instantiate(self, cls): + + """ + Instantiate 'cls' and return the result if 'cls' is a different class to + this instance. Otherwise, return this instance. + """ + + if cls is self.__class__: + return self + else: + return cls(self.output, self.formats, self.linker) + def escape_attr(s): "Escape XML document attribute." diff -r cc8b25a249cb -r cd1788f92058 moinformat/tree/moin.py --- a/moinformat/tree/moin.py Sun Jul 29 00:19:30 2018 +0200 +++ b/moinformat/tree/moin.py Sun Jul 29 23:27:23 2018 +0200 @@ -138,21 +138,18 @@ def to_string(self, out): out.start_region(self.level, self.indent, self.type, self.extra) - # Obtain a serialiser class for the region from the same format family. - - serialiser_name = "%s.%s" % (out.format, self.type) - serialiser_cls = out.formats and out.formats.get(serialiser_name) - + # Obtain a serialiser for the region from the same format family. # Retain the same serialiser if no appropriate serialiser could be # obtained. - region_out = serialiser_cls and serialiser_cls is not out and \ - serialiser_cls(out.out, out.formats, out.linker) or \ - out + serialiser_name = "%s.%s" % (out.format, self.type) + serialiser = out.get_serialiser(serialiser_name) # Serialise the region. - self._to_string(region_out) + self._to_string(serialiser) + + # End the region with the previous serialiser. out.end_region(self.level, self.indent, self.type, self.extra)