From 4c72801eb80f7d4acda4272f7e604f7e1cda7ab8 Mon Sep 17 00:00:00 2001 From: milde Date: Wed, 28 Oct 2009 14:08:17 +0000 Subject: [PATCH] (partially) Fix reporting for problems in included files. git-svn-id: https://docutils.svn.sourceforge.net/svnroot/docutils/trunk/docutils@6188 929543f6-e4f2-0310-98a6-ba3bd3dd1d04 --- BUGS.txt | 9 ++-- HISTORY.txt | 4 ++ docutils/parsers/rst/__init__.py | 16 ++---- docutils/parsers/rst/states.py | 25 ++++----- docutils/statemachine.py | 22 +++++--- docutils/utils.py | 5 ++ .../test_rst/test_directives/include10.txt | 3 ++ .../test_rst/test_directives/test_include.py | 59 ++++++++++++++-------- 8 files changed, 88 insertions(+), 55 deletions(-) diff --git a/BUGS.txt b/BUGS.txt index 6cabfa5bb..e4daf4d66 100644 --- a/BUGS.txt +++ b/BUGS.txt @@ -168,11 +168,12 @@ Also see the `SourceForge Bug Tracker`_. reported with the correct "source" or "line" numbers. Perhaps all Reporter calls need "source" and "line" keyword arguments. Elements' .line assignments should be checked. (Assign to .source - too? Add a set_info method? To what?) There's a test in - test/test_parsers/test_rst/test_directives/test_include.py. + too? Add a set_info method? To what?) - This is partially fixed in the commit on 2009-09-25, although the - commented-out test case in the abovementioned file does still fail. + This is partially fixed in the commits from 2009-09-25 and 2009-10-28. + The test in test/test_parsers/test_rst/test_directives/test_include.py + works, but there are still plenty of system messages pointing to the + wrong spot. - Some line numbers in elements are not being set properly (explicitly), just implicitly/automatically. See rev. 1.74 of diff --git a/HISTORY.txt b/HISTORY.txt index 74fe95d66..d512a91e0 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -17,6 +17,10 @@ Changes Since 0.6 ================= +* General: + + - (partially) Fix reporting for problems in included files. + * docutils/io.py - FileInput opens files as text files with universal newline support diff --git a/docutils/parsers/rst/__init__.py b/docutils/parsers/rst/__init__.py index 64ef0b334..1dfa88222 100644 --- a/docutils/parsers/rst/__init__.py +++ b/docutils/parsers/rst/__init__.py @@ -169,15 +169,14 @@ class DirectiveError(Exception): instead! """ - def __init__(self, level, message, source, line): + def __init__(self, level, message, spot): """ - Initialize with message `message`. `level` is a system message level. + Initialize with `message`, system message `level`, and error `spot` """ Exception.__init__(self) self.level = level self.msg = message - self.source = source - self.line = line + self.spot = spot class Directive(object): @@ -315,13 +314,8 @@ class Directive(object): You'd often use self.error(message) instead, which will generate an ERROR-level directive error. """ - # source = self.state_machine.get_source(self.lineno - 1) - try: - (source, line) = self.state_machine.input_lines.info(self.lineno) - except IndexError: - source = self.state_machine.get_source(self.lineno - 1) - line = self.lineno - return DirectiveError(level, message, source, line) + spot = self.state_machine.get_source_spot(self.lineno - 1) + return DirectiveError(level, message, spot) def debug(self, message): return self.directive_error(0, message) diff --git a/docutils/parsers/rst/states.py b/docutils/parsers/rst/states.py index 687eb0baf..bd4ebad52 100644 --- a/docutils/parsers/rst/states.py +++ b/docutils/parsers/rst/states.py @@ -423,9 +423,12 @@ class RSTState(StateWS): return self.inliner.parse(text, lineno, self.memo, self.parent) def unindent_warning(self, node_name): - return self.reporter.warning( - '%s ends without a blank line; unexpected unindent.' % node_name, - line=(self.state_machine.abs_line_number() + 1)) + # the actual problem is one line below the current line + spot = self.state_machine.get_source_spot() + spot['line'] += 1 + return self.reporter.warning('%s ends without a blank line; ' + 'unexpected unindent.' % node_name, + **spot) def build_regexp(definition, compile=1): @@ -1930,6 +1933,7 @@ class Body(RSTState): def substitution_def(self, match): pattern = self.explicit.patterns.substitution lineno = self.state_machine.abs_line_number() + spot = self.state_machine.get_source_spot() block, indent, offset, blank_finish = \ self.state_machine.get_first_known_indented(match.end(), strip_indent=0) @@ -1960,7 +1964,7 @@ class Body(RSTState): if not block: msg = self.reporter.warning( 'Substitution definition "%s" missing contents.' % subname, - nodes.literal_block(blocktext, blocktext), line=lineno) + nodes.literal_block(blocktext, blocktext), **spot) return [msg], blank_finish block[0] = block[0].strip() substitution_node['names'].append( @@ -1981,14 +1985,12 @@ class Body(RSTState): pformat = nodes.literal_block('', node.pformat().rstrip()) msg = self.reporter.error( 'Substitution definition contains illegal element:', - pformat, nodes.literal_block(blocktext, blocktext), - line=lineno) + pformat, nodes.literal_block(blocktext, blocktext), **spot) return [msg], blank_finish if len(substitution_node) == 0: msg = self.reporter.warning( - 'Substitution definition "%s" empty or invalid.' - % subname, - nodes.literal_block(blocktext, blocktext), line=lineno) + 'Substitution definition "%s" empty or invalid.' % subname, + nodes.literal_block(blocktext, blocktext), **spot) return [msg], blank_finish self.document.note_substitution_def( substitution_node, subname, self.parent) @@ -2062,9 +2064,8 @@ class Body(RSTState): result = directive_instance.run() except docutils.parsers.rst.DirectiveError, error: msg_node = self.reporter.system_message(error.level, error.msg, - source=error.source, line=error.line) + **error.spot) msg_node += nodes.literal_block(block_text, block_text) - msg_node['line'] = lineno result = [msg_node] assert isinstance(result, list), \ 'Directive "%s" must return a list of nodes.' % type_name @@ -2761,7 +2762,7 @@ class Text(RSTState): if len(parts) == 1: node_list[-1] += node else: - + node_list[-1] += nodes.Text(parts[0].rstrip()) for part in parts[1:]: classifier_node = nodes.classifier('', part) diff --git a/docutils/statemachine.py b/docutils/statemachine.py index 83eec3689..ce05d3b74 100644 --- a/docutils/statemachine.py +++ b/docutils/statemachine.py @@ -348,6 +348,15 @@ class StateMachine: """Return source of line at absolute line offset `line_offset`.""" return self.input_lines.source(line_offset - self.input_offset) + def get_source_spot(self, line_offset=None): + """Return dict with source position of current or given line""" + if line_offset is None: + line_offset = self.line_offset + else: + line_offset -= self.input_offset + (source, offset) = self.input_lines.info(line_offset) + return {'source': source, 'line': offset + 1} + def abs_line_offset(self): """Return line offset of current line, from beginning of file.""" return self.line_offset + self.input_offset @@ -358,9 +367,9 @@ class StateMachine: def insert_input(self, input_lines, source): self.input_lines.insert(self.line_offset + 1, '', - source='internal padding') + source='internal padding after ' + source) self.input_lines.insert(self.line_offset + 1, '', - source='internal padding') + source='internal padding before '+ source) self.input_lines.insert(self.line_offset + 2, StringList(input_lines, source)) @@ -757,7 +766,7 @@ class StateMachineWS(StateMachine): `StateMachine` subclass specialized for whitespace recognition. There are three methods provided for extracting indented text blocks: - + - `get_indented()`: use when the indent is unknown. - `get_known_indented()`: use when the indent is known for all lines. - `get_first_known_indented()`: use when only the first line's indent is @@ -1046,7 +1055,7 @@ class ViewList: child and parent lists can be broken by calling `disconnect()` on the child list. - Also, ViewList objects keep track of the source & offset of each item. + Also, ViewList objects keep track of the source & offset of each item. This information is accessible via the `source()`, `offset()`, and `info()` methods. """ @@ -1103,9 +1112,8 @@ class ViewList: def __len__(self): return len(self.data) # The __getitem__()/__setitem__() methods check whether the index - # is a slice first, since native list objects start supporting - # them directly in Python 2.3 (no exception is raised when - # indexing a list with a slice object; they just work). + # is a slice first, since indexing a native list with a slice object + # just works. def __getitem__(self, i): if isinstance(i, types.SliceType): diff --git a/docutils/utils.py b/docutils/utils.py index 70c4bfcf4..3208905a2 100644 --- a/docutils/utils.py +++ b/docutils/utils.py @@ -166,9 +166,14 @@ class Reporter: Raise an exception or generate a warning if appropriate. """ attributes = kwargs.copy() + # print "System Message: ", self.levels[level], + # print " source", unicode(attributes.get("source")).encode('utf8'), + # print " line", attributes.get("line") + # print " spot", attributes.get("spot") if 'base_node' in kwargs: source, line = get_source_line(kwargs['base_node']) del attributes['base_node'] + # print " from base_node", source, line if source is not None: attributes.setdefault('source', source) if line is not None: diff --git a/test/test_parsers/test_rst/test_directives/include10.txt b/test/test_parsers/test_rst/test_directives/include10.txt index 8eb5b1720..ebae13a38 100644 --- a/test/test_parsers/test_rst/test_directives/include10.txt +++ b/test/test_parsers/test_rst/test_directives/include10.txt @@ -3,5 +3,8 @@ hi ----- + indent +error + hi ----- diff --git a/test/test_parsers/test_rst/test_directives/test_include.py b/test/test_parsers/test_rst/test_directives/test_include.py index 50ed72cd5..594e6e964 100755 --- a/test/test_parsers/test_rst/test_directives/test_include.py +++ b/test/test_parsers/test_rst/test_directives/test_include.py @@ -324,27 +324,44 @@ Include file is UTF-16-encoded, and is not valid ASCII. :encoding: ascii """ % utf_16_file_rel], # @@@ BUG with errors reported with incorrect "source" & "line": -# ["""\ -# Testing bad charent includes: -# -# .. include:: %s -# """ % include10, -# """\ -# -# -# Testing bad charent includes: -# -# -# Invalid character code: 0xFFFFFFFFF -# int() literal too large: FFFFFFFFF -# -# unicode:: 0xFFFFFFFFF -# -# -# Substitution definition "bad" empty or invalid. -# -# .. |bad| unicode:: 0xFFFFFFFFF -# """ % (include10rel, include10rel)], +["""\ +Testing bad charent includes: + +.. include:: %s +""" % include10, +"""\ + + + Testing bad charent includes: + + + Invalid character code: 0xFFFFFFFFF + ValueError: unichr() arg not in range(0x110000) (wide Python build) + + unicode:: 0xFFFFFFFFF + + + Substitution definition "bad" empty or invalid. + + .. |bad| unicode:: 0xFFFFFFFFF +
+ + hi + <block_quote> + <paragraph> + indent + <system_message level="2" line="7" source="%s" type="WARNING"> + <paragraph> + Block quote ends without a blank line; unexpected unindent. + <paragraph> + error + <section dupnames="hi" ids="id1"> + <title> + hi + <system_message backrefs="id1" level="1" line="10" source="%s" type="INFO"> + <paragraph> + Duplicate implicit target name: "hi". +""" % (include10rel, include10rel, include10rel, include10rel)], ["""\ Include file with whitespace in the path: -- 2.11.4.GIT