# HG changeset patch # User Paul Boddie # Date 1532963932 -7200 # Node ID 8d437cdf23810bd673f6b5caeccda0996a187456 # Parent 375cfd1f31618c42d4394e19446491a9cf24940c Introduced Unicode strings as the typical form of document data for processing, employing output encodings when serialising output documents. Introduced input contexts which provide details of input document encodings and document source locations. Introduced the familiar manifest and lookup mechanisms for input and output contexts. Provided support for standalone contexts, with the output context suppressing the generation of additional files. Moved common directory functionality to a separate utility module. Updated the conversion script and test framework, adding encoding-related tests. diff -r 375cfd1f3161 -r 8d437cdf2381 convert.py --- a/convert.py Mon Jul 30 01:03:50 2018 +0200 +++ b/convert.py Mon Jul 30 17:18:52 2018 +0200 @@ -1,7 +1,7 @@ #!/usr/bin/env python -from moinformat import make_linker, make_output, make_parser, make_serialiser, \ - parse, serialise +from moinformat import make_input, make_linker, make_output, make_parser, \ + make_serialiser, parse, serialise from os.path import split import sys @@ -9,13 +9,20 @@ dirname, progname = split(sys.argv[0]) args = sys.argv[1:] + # Option values. + l = filenames = [] formats = [] - pagenames = [] + input_encodings = [] mappings = [] - outputs = [] + output_dirs = [] + output_encodings = [] + pagenames = [] + + # Flags. + + macros = False tree = False - macros = False for arg in args: @@ -35,6 +42,12 @@ l = formats continue + # Switch to collecting input encodings. + + elif arg == "--input-encoding": + l = input_encodings + continue + # Switch to collecting mappings. elif arg == "--mapping": @@ -43,8 +56,14 @@ # Switch to collecting output locations. - elif arg == "--output": - l = outputs + elif arg == "--output-dir": + l = output_dirs + continue + + # Switch to collecting output encodings. + + elif arg == "--output-encoding": + l = output_encodings continue # Switch to collecting page names. @@ -74,10 +93,6 @@ 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,24 +105,48 @@ mapping[key] = arg key = None + # Obtain output location. + + output_dir = output_dirs and output_dirs[0] or None + + # Obtain encodings. + + input_encoding = input_encodings and input_encodings[0] or None + output_encoding = output_encodings and output_encodings[0] or None + # Open the file, parse the content, serialise the document. - f = open(filename) - try: - p = make_parser() - d = parse(f.read(), p) + input = make_input("standalone", {"encoding" : input_encoding}) + + p = make_parser() + d = parse(input.readfile(filename), p) - if macros: - p.evaluate_macros() + if macros: + p.evaluate_macros() + + # Show a document tree for debugging purposes, if requested. + + if tree: + print d.prettyprint() + + # Otherwise, serialise the document. - if tree: - print d.prettyprint() - else: - l = make_linker(format, pagename, mapping) - s = make_serialiser(format, output, l) - print serialise(d, s) - finally: - f.close() + else: + # Obtain an output context from any specified output details. + + output_context = output_dir and "directory" or "standalone" + + output = make_output(output_context, {"encoding" : output_encoding, + "filename" : output_dir}) + + # Obtain a linker using format and pagename details. + + linker = make_linker(format, pagename, mapping) + + # Obtain a serialiser using the configuration. + + serialiser = make_serialiser(format, output, linker) + print serialise(d, serialiser) if __name__ == "__main__": main() diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/__init__.py --- a/moinformat/__init__.py Mon Jul 30 01:03:50 2018 +0200 +++ b/moinformat/__init__.py Mon Jul 30 17:18:52 2018 +0200 @@ -19,6 +19,7 @@ this program. If not, see . """ +from moinformat.input import make_input from moinformat.links import make_linker from moinformat.output import make_output from moinformat.parsers import get_parser, make_parser, parse diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/input/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/input/__init__.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +""" +Input 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.input.manifest import inputs + +# Top-level functions. + +def get_input(name): + + """ + Return the input context class with the given 'name' or None if no such + class is found. + """ + + return inputs.get(name) + +def make_input(name, parameters): + + """ + Return an input context of the type indicated by 'name', employing the + given 'parameters'. + """ + + input_cls = get_input(name) + if not input_cls: + return None + + return input_cls(parameters) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/input/common.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/input/common.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +""" +Input 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 . +""" + +import codecs + +class Input: + + "A common input context abstraction." + + default_encoding = "utf-8" + + def __init__(self, parameters=None): + + "Initialise the input context with the optional 'parameters'." + + self.parameters = parameters + self.encoding = parameters and parameters.get("encoding") or self.default_encoding + + def readfile(self, filename, encoding=None): + + """ + Return the contents of the file having the given 'filename'. If the + optional 'encoding' is specified, override the general encoding. + """ + + f = codecs.open(filename, encoding=encoding or self.encoding) + try: + return f.read() + finally: + f.close() + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/input/directory.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/input/directory.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +""" +Directory input 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.input.common import Input +from moinformat.utils.directory import Directory + +class DirectoryInput(Input, Directory): + + "A directory output context." + + name = "directory" + + def __init__(self, parameters=None): + + "Initialise the context with the given 'parameters'." + + if not parameters or not parameters.has_key("filename"): + raise ValueError, parameters + + Input.__init__(self, parameters) + Directory.__init__(self, parameters["filename"]) + + def readfile(self, filename, encoding=None): + + """ + Return the contents of the file having the given 'filename' and optional + 'encoding'. + """ + + return Input.readfile(self, self.get_filename(filename), encoding) + +input = DirectoryInput + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/input/manifest.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/input/manifest.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +""" +Input context implementation manifest. + +Copyright (C) 2017, 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.imports import get_extensions +from os.path import split + +reserved = ["__init__", "common", "manifest"] + +# Obtain details of this module's package. + +dirname = split(__file__)[0] +package = __name__.rsplit(".", 1)[0] + +# Define an attribute mapping names to modules. + +modules = {} +get_extensions(dirname, package, modules, reserved) + +# Obtain all input contexts. + +inputs = {} + +# Use names declared in each class to register the handlers: +# input.name -> input + +for module in modules.values(): + inputs[module.input.name] = module.input + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/input/standalone.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/input/standalone.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +""" +Stand-alone input 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.input.common import Input +import codecs +import sys + +class StandaloneInput(Input): + + "A stand-alone output context." + + name = "standalone" + + def read(self, stream=None): + + """ + Read from 'stream', or standard input if not specified, returning a + character string. + """ + + stream = stream or sys.stdin + f = codecs.getreader(self.encoding)(stream) + return f.read() + + def readfile(self, filename, encoding=None): + + """ + Return the contents of the file having the given 'filename'. If the + optional 'encoding' is specified, override the general encoding. + """ + + return Input.readfile(self, filename, encoding) + +input = StandaloneInput + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/output/__init__.py --- a/moinformat/output/__init__.py Mon Jul 30 01:03:50 2018 +0200 +++ b/moinformat/output/__init__.py Mon Jul 30 17:18:52 2018 +0200 @@ -19,12 +19,30 @@ this program. If not, see . """ -from moinformat.output.directory import DirectoryOutput +from moinformat.output.manifest import outputs + +# Top-level functions. + +def get_output(name): -def make_output(dirname): + """ + Return the output context class with the given 'name' or None if no such + class is found. + """ + + return outputs.get(name) - "Return a directory output context employing 'dirname'." +def make_output(name, parameters=None): - return DirectoryOutput(dirname) + """ + Return an output context of the type indicated by 'name', employing the + given 'parameters'. + """ + + output_cls = get_output(name) + if not output_cls: + return None + + return output_cls(parameters) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/output/common.py --- a/moinformat/output/common.py Mon Jul 30 01:03:50 2018 +0200 +++ b/moinformat/output/common.py Mon Jul 30 17:18:52 2018 +0200 @@ -23,17 +23,44 @@ "A common output context abstraction." - def __init__(self): + default_encoding = "utf-8" + + def __init__(self, parameters=None): - "Initialise the output context." + "Initialise the output context with the optional 'parameters'." + + self.parameters = parameters + self.encoding = parameters and parameters.get("encoding") or self.default_encoding + + # Set up an output collector. self.output = [] - self.out = self.output.append + + def encode(self, text): + + "Encode 'text' using the configured encoding." + + return encode(text, self.encoding) + + def out(self, text): + + "Add 'text' to the output collector." + + self.output.append(self.encode(text)) def to_string(self): - "Return the output as a string." + "Return the output as a plain string." return "".join(self.output) +def encode(s, encoding): + + "Encode 's' using 'encoding' if Unicode." + + if isinstance(s, unicode): + return s.encode(encoding) + else: + return s + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/output/directory.py --- a/moinformat/output/directory.py Mon Jul 30 01:03:50 2018 +0200 +++ b/moinformat/output/directory.py Mon Jul 30 17:18:52 2018 +0200 @@ -20,41 +20,24 @@ """ from moinformat.output.common import Output -from os.path import abspath, commonprefix, join - -def inside(filename, dirname): - - "Return whether 'filename' is inside 'dirname'." +from moinformat.utils.directory import Directory - # Get the directory with trailing path separator. - - dirname = join(dirname, "") - return commonprefix((filename, dirname)) == dirname - - - -class DirectoryOutput(Output): +class DirectoryOutput(Output, Directory): "A directory output context." - def __init__(self, filename): - - "Initialise the context with the given 'filename'." + name = "directory" - Output.__init__(self) - self.filename = abspath(filename) + def __init__(self, parameters=None): - def get_filename(self, filename): + "Initialise the context with the given 'parameters'." - "Return a file with the given 'filename' within the directory." - - # Get the absolute path for the combination of directory and filename. + if not parameters or not parameters.has_key("filename"): + raise ValueError, parameters - pathname = abspath(join(self.filename, filename)) + Output.__init__(self, parameters) + Directory.__init__(self, parameters["filename"]) - if inside(pathname, self.filename): - return pathname - else: - raise ValueError, filename +output = DirectoryOutput # vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/output/manifest.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/output/manifest.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +""" +Output context implementation manifest. + +Copyright (C) 2017, 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.imports import get_extensions +from os.path import split + +reserved = ["__init__", "common", "manifest"] + +# Obtain details of this module's package. + +dirname = split(__file__)[0] +package = __name__.rsplit(".", 1)[0] + +# Define an attribute mapping names to modules. + +modules = {} +get_extensions(dirname, package, modules, reserved) + +# Obtain all output contexts. + +outputs = {} + +# Use names declared in each class to register the handlers: +# output.name -> output + +for module in modules.values(): + outputs[module.output.name] = module.output + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/output/standalone.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/output/standalone.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +""" +Stand-alone 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 + +class StandaloneOutput(Output): + + "A stand-alone output context." + + name = "standalone" + + def get_filename(self, filename): + + """ + Prevent independent output by returning a filename of None corresponding + to any specified 'filename'. + """ + + return None + +output = StandaloneOutput + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/serialisers/__init__.py --- a/moinformat/serialisers/__init__.py Mon Jul 30 01:03:50 2018 +0200 +++ b/moinformat/serialisers/__init__.py Mon Jul 30 17:18:52 2018 +0200 @@ -20,7 +20,7 @@ """ from moinformat.links import make_linker -from moinformat.output.directory import DirectoryOutput +from moinformat.output import make_output from moinformat.serialisers.manifest import serialisers from moinformat.serialisers.moin.moin import MoinSerialiser from os.path import curdir @@ -39,13 +39,13 @@ 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. + are stored, with the default being no storage of such resources. 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) + output = output or make_output("standalone") linker = linker or make_linker(name, "") return get_serialiser(name)(output, serialisers, linker) @@ -57,7 +57,7 @@ """ if not serialiser: - output = DirectoryOutput(curdir) + output = make_output("standalone") serialiser = MoinSerialiser(output, serialisers) doc.to_string(serialiser) diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/serialisers/html/graphviz.py --- a/moinformat/serialisers/html/graphviz.py Mon Jul 30 01:03:50 2018 +0200 +++ b/moinformat/serialisers/html/graphviz.py Mon Jul 30 17:18:52 2018 +0200 @@ -102,6 +102,11 @@ identifier = get_output_identifier(text) filename = self.output.get_filename(identifier) + # Handle situations where no independent output is permitted. + + if not filename: + return + # Permit imagemaps only for image formats. if format in IMAGE_FORMATS: diff -r 375cfd1f3161 -r 8d437cdf2381 moinformat/utils/directory.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/utils/directory.py Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,102 @@ +#!/usr/bin/env python + +""" +Directory context 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 . +""" + +from glob import glob +from os.path import abspath, commonprefix, exists, join + +# Get the directory with trailing path separator when assessing path prefixes +# in order to prevent sibling directory confusion. + +def inside(filename, dirname): + + "Return whether 'filename' is inside 'dirname'." + + dirname = join(dirname, "") + return commonprefix((filename, dirname)) == dirname + +def within(filename, dirname): + + "Return the part of 'filename' found within 'dirname'." + + dirname = join(dirname, "") + prefix = commonprefix((filename, dirname)) + + if prefix == dirname: + return filename[len(prefix):] + else: + return None + + + +class Directory: + + "A directory abstraction." + + def __init__(self, filename): + + "Initialise the abstraction with the given 'filename'." + + self.filename = abspath(filename) + + def exists(self, filename): + + """ + Return whether 'filename' exists within the directory. This filename + is relative to the directory. + """ + + return exists(self.get_filename(filename)) + + def get_filename(self, filename): + + """ + Return the full path of a file with the given 'filename' found within + the directory. The full path is an absolute path. + """ + + # 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 + + def select_files(self, pattern): + + """ + Return a list of filenames found within the directory matching + 'pattern'. These filenames are relative to the directory. + """ + + full_pattern = self.get_filename(pattern) + + filenames = [] + + for filename in glob(full_pattern): + filename = within(filename, self.filename) + if filename: + filenames.append(filename) + + return filenames + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 375cfd1f3161 -r 8d437cdf2381 tests/test_encoding.tree --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_encoding.tree Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,9 @@ +Region + Block + Text + Break + Block + Text + Larger + Text + Text diff -r 375cfd1f3161 -r 8d437cdf2381 tests/test_encoding.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_encoding.txt Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,3 @@ +This file uses the UTF-8 encoding. + +Norske bokstaver: æøå, ~+større+~, og sÃ¥ videre. diff -r 375cfd1f3161 -r 8d437cdf2381 tests/test_encoding.txt.iso-8859-1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_encoding.txt.iso-8859-1 Mon Jul 30 17:18:52 2018 +0200 @@ -0,0 +1,3 @@ +This file uses the ISO-8859-1 encoding. + +Norske bokstaver: æøå, ~+større+~, og så videre. diff -r 375cfd1f3161 -r 8d437cdf2381 tests/test_parser.py --- a/tests/test_parser.py Mon Jul 30 01:03:50 2018 +0200 +++ b/tests/test_parser.py Mon Jul 30 17:18:52 2018 +0200 @@ -1,8 +1,10 @@ #!/usr/bin/env python -from os.path import abspath, exists, join, split +from os.path import abspath, split import sys +# Locate and import the moinformat package. + dirname = split(abspath(sys.argv[0]))[0] parent = split(dirname)[0] @@ -12,17 +14,20 @@ if split(parent)[1] == "MoinLight": sys.path.append(parent) -from moinformat import make_serialiser, parse, serialise +# Import specific objects. + +from moinformat import make_input, make_output, make_serialiser, parse, serialise from moinformat.tree.moin import Container -from glob import glob def test_input(d, s): "Compare serialised output from 'd' with its original form 's'." - o = serialise(d) + output = make_output("standalone") + expected = output.encode(s) - identical = o == s + result = serialise(d, make_serialiser("moin", output)) + identical = result == expected if quiet: return identical @@ -31,12 +36,12 @@ print identical print "-" * 60 - print o + print result if not identical: print "-" * 60 - print s + print expected print "-" * 60 - print serialise(d, make_serialiser("html")) + print serialise(d, make_serialiser("html", output)) print "-" * 60 print @@ -161,16 +166,6 @@ return branches[0] -def readfile(filename): - - "Read the contents of 'filename' and return them." - - f = open(filename) - try: - return f.read() - finally: - f.close() - if __name__ == "__main__": args = sys.argv[1:] @@ -178,21 +173,46 @@ if quiet: del args[args.index("-q")] - filenames = args or glob(join(dirname, "test*.txt")) + # Make an input context. + + input = make_input("directory", {"filename" : dirname}) + + # Obtain input filenames. + + filenames = args or input.select_files("test*.txt*") filenames.sort() + # Process each filename, obtaining a corresponding tree definition. + for filename in filenames: - tree_filename = "%s.tree" % filename.rsplit(".", 1)[0] + + # Test for an explicit encoding suffix. - s = readfile(filename) + t = filename.split(".") + if len(t) > 2: + text_filename = ".".join(t[:2]) + encoding = t[2] + else: + text_filename = filename + encoding = None + + tree_filename = "%s.tree" % text_filename.rsplit(".", 1)[0] + + # Read and parse the input. + + s = input.readfile(text_filename, encoding) d = parse(s) - if exists(tree_filename): - ts = readfile(tree_filename) + # Read and parse any tree definition. + + if input.exists(tree_filename): + ts = input.readfile(tree_filename) t = parse_tree(ts) else: ts = None + # Report the test results. + if not quiet: print filename