You can subscribe to this list here.
2002 |
Jan
|
Feb
|
Mar
|
Apr
(106) |
May
(215) |
Jun
(104) |
Jul
(290) |
Aug
(351) |
Sep
(245) |
Oct
(289) |
Nov
(184) |
Dec
(113) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2003 |
Jan
(179) |
Feb
(88) |
Mar
(77) |
Apr
(70) |
May
(107) |
Jun
(288) |
Jul
(115) |
Aug
(67) |
Sep
(91) |
Oct
(34) |
Nov
(31) |
Dec
(61) |
2004 |
Jan
(54) |
Feb
(17) |
Mar
(102) |
Apr
(152) |
May
(178) |
Jun
(377) |
Jul
(136) |
Aug
(37) |
Sep
(196) |
Oct
(142) |
Nov
(119) |
Dec
(58) |
2005 |
Jan
(51) |
Feb
(76) |
Mar
(220) |
Apr
(132) |
May
(134) |
Jun
(230) |
Jul
(142) |
Aug
(58) |
Sep
(71) |
Oct
(76) |
Nov
(129) |
Dec
(117) |
2006 |
Jan
(94) |
Feb
(30) |
Mar
(97) |
Apr
(63) |
May
(63) |
Jun
(62) |
Jul
(23) |
Aug
(40) |
Sep
(47) |
Oct
(40) |
Nov
(23) |
Dec
(21) |
2007 |
Jan
(57) |
Feb
(65) |
Mar
(77) |
Apr
(23) |
May
(118) |
Jun
(127) |
Jul
(87) |
Aug
(33) |
Sep
(26) |
Oct
(8) |
Nov
(4) |
Dec
(25) |
2008 |
Jan
(16) |
Feb
(18) |
Mar
(16) |
Apr
(4) |
May
(22) |
Jun
(20) |
Jul
(38) |
Aug
(14) |
Sep
(18) |
Oct
(68) |
Nov
(16) |
Dec
(95) |
2009 |
Jan
(28) |
Feb
(16) |
Mar
(8) |
Apr
(44) |
May
(35) |
Jun
(41) |
Jul
(63) |
Aug
(40) |
Sep
(38) |
Oct
(41) |
Nov
(17) |
Dec
(9) |
2010 |
Jan
(9) |
Feb
(3) |
Mar
(71) |
Apr
(20) |
May
(15) |
Jun
(16) |
Jul
(33) |
Aug
(13) |
Sep
(39) |
Oct
(30) |
Nov
(25) |
Dec
(20) |
2011 |
Jan
(213) |
Feb
(252) |
Mar
(24) |
Apr
(24) |
May
(20) |
Jun
(21) |
Jul
(37) |
Aug
(18) |
Sep
(28) |
Oct
(65) |
Nov
(22) |
Dec
(48) |
2012 |
Jan
(35) |
Feb
(39) |
Mar
(17) |
Apr
(9) |
May
(37) |
Jun
(31) |
Jul
(23) |
Aug
(14) |
Sep
(16) |
Oct
(15) |
Nov
(5) |
Dec
(43) |
2013 |
Jan
(15) |
Feb
(19) |
Mar
(26) |
Apr
(13) |
May
(9) |
Jun
(11) |
Jul
(32) |
Aug
(9) |
Sep
(6) |
Oct
|
Nov
(13) |
Dec
(5) |
2014 |
Jan
(2) |
Feb
(3) |
Mar
(1) |
Apr
|
May
(2) |
Jun
(4) |
Jul
(18) |
Aug
|
Sep
|
Oct
(3) |
Nov
(4) |
Dec
(2) |
2015 |
Jan
(3) |
Feb
(25) |
Mar
(49) |
Apr
(28) |
May
(13) |
Jun
(2) |
Jul
(2) |
Aug
(14) |
Sep
(9) |
Oct
(6) |
Nov
|
Dec
(2) |
2016 |
Jan
(2) |
Feb
(1) |
Mar
|
Apr
|
May
(12) |
Jun
|
Jul
(17) |
Aug
(7) |
Sep
(3) |
Oct
(2) |
Nov
(5) |
Dec
(28) |
2017 |
Jan
(11) |
Feb
(6) |
Mar
(10) |
Apr
(10) |
May
(34) |
Jun
(32) |
Jul
(15) |
Aug
(28) |
Sep
(8) |
Oct
(10) |
Nov
(14) |
Dec
(2) |
2018 |
Jan
(8) |
Feb
|
Mar
|
Apr
|
May
|
Jun
(5) |
Jul
(7) |
Aug
|
Sep
(1) |
Oct
|
Nov
(15) |
Dec
|
2019 |
Jan
|
Feb
(7) |
Mar
(2) |
Apr
(2) |
May
(2) |
Jun
(2) |
Jul
(48) |
Aug
(73) |
Sep
(22) |
Oct
(8) |
Nov
(16) |
Dec
(26) |
2020 |
Jan
(30) |
Feb
(13) |
Mar
(15) |
Apr
(6) |
May
(1) |
Jun
(3) |
Jul
(12) |
Aug
(18) |
Sep
(18) |
Oct
(5) |
Nov
(9) |
Dec
(16) |
2021 |
Jan
(13) |
Feb
(17) |
Mar
(19) |
Apr
(70) |
May
(43) |
Jun
(27) |
Jul
(18) |
Aug
(15) |
Sep
(16) |
Oct
(37) |
Nov
(38) |
Dec
(11) |
2022 |
Jan
(73) |
Feb
(18) |
Mar
(36) |
Apr
(6) |
May
(8) |
Jun
(33) |
Jul
(22) |
Aug
|
Sep
(6) |
Oct
(71) |
Nov
(91) |
Dec
(26) |
2023 |
Jan
(12) |
Feb
(5) |
Mar
(5) |
Apr
(34) |
May
(29) |
Jun
(27) |
Jul
(3) |
Aug
(17) |
Sep
(11) |
Oct
(4) |
Nov
(34) |
Dec
(7) |
2024 |
Jan
(16) |
Feb
(27) |
Mar
(60) |
Apr
(57) |
May
(55) |
Jun
(50) |
Jul
(36) |
Aug
(108) |
Sep
(27) |
Oct
(33) |
Nov
(15) |
Dec
(14) |
2025 |
Jan
(2) |
Feb
(7) |
Mar
(49) |
Apr
(51) |
May
(35) |
Jun
(34) |
Jul
(10) |
Aug
(32) |
Sep
|
Oct
|
Nov
|
Dec
|
From: <mi...@us...> - 2025-08-28 22:07:51
|
Revision: 10225 http://sourceforge.net/p/docutils/code/10225 Author: milde Date: 2025-08-28 22:07:49 +0000 (Thu, 28 Aug 2025) Log Message: ----------- Deprecate the "match_titles" argument of `states.RSTState.nested_list_parse()`. `nested_list_parse()` is intended for second and subsequent items of lists and list-like constructs, it does never match section titles. The argument value was (mis)used in directive classes to check wheter a `<topic>` or `<sidebar>` element can be returned. This check is now replaced by testing for a valid parent node. Modified Paths: -------------- trunk/docutils/RELEASE-NOTES.rst trunk/docutils/docutils/parsers/rst/directives/body.py trunk/docutils/docutils/parsers/rst/directives/parts.py trunk/docutils/docutils/parsers/rst/states.py Modified: trunk/docutils/RELEASE-NOTES.rst =================================================================== --- trunk/docutils/RELEASE-NOTES.rst 2025-08-28 22:07:36 UTC (rev 10224) +++ trunk/docutils/RELEASE-NOTES.rst 2025-08-28 22:07:49 UTC (rev 10225) @@ -217,6 +217,10 @@ * Remove `parsers.rst.states.Struct` (obsoleted by `types.SimpleNamespace`) in Docutils 2.0. +* Ignore the "match_titles" argument of + `parsers.rst.states.RSTState.nested_list_parse()` in Docutils 1.0; + remove it in Docutils 2.0. + * Remove `frontend.OptionParser`, `frontend.Option`, `frontend.Values`, `frontend.store_multiple()`, and `frontend.read_config_file()` when migrating to argparse_ in Docutils 2.0 or later. Modified: trunk/docutils/docutils/parsers/rst/directives/body.py =================================================================== --- trunk/docutils/docutils/parsers/rst/directives/body.py 2025-08-28 22:07:36 UTC (rev 10224) +++ trunk/docutils/docutils/parsers/rst/directives/body.py 2025-08-28 22:07:49 UTC (rev 10225) @@ -19,9 +19,8 @@ class BasePseudoSection(Directive): + """Base class for Topic and Sidebar.""" - required_arguments = 1 - optional_arguments = 0 final_argument_whitespace = True option_spec = {'class': directives.class_option, 'name': directives.unchanged} @@ -31,8 +30,8 @@ """Node class to be used (must be set in subclasses).""" def run(self): - if not (self.state_machine.match_titles - or isinstance(self.state_machine.node, nodes.sidebar)): + if not isinstance(self.state_machine.node, + (nodes.document, nodes.section, nodes.sidebar)): raise self.error('The "%s" directive may not be used within ' 'topics or body elements.' % self.name) self.assert_has_content() @@ -64,15 +63,14 @@ class Topic(BasePseudoSection): + required_arguments = 1 node_class = nodes.topic class Sidebar(BasePseudoSection): + optional_arguments = 1 node_class = nodes.sidebar - - required_arguments = 0 - optional_arguments = 1 option_spec = BasePseudoSection.option_spec | { 'subtitle': directives.unchanged_required} Modified: trunk/docutils/docutils/parsers/rst/directives/parts.py =================================================================== --- trunk/docutils/docutils/parsers/rst/directives/parts.py 2025-08-28 22:07:36 UTC (rev 10224) +++ trunk/docutils/docutils/parsers/rst/directives/parts.py 2025-08-28 22:07:49 UTC (rev 10225) @@ -43,8 +43,8 @@ 'class': directives.class_option} def run(self): - if not (self.state_machine.match_titles - or isinstance(self.state_machine.node, nodes.sidebar)): + if not isinstance(self.state_machine.node, + (nodes.document, nodes.section, nodes.sidebar)): raise self.error('The "%s" directive may not be used within ' 'topics or body elements.' % self.name) document = self.state_machine.document Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-28 22:07:36 UTC (rev 10224) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-28 22:07:49 UTC (rev 10225) @@ -108,6 +108,7 @@ import re from types import FunctionType, MethodType from types import SimpleNamespace as Struct +import warnings from docutils import nodes, statemachine, utils from docutils import ApplicationError, DataError @@ -341,7 +342,7 @@ blank_finish, blank_finish_state=None, extra_settings={}, - match_titles=False, + match_titles=False, # deprecated, will be removed state_machine_class=None, state_machine_kwargs=None): """ @@ -355,6 +356,12 @@ Return new offset and a boolean indicating whether there was a blank final line. """ + if match_titles: + warnings.warn('The "match_titles" argument of ' + 'parsers.rst.states.RSTState.nested_list_parse() ' + 'will be ignored in Docutils 1.0 ' + 'and removed in Docutils 2.0.', + PendingDeprecationWarning, stacklevel=2) if state_machine_class is None: state_machine_class = self.nested_sm if state_machine_kwargs is None: @@ -2435,8 +2442,7 @@ self.state_machine.input_lines[offset:], input_offset=self.state_machine.abs_line_offset() + 1, node=self.parent, initial_state='Explicit', - blank_finish=blank_finish, - match_titles=self.state_machine.match_titles) + blank_finish=blank_finish) self.goto_line(newline_offset) if not blank_finish: self.parent += self.unindent_warning('Explicit markup') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-28 22:07:39
|
Revision: 10224 http://sourceforge.net/p/docutils/code/10224 Author: milde Date: 2025-08-28 22:07:36 +0000 (Thu, 28 Aug 2025) Log Message: ----------- More distict variable name in parsers.rst.states.RSTState.nested_list_parse(). The `RSTState.nested_parse()` method creates/uses a new state machine that differs from the class instance's `self.state_machine` (which may be the master state machine or another nested state machine). Rename the local variable holding the new machine from `state_machine` to `my_state_machine`. Modified Paths: -------------- trunk/docutils/docutils/parsers/rst/states.py Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-28 16:15:22 UTC (rev 10223) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-28 22:07:36 UTC (rev 10224) @@ -309,27 +309,28 @@ if state_machine_kwargs is None: state_machine_kwargs = self.nested_sm_kwargs use_default += 1 - state_machine = None + my_state_machine = None if use_default == 2: try: - state_machine = self.nested_sm_cache.pop() + # get cached state machine, prevent others from using it + my_state_machine = self.nested_sm_cache.pop() except IndexError: pass - if not state_machine: - state_machine = state_machine_class( - debug=self.debug, - parent_state_machine=self.state_machine, - **state_machine_kwargs) - # run the statemachine and populate `node`: + if not my_state_machine: + my_state_machine = state_machine_class( + debug=self.debug, + parent_state_machine=self.state_machine, + **state_machine_kwargs) + # run the state machine and populate `node`: block_length = len(block) - state_machine.run(block, input_offset, memo=self.memo, - node=node, match_titles=match_titles) + my_state_machine.run(block, input_offset, memo=self.memo, + node=node, match_titles=match_titles) # clean up + new_offset = my_state_machine.abs_line_offset() if use_default == 2: - self.nested_sm_cache.append(state_machine) + self.nested_sm_cache.append(my_state_machine) else: - state_machine.unlink() - new_offset = state_machine.abs_line_offset() + my_state_machine.unlink() # No `block.parent` implies disconnected -- lines aren't in sync: if block.parent and (len(block) - block_length) != 0: # Adjustment for block if modified in nested parse: @@ -359,20 +360,20 @@ if state_machine_kwargs is None: state_machine_kwargs = self.nested_sm_kwargs.copy() state_machine_kwargs['initial_state'] = initial_state - state_machine = state_machine_class( - debug=self.debug, - parent_state_machine=self.state_machine, - **state_machine_kwargs) + my_state_machine = state_machine_class( + debug=self.debug, + parent_state_machine=self.state_machine, + **state_machine_kwargs) if blank_finish_state is None: blank_finish_state = initial_state - state_machine.states[blank_finish_state].blank_finish = blank_finish + my_state_machine.states[blank_finish_state].blank_finish = blank_finish for key, value in extra_settings.items(): - setattr(state_machine.states[initial_state], key, value) - state_machine.run(block, input_offset, memo=self.memo, - node=node, match_titles=match_titles) - blank_finish = state_machine.states[blank_finish_state].blank_finish - state_machine.unlink() - return state_machine.abs_line_offset(), blank_finish + setattr(my_state_machine.states[initial_state], key, value) + my_state_machine.run(block, input_offset, memo=self.memo, + node=node, match_titles=match_titles) + blank_finish = my_state_machine.states[blank_finish_state].blank_finish + my_state_machine.unlink() + return my_state_machine.abs_line_offset(), blank_finish def section(self, title, source, style, lineno, messages) -> None: """Check for a valid subsection and create one if it checks out.""" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-28 16:15:24
|
Revision: 10223 http://sourceforge.net/p/docutils/code/10223 Author: milde Date: 2025-08-28 16:15:22 +0000 (Thu, 28 Aug 2025) Log Message: ----------- New attribute to store the parent state machine of nested state machines. Allows, e.g., passing an updated "current node" to the parent state machine(s) to fix issues with nested parse with section support (cf. [bugs:#511]). Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/parsers/rst/states.py trunk/docutils/docutils/statemachine.py trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-26 08:06:11 UTC (rev 10222) +++ trunk/docutils/HISTORY.rst 2025-08-28 16:15:22 UTC (rev 10223) @@ -32,11 +32,19 @@ - Ensure new "current node" is valid when switching section level (cf. bugs #508 and #509). - Use a `separate title style hierarchy for nested parsing`__. + - Set `parent_state_machine` attribute when creating nested state machines. + Allows passing an updated "current node" to the parent state machine, + e.g. for changing the section level in a directive. - Better error messages for grid table markup errors (bug #504), based on patch #214 by Jynn Nelson. __ RELEASE-NOTES.html#nested-parsing +* docutils/statemachine.py + + - New attribute `StateMachine.parent_state_machine` to store the + parent state machine of nested state machines. + * docutils/transforms/references.py - Better error reports for hyperlinks with embedded URI or alias. Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-26 08:06:11 UTC (rev 10222) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-28 16:15:22 UTC (rev 10223) @@ -316,8 +316,10 @@ except IndexError: pass if not state_machine: - state_machine = state_machine_class(debug=self.debug, - **state_machine_kwargs) + state_machine = state_machine_class( + debug=self.debug, + parent_state_machine=self.state_machine, + **state_machine_kwargs) # run the statemachine and populate `node`: block_length = len(block) state_machine.run(block, input_offset, memo=self.memo, @@ -357,8 +359,10 @@ if state_machine_kwargs is None: state_machine_kwargs = self.nested_sm_kwargs.copy() state_machine_kwargs['initial_state'] = initial_state - state_machine = state_machine_class(debug=self.debug, - **state_machine_kwargs) + state_machine = state_machine_class( + debug=self.debug, + parent_state_machine=self.state_machine, + **state_machine_kwargs) if blank_finish_state is None: blank_finish_state = initial_state state_machine.states[blank_finish_state].blank_finish = blank_finish Modified: trunk/docutils/docutils/statemachine.py =================================================================== --- trunk/docutils/docutils/statemachine.py 2025-08-26 08:06:11 UTC (rev 10222) +++ trunk/docutils/docutils/statemachine.py 2025-08-28 16:15:22 UTC (rev 10223) @@ -130,7 +130,8 @@ results of processing in a list. """ - def __init__(self, state_classes, initial_state, debug=False) -> None: + def __init__(self, state_classes, initial_state, + debug=False, parent_state_machine=None) -> None: """ Initialize a `StateMachine` object; add state objects. @@ -139,8 +140,8 @@ - `state_classes`: a list of `State` (sub)classes. - `initial_state`: a string, the class name of the initial state. - `debug`: a boolean; produce verbose output if true (nonzero). + - `parent_state_machine`: the parent of a nested state machine. """ - self.input_lines = None """`StringList` of input lines (without newlines). Filled by `self.run()`.""" @@ -157,6 +158,9 @@ self.debug = debug """Debugging mode on/off.""" + self.parent_state_machine = parent_state_machine + """The instance of the parent state machine or None.""" + self.initial_state = initial_state """The name of the initial state (key to `self.states`).""" Modified: trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-26 08:06:11 UTC (rev 10222) +++ trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-28 16:15:22 UTC (rev 10223) @@ -51,7 +51,6 @@ self.state.nested_parse(self.content, input_offset=0, node=node, match_titles=match_titles) # Append and move the "insertion point" to the last nested section. - # TODO: this fails in some cases, see tests below. self.state_machine.node += node.children # print(self.state_machine, self.state_machine.node[-1].shortrepr()) try: @@ -59,6 +58,14 @@ self.state_machine.node = self.state_machine.node[-1] except IndexError: pass + # pass on the new "current node" to parent state machines + sm = self.state_machine + try: + while True: + sm = sm.parent_state_machine + sm.node = self.state_machine.node + except AttributeError: + pass return [] # node already attached to document @@ -203,7 +210,7 @@ This paragraph belongs to the last nested section. """], ["""\ -.. note:: A preceding directive foils the "insertion point move". +.. note:: A preceding directive must not foil the "insertion point move". .. nested:: @@ -212,13 +219,13 @@ nested1.1 --------- -TODO: This paragraph belongs to the last nested section. +This paragraph belongs to the last nested section. """, """\ <document source="test data"> <note> <paragraph> - A preceding directive foils the "insertion point move". + A preceding directive must not foil the "insertion point move". <section ids="nested1" names="nested1"> <title> nested1 @@ -225,12 +232,24 @@ <section ids="nested1-1" names="nested1.1"> <title> nested1.1 + <paragraph> + This paragraph belongs to the last nested section. +"""], +["""\ +.. nested:: + + Keep the "current node", if the nested parse does not + contain a section. + +This paragraph belongs to the document. +""", +"""\ +<document source="test data"> <paragraph> - TODO: This paragraph belongs to the last nested section. - <system_message level="2" line="10" source="test data" type="WARNING"> - <paragraph> - Element <document source="test data"> invalid: - Child element <paragraph> not allowed at this position. + Keep the "current node", if the nested parse does not + contain a section. + <paragraph> + This paragraph belongs to the document. """], # base node == current node ["""\ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-26 08:06:14
|
Revision: 10222 http://sourceforge.net/p/docutils/code/10222 Author: milde Date: 2025-08-26 08:06:11 +0000 (Tue, 26 Aug 2025) Log Message: ----------- Element after a section from nested parsing may be invalid. `parsers.rst.RSTSTate.nested_parse()` with `match_titles=True` (i.e. support for sections) leads to an invalid document tree, if the nested block contains a section but the element following the nested block is not a section. The "structure model" ony allows a `<section>` as sibling after a `<section>`. https://docutils.sourceforge.io/docs/ref/doctree.html#structure-model An invalid doctree can be prevented if the following content is appended to the last nested section instead of its parent. The "nested" directive attempts this but fails if it is called from another nested state machine: In a nested state_machine we cannot access/change the `node` attribute ("insertion point") of the calling state_machine. Modified Paths: -------------- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py Modified: trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-25 08:33:52 UTC (rev 10221) +++ trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-26 08:06:11 UTC (rev 10222) @@ -31,10 +31,11 @@ from docutils import nodes from docutils.frontend import get_default_settings from docutils.parsers import rst +from docutils.parsers.rst.directives import register_directive from docutils.utils import new_document -class ParseIntoDetachedNode(rst.Directive): +class ParseIntoNode(rst.Directive): """A directive implementing nested parsing with support for sections. """ final_argument_whitespace = True @@ -44,26 +45,36 @@ # similar to sphinx.util.parsing.nested_parse_to_nodes() node = nodes.Element() node.document = self.state.document # not required - # support sections if it is valid: - match_titles = isinstance(self.state.parent, (nodes.document, - nodes.section)) + # support sections (unless we know it is invalid): + match_titles = isinstance(self.state_machine.node, + (nodes.document, nodes.section)) self.state.nested_parse(self.content, input_offset=0, node=node, match_titles=match_titles) - return node.children + # Append and move the "insertion point" to the last nested section. + # TODO: this fails in some cases, see tests below. + self.state_machine.node += node.children + # print(self.state_machine, self.state_machine.node[-1].shortrepr()) + try: + while isinstance(self.state_machine.node[-1], nodes.section): + self.state_machine.node = self.state_machine.node[-1] + except IndexError: + pass + return [] # node already attached to document -class ParseIntoCurrentNode(ParseIntoDetachedNode): +class ParseIntoCurrentNode(ParseIntoNode): def run(self): - node = self.state.parent # the current "insertion point" - # support sections if it is valid: + node = self.state_machine.node # the current "insertion point" + # support sections (unless we know it is invalid): match_titles = isinstance(node, (nodes.document, nodes.section)) self.state.nested_parse(self.content, 0, node, match_titles) - return [] # nodes already attached to document + return [] # node already attached to document -class ParseIntoSectionNode(ParseIntoDetachedNode): +class ParseIntoSectionNode(ParseIntoNode): def run(self): - if not isinstance(self.state.parent, (nodes.document, nodes.section)): + if not isinstance(self.state_machine.node, + (nodes.document, nodes.section)): msg = self.reporter.error( 'The "nested-section" directive can only be used' ' where a section is valid.', @@ -82,12 +93,9 @@ maxDiff = None def test_parser(self): - rst.directives.register_directive('nested-detached', - ParseIntoDetachedNode) - rst.directives.register_directive('nested-current', - ParseIntoCurrentNode) - rst.directives.register_directive('nested-section', - ParseIntoSectionNode) + register_directive('nested', ParseIntoNode) + register_directive('nested-current', ParseIntoCurrentNode) + register_directive('nested-section', ParseIntoSectionNode) parser = rst.Parser() settings = get_default_settings(rst.Parser) settings.warning_stream = '' @@ -97,6 +105,11 @@ with self.subTest(id=f'totest[{name!r}][{casenum}]'): document = new_document('test data', settings.copy()) parser.parse(case_input, document) + try: + document.validate() + except nodes.ValidationError as e: + document.append(document.reporter.warning( + str(e), base_node=e.problematic_element or None)) output = document.pformat() self.assertEqual(case_expected, output) @@ -104,57 +117,35 @@ totest = {} totest['nested_parsing'] = [ - +# Start new section hierarchy with every nested parse. ["""\ -Parse into section node: - -.. nested-section:: - - This is nested. - -sec2 -==== -""", -"""\ -<document source="test data"> - <paragraph> - Parse into section node: - <section> - <title> - generated section - <paragraph> - This is nested. - <section ids="sec2" names="sec2"> - <title> - sec2 -"""], -# start new section hierarchy with every nested parse -["""\ sec1 ==== sec1.1 ------ -.. nested-detached:: - detached1 - ********* - detached1.1 - ----------- - detached1.1.1 - ============= +.. nested:: -.. nested-detached:: + nested1 + ******* + nested1.1 + ========= - detached2 - --------- - detached2.1 - *********** - -sec1.1.1 -~~~~~~~~ sec2 ==== The document-wide section title styles are kept. + +.. nested:: + + nested2 + ======= + nested2.1 + ********* + +sec2.2 +------ +sec2.2.1 +~~~~~~~~ """, """\ <document source="test data"> @@ -164,30 +155,83 @@ <section ids="sec1-1" names="sec1.1"> <title> sec1.1 - <section ids="detached1" names="detached1"> + <section ids="nested1" names="nested1"> <title> - detached1 - <section ids="detached1-1" names="detached1.1"> + nested1 + <section ids="nested1-1" names="nested1.1"> <title> - detached1.1 - <section ids="detached1-1-1" names="detached1.1.1"> - <title> - detached1.1.1 - <section ids="detached2" names="detached2"> - <title> - detached2 - <section ids="detached2-1" names="detached2.1"> - <title> - detached2.1 - <section ids="sec1-1-1" names="sec1.1.1"> - <title> - sec1.1.1 + nested1.1 <section ids="sec2" names="sec2"> <title> sec2 <paragraph> The document-wide section title styles are kept. + <section ids="nested2" names="nested2"> + <title> + nested2 + <section ids="nested2-1" names="nested2.1"> + <title> + nested2.1 + <section ids="sec2-2" names="sec2.2"> + <title> + sec2.2 + <section ids="sec2-2-1" names="sec2.2.1"> + <title> + sec2.2.1 """], +# Move "insertion point" if the nested block contains sections to +# comply with the validity constraints of the "structure model". +["""\ +.. nested:: + + nested1 + ******* + nested1.1 + --------- + +This paragraph belongs to the last nested section. +""", +"""\ +<document source="test data"> + <section ids="nested1" names="nested1"> + <title> + nested1 + <section ids="nested1-1" names="nested1.1"> + <title> + nested1.1 + <paragraph> + This paragraph belongs to the last nested section. +"""], +["""\ +.. note:: A preceding directive foils the "insertion point move". + +.. nested:: + + nested1 + ********* + nested1.1 + --------- + +TODO: This paragraph belongs to the last nested section. +""", +"""\ +<document source="test data"> + <note> + <paragraph> + A preceding directive foils the "insertion point move". + <section ids="nested1" names="nested1"> + <title> + nested1 + <section ids="nested1-1" names="nested1.1"> + <title> + nested1.1 + <paragraph> + TODO: This paragraph belongs to the last nested section. + <system_message level="2" line="10" source="test data" type="WARNING"> + <paragraph> + Element <document source="test data"> invalid: + Child element <paragraph> not allowed at this position. +"""], # base node == current node ["""\ sec1 @@ -203,7 +247,7 @@ current1.1.1 ============ -sec1.1.1 +sec1.1.2 ~~~~~~~~ """, """\ @@ -223,9 +267,9 @@ <section ids="current1-1-1" names="current1.1.1"> <title> current1.1.1 - <section ids="sec1-1-1" names="sec1.1.1"> + <section ids="sec1-1-2" names="sec1.1.2"> <title> - sec1.1.1 + sec1.1.2 """], # parse into generated <section> node: ["""\ @@ -235,12 +279,14 @@ ------ .. nested-section:: - attached1 - ********* - attached1.1 - =========== + nested-section1 + *************** + nested-section1.1 + ================= -sec1.1.1 +This paragraph belongs to the last nested section. + +sec1.1.2 ~~~~~~~~ """, @@ -255,15 +301,21 @@ <section> <title> generated section - <section ids="attached1" names="attached1"> + <section ids="nested-section1" names="nested-section1"> <title> - attached1 - <section ids="attached1-1" names="attached1.1"> + nested-section1 + <section ids="nested-section1-1" names="nested-section1.1"> <title> - attached1.1 - <section ids="sec1-1-1" names="sec1.1.1"> + nested-section1.1 + <paragraph> + This paragraph belongs to the last nested section. + <section ids="sec1-1-2" names="sec1.1.2"> <title> - sec1.1.1 + sec1.1.2 + <system_message level="2" line="12" source="test data" type="WARNING"> + <paragraph> + Element <section ids="sec1-1" names="sec1.1"> invalid: + Child element <paragraph> not allowed at this position. """], # Nested parsing in a block-quote: ["""\ @@ -274,7 +326,7 @@ nested section ============== - .. nested-detached:: + .. nested:: invalid section --------------- This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-25 08:33:55
|
Revision: 10221 http://sourceforge.net/p/docutils/code/10221 Author: milde Date: 2025-08-25 08:33:52 +0000 (Mon, 25 Aug 2025) Log Message: ----------- Use a "property" for `parsers.rst.states.RSTState.parent`. Using a property attribute instead of runtime initialization ensures the "insertion point"/"current node" is synchronised across the statemachine and all state instances. This may allow support for nested parsing with document-wide section title styles. Modified Paths: -------------- trunk/docutils/docutils/parsers/rst/states.py Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-22 11:14:54 UTC (rev 10220) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-25 08:33:52 UTC (rev 10221) @@ -232,11 +232,18 @@ self.document = memo.document self.inliner = memo.inliner self.reporter = self.document.reporter - self.parent = self.state_machine.node # enable the reporter to determine source and source-line if not hasattr(self.reporter, 'get_source_and_line'): self.reporter.get_source_and_line = self.state_machine.get_source_and_line # noqa:E501 + @property + def parent(self) -> nodes.Element | None: + return self.state_machine.node + + @parent.setter + def parent(self, value: nodes.Element): + self.state_machine.node = value + def goto_line(self, abs_line_offset) -> None: """ Jump to input line `abs_line_offset`, ignoring jumps past the end. @@ -425,15 +432,7 @@ section_node += title_messages self.document.note_implicit_target(section_node, section_node) # Update state: - self.state_machine.node = section_node - # Also update the ".parent" attribute in all states. - # This is a bit violent, but the state classes copy their .parent from - # state_machine.node on creation, so we need to update them. We could - # also remove RSTState.parent entirely and replace references to it - # with statemachine.node, but that might break code downstream of - # docutils. - for s in self.state_machine.states.values(): - s.parent = section_node + self.parent = section_node def paragraph(self, lines, lineno): """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-22 11:14:56
|
Revision: 10220 http://sourceforge.net/p/docutils/code/10220 Author: milde Date: 2025-08-22 11:14:54 +0000 (Fri, 22 Aug 2025) Log Message: ----------- Fix/Update tests for nested parsing with sections. Add error messages for invalid sections to the sample directives. Update the sample text to mirror the current behaviour. Modified Paths: -------------- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py Modified: trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-21 15:18:05 UTC (rev 10219) +++ trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-22 11:14:54 UTC (rev 10220) @@ -43,9 +43,12 @@ def run(self): # similar to sphinx.util.parsing.nested_parse_to_nodes() node = nodes.Element() - node.document = self.state.document + node.document = self.state.document # not required + # support sections if it is valid: + match_titles = isinstance(self.state.parent, (nodes.document, + nodes.section)) self.state.nested_parse(self.content, input_offset=0, - node=node, match_titles=True) + node=node, match_titles=match_titles) return node.children @@ -52,16 +55,27 @@ class ParseIntoCurrentNode(ParseIntoDetachedNode): def run(self): node = self.state.parent # the current "insertion point" - self.state.nested_parse(self.content, 0, node, match_titles=True) + # support sections if it is valid: + match_titles = isinstance(node, (nodes.document, nodes.section)) + self.state.nested_parse(self.content, 0, node, match_titles) return [] # nodes already attached to document -class ParseIntoAttachedNode(ParseIntoDetachedNode): +class ParseIntoSectionNode(ParseIntoDetachedNode): def run(self): - node = nodes.sidebar('') - self.state.parent.append(node) + if not isinstance(self.state.parent, (nodes.document, nodes.section)): + msg = self.reporter.error( + 'The "nested-section" directive can only be used' + ' where a section is valid.', + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return [msg] + node = nodes.section('') + node.append(nodes.title('', 'generated section')) + # In production, also generate and register section name and ID + # (cf. rst.states.RSTState.new_subsection()). self.state.nested_parse(self.content, 0, node, match_titles=True) - return [] # nodes already attached to document + return [node] class ParserTestCase(unittest.TestCase): @@ -72,8 +86,8 @@ ParseIntoDetachedNode) rst.directives.register_directive('nested-current', ParseIntoCurrentNode) - rst.directives.register_directive('nested-attached', - ParseIntoAttachedNode) + rst.directives.register_directive('nested-section', + ParseIntoSectionNode) parser = rst.Parser() settings = get_default_settings(rst.Parser) settings.warning_stream = '' @@ -89,29 +103,32 @@ totest = {} -# Parse into the base node: totest['nested_parsing'] = [ + ["""\ -Preceding paragraph. +Parse into section node: -.. nested-attached:: +.. nested-section:: - .. hint:: this is nested. + This is nested. -Succeeding paragraph. +sec2 +==== """, """\ <document source="test data"> <paragraph> - Preceding paragraph. - <sidebar> - <hint> - <paragraph> - this is nested. - <paragraph> - Succeeding paragraph. + Parse into section node: + <section> + <title> + generated section + <paragraph> + This is nested. + <section ids="sec2" names="sec2"> + <title> + sec2 """], -# detached base node -> start new section hierarchy with every nested parse +# start new section hierarchy with every nested parse ["""\ sec1 ==== @@ -133,8 +150,8 @@ detached2.1 *********** -Succeeding paragraph. - +sec1.1.1 +~~~~~~~~ sec2 ==== The document-wide section title styles are kept. @@ -162,8 +179,9 @@ <section ids="detached2-1" names="detached2.1"> <title> detached2.1 - <paragraph> - Succeeding paragraph. + <section ids="sec1-1-1" names="sec1.1.1"> + <title> + sec1.1.1 <section ids="sec2" names="sec2"> <title> sec2 @@ -170,7 +188,7 @@ <paragraph> The document-wide section title styles are kept. """], -# base node == current node -> keep section hierarchy +# base node == current node ["""\ sec1 ==== @@ -184,9 +202,9 @@ ----------- current1.1.1 ============ - Top-level section appended to document. -Succeeding paragraph. +sec1.1.1 +~~~~~~~~ """, """\ <document source="test data"> @@ -205,18 +223,17 @@ <section ids="current1-1-1" names="current1.1.1"> <title> current1.1.1 - <paragraph> - Top-level section appended to document. - <paragraph> - Succeeding paragraph. + <section ids="sec1-1-1" names="sec1.1.1"> + <title> + sec1.1.1 """], -# parse into attached wrapper node: +# parse into generated <section> node: ["""\ sec1 ==== sec1.1 ------ -.. nested-attached:: +.. nested-section:: attached1 ********* @@ -223,7 +240,9 @@ attached1.1 =========== -Succeeding paragraph. +sec1.1.1 +~~~~~~~~ + """, """\ <document source="test data"> @@ -233,7 +252,9 @@ <section ids="sec1-1" names="sec1.1"> <title> sec1.1 - <sidebar> + <section> + <title> + generated section <section ids="attached1" names="attached1"> <title> attached1 @@ -240,78 +261,52 @@ <section ids="attached1-1" names="attached1.1"> <title> attached1.1 - <paragraph> - Succeeding paragraph. -"""], -# detached base node -> start new section hierarchy -["""\ -sec1 -==== -sec1.1 ------- -sec2 -==== -.. nested-detached:: - detached1 - ~~~~~~~~~ - detached1.1 - ----------- - -Succeeding paragraph. -""", -"""\ -<document source="test data"> - <section ids="sec1" names="sec1"> - <title> - sec1 - <section ids="sec1-1" names="sec1.1"> - <title> - sec1.1 - <section ids="sec2" names="sec2"> - <title> - sec2 - <section ids="detached1" names="detached1"> - <title> - detached1 - <section ids="detached1-1" names="detached1.1"> + <section ids="sec1-1-1" names="sec1.1.1"> <title> - detached1.1 - <paragraph> - Succeeding paragraph. + sec1.1.1 """], -# base node == <blockquote> +# Nested parsing in a block-quote: ["""\ -sec1 -==== + .. nested-current:: - A block-quote is parsed into a detached <blockquote> element. + Nested parsing is OK but a section is invalid in a block-quote. - .. nested-current:: - nested section ============== - The nested <section> becomes a child of the <blockquote> (sic.)! + .. nested-detached:: -The calling directive should move the nested <section> or report -a validity violation. + invalid section + --------------- + + .. nested-section:: + + The <section> base node is invalid in a block-quote. """, """\ <document source="test data"> - <section ids="sec1" names="sec1"> - <title> - sec1 - <block_quote> + <block_quote> + <paragraph> + Nested parsing is OK but a section is invalid in a block-quote. + <system_message level="3" line="6" source="test data" type="ERROR"> <paragraph> - A block-quote is parsed into a detached <blockquote> element. - <section ids="nested-section" names="nested\\ section"> - <title> - nested section + Unexpected section title. + <literal_block xml:space="preserve"> + nested section + ============== + <system_message level="3" line="11" source="test data" type="ERROR"> <paragraph> - The nested <section> becomes a child of the <blockquote> (sic.)! - <paragraph> - The calling directive should move the nested <section> or report - a validity violation. + Unexpected section title. + <literal_block xml:space="preserve"> + invalid section + --------------- + <system_message level="3" line="13" source="test data" type="ERROR"> + <paragraph> + The "nested-section" directive can only be used where a section is valid. + <literal_block xml:space="preserve"> + .. nested-section:: + \n\ + The <section> base node is invalid in a block-quote. """], ] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-21 15:18:09
|
Revision: 10219 http://sourceforge.net/p/docutils/code/10219 Author: milde Date: 2025-08-21 15:18:05 +0000 (Thu, 21 Aug 2025) Log Message: ----------- LaTeX writer: write "label" commands for elements with "ids". Fix [bugs:#503]. Currently, internal cross-references to most body elements (admonitions, lists, block_quotes, container, compound, ...) fail, because the "ids" attribute of these elements is ignored. Calls `ids_to_labels()` for (almost) all elements that may have one or more IDs. TODO: * test/revise citations and footnotes * class handling for image and list items (use DUclass environment or DUrole function?) * Currently, there is no way to specify a target name or custom class for the "docinfo" in the rST source. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/writers/latex2e/__init__.py trunk/docutils/test/functional/expected/latex_cornercases.tex trunk/docutils/test/functional/expected/latex_leavevmode.tex trunk/docutils/test/functional/expected/latex_memoir.tex trunk/docutils/test/functional/expected/standalone_rst_latex.tex trunk/docutils/test/functional/expected/standalone_rst_xetex.tex trunk/docutils/test/test_writers/test_latex2e.py trunk/docutils/test/test_writers/test_latex2e_parts.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/HISTORY.rst 2025-08-21 15:18:05 UTC (rev 10219) @@ -43,7 +43,8 @@ * docutils/writers/latex2e/__init__.py - - Prepend ``\phantomsection`` to labelled math-blocks. + - Add cross-reference anchors (``\phantomsection\label{...}``) + for elements with IDs (fixes bug #503). - Fix cross-reference anchor placement in figures, images, literal-blocks, tables, and (sub)titles. - Simplify code for nested image. Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-21 15:18:05 UTC (rev 10219) @@ -1714,6 +1714,8 @@ self.provide_fallback('admonition') if 'error' in node['classes']: self.provide_fallback('error') + if not isinstance(node, nodes.system_message): + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) self.out.append('\\begin{DUadmonition}') @@ -1746,6 +1748,7 @@ self.depart_docinfo_item(node) def visit_block_quote(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) self.out.append('\\begin{quote}') @@ -1754,6 +1757,7 @@ self.duclass_close(node) def visit_bullet_list(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) self.out.append('\\begin{itemize}') @@ -1876,6 +1880,7 @@ def visit_compound(self, node) -> None: if isinstance(node.parent, nodes.compound): self.out.append('\n') + self.out += self.ids_to_labels(node, pre_nl=True) node['classes'].insert(0, 'compound') self.duclass_open(node) @@ -1889,6 +1894,7 @@ self.depart_docinfo_item(node) def visit_container(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) def depart_container(self, node) -> None: @@ -1920,6 +1926,7 @@ pass def visit_definition_list(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) self.out.append('\\begin{description}\n') @@ -1928,7 +1935,7 @@ self.duclass_close(node) def visit_definition_list_item(self, node) -> None: - pass + self.out += self.ids_to_labels(node, newline=True) def depart_definition_list_item(self, node) -> None: if node.next_node(descend=False, siblings=True) is not None: @@ -2239,6 +2246,7 @@ label = r'%s\%s{%s}%s' % (prefix, enumtype, counter_name, suffix) self._enumeration_counters.append(label) + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) if enum_level <= 4: self.out.append('\\begin{enumerate}') @@ -2263,8 +2271,8 @@ self._enumeration_counters.pop() def visit_field(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) # output is done in field_body, field_name - pass def depart_field(self, node) -> None: pass @@ -2278,6 +2286,7 @@ self.out.append(r'\\'+'\n') def visit_field_list(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) if self.out is not self.docinfo: self.provide_fallback('fieldlist') @@ -2447,6 +2456,7 @@ def visit_image(self, node) -> None: # <image> can be inline element, body element, or nested in a <figure> # in all three cases the <image> may also be nested in a <reference> + # TODO: "classes" attribute currently ignored! self.requirements['graphicx'] = self.graphicx_package attrs = node.attributes # convert image URI to filesystem path, do not adjust relative path: @@ -2560,6 +2570,7 @@ '\\begin{DUlineblock}{\\DUlineblockindent}\n') # In rST, nested line-blocks cannot be given class arguments else: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) self.out.append('\\begin{DUlineblock}{0em}\n') self.insert_align_declaration(node) @@ -2569,6 +2580,7 @@ self.duclass_close(node) def visit_list_item(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) self.out.append('\n\\item ') def depart_list_item(self, node) -> None: @@ -2784,6 +2796,7 @@ def visit_option_list(self, node) -> None: self.provide_fallback('providelength', '_providelength') self.provide_fallback('optionlist') + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) self.out.append('\\begin{DUoptionlist}\n') @@ -2792,7 +2805,7 @@ self.duclass_close(node) def visit_option_list_item(self, node) -> None: - pass + self.out += self.ids_to_labels(node, newline=True) def depart_option_list_item(self, node) -> None: pass @@ -2927,10 +2940,13 @@ self.provide_fallback('rubric') # class wrapper would interfere with ``\section*"`` type commands # (spacing/indent of first paragraph) - self.out.append('\n\\DUrubric{') + self.out += self.ids_to_labels(node, pre_nl=True) + self.duclass_open(node) + self.out.append('\\DUrubric{') def depart_rubric(self, node) -> None: self.out.append('}\n') + self.duclass_close(node) def visit_section(self, node) -> None: # Update counter-prefix for compound enumerators @@ -2972,6 +2988,7 @@ self.section_level -= 1 def visit_sidebar(self, node) -> None: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) self.requirements['color'] = PreambleCmds.color self.provide_fallback('sidebar') @@ -2988,12 +3005,15 @@ def visit_attribution(self, node) -> None: prefix, suffix = self.attribution_formats[self.settings.attribution] - self.out.append('\\nopagebreak\n\n\\raggedleft ') - self.out.append(prefix) + self.out.append('\\nopagebreak\n') + self.out += self.ids_to_labels(node, pre_nl=True) + self.duclass_open(node) + self.out.append(f'\\raggedleft {prefix}') self.context.append(suffix) def depart_attribution(self, node) -> None: self.out.append(self.context.pop() + '\n') + self.duclass_close(node) def visit_status(self, node) -> None: self.visit_docinfo_item(node) @@ -3281,7 +3301,7 @@ # labels and PDF bookmark (sidebar entry) self.out.append('\n') # start new paragraph - if node['names']: # don't add labels just for auto-ids + if len(node['names']) > 1: # don't add labels just for the auto-id self.out += self.ids_to_labels(node, newline=True) if (isinstance(node.next_node(), nodes.title) and 'local' not in node['classes'] Modified: trunk/docutils/test/functional/expected/latex_cornercases.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_cornercases.tex 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/test/functional/expected/latex_cornercases.tex 2025-08-21 15:18:05 UTC (rev 10219) @@ -139,7 +139,6 @@ These tests contain syntax elements and combinations which may cause trouble for the LaTeX writer. -\phantomsection\label{contents} \pdfbookmark[1]{Contents}{contents} \tableofcontents Modified: trunk/docutils/test/functional/expected/latex_leavevmode.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_leavevmode.tex 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/test/functional/expected/latex_leavevmode.tex 2025-08-21 15:18:05 UTC (rev 10219) @@ -360,6 +360,7 @@ \item[{Comment and Target}] \leavevmode % This is ignored. +\phantomsection\label{foo} \begin{itemize} \item Comments and other “Invisible” nodes (substitution definitions, targets, pending) must be skipped when determining whether a Modified: trunk/docutils/test/functional/expected/latex_memoir.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-21 15:18:05 UTC (rev 10219) @@ -271,7 +271,6 @@ \pagebreak[4] % start ToC on new page -\phantomsection\label{table-of-contents} \renewcommand{\contentsname}{Table of Contents} \tableofcontents Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-21 15:18:05 UTC (rev 10219) @@ -268,7 +268,6 @@ \pagebreak[4] % start ToC on new page -\phantomsection\label{table-of-contents} \pdfbookmark[1]{Table of Contents}{table-of-contents} \renewcommand{\contentsname}{Table of Contents} \tableofcontents Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-21 15:18:05 UTC (rev 10219) @@ -135,7 +135,6 @@ \pagebreak[4] % start ToC on new page -\phantomsection\label{table-of-contents} \pdfbookmark[1]{Table of Contents}{table-of-contents} \begin{DUclass}{contents} @@ -787,7 +786,6 @@ \label{directives}% } -\phantomsection\label{contents} \begin{DUclass}{contents} \begin{DUclass}{local} Modified: trunk/docutils/test/test_writers/test_latex2e.py =================================================================== --- trunk/docutils/test/test_writers/test_latex2e.py 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/test/test_writers/test_latex2e.py 2025-08-21 15:18:05 UTC (rev 10219) @@ -316,9 +316,11 @@ \\hline \\end{longtable*} """], +]) + # Test handling of IDs and custom class values # -------------------------------------------- -# targets with ID +samples['IDs and classes'] = ({}, [ ["""\ A paragraph with _`inline target`. @@ -335,6 +337,177 @@ \phantomsection\label{block-target} \DUrole{custom}{\DUrole{paragraph}{Next paragraph.}} """], +# admonition +[""" +.. class:: cls1 +.. _label1: +.. hint:: + :name: label2 + :class: cls2 + + Don't forget to breathe. +""", +r""" +\phantomsection\label{label2}\label{label1} +\begin{DUclass}{cls2} +\begin{DUclass}{cls1} +\begin{DUclass}{hint} +\begin{DUadmonition} +\DUtitle{Hint} + +Don't forget to breathe. +\end{DUadmonition} +\end{DUclass} +\end{DUclass} +\end{DUclass} +"""], +# block quote +[""" +.. class:: cls1 +.. _label1: + + Exlicit is better than implicit. + + .. class:: attribute-cls cute + .. _a-tribution: + + -- Zen of Python +""", +r""" +\phantomsection\label{label1} +\begin{DUclass}{cls1} +\begin{quote} +Exlicit is better than implicit. +\nopagebreak + +\phantomsection\label{a-tribution} +\begin{DUclass}{attribute-cls} +\begin{DUclass}{cute} +\raggedleft —Zen of Python +\end{DUclass} +\end{DUclass} +\end{quote} +\end{DUclass} +"""], +# bullet list +[""" +.. class:: cls1 +.. _bullet1: + +* list item + + .. class:: bullet-class + .. _b-item: + +* second bullet list item +""", +r""" +\phantomsection\label{bullet1} +\begin{DUclass}{cls1} +\begin{itemize} +\item list item + +\phantomsection\label{b-item} +\item second bullet list item +\end{itemize} +\end{DUclass} +"""], +# definition list +[""" +.. class:: def-list-class +.. _def-list: + +definition + list + + .. class:: def-item-class + .. _def-item: + +term + definition +""", +""" +\\phantomsection\\label{def-list} +\\begin{DUclass}{def-list-class} +\\begin{description} +\\item[{definition}] \n\ +list + +\\phantomsection\\label{def-item} +\\item[{term}] \n\ +definition +\\end{description} +\\end{DUclass} +"""], +# enumerated list +[""" +.. class:: cls1 +.. _enumerated1: + +#. list item + + .. class:: e-item-class + .. _e-item: + +#. enumerated list item +""", +r""" +\phantomsection\label{enumerated1} +\begin{DUclass}{cls1} +\begin{enumerate} +\item list item + +\phantomsection\label{e-item} +\item enumerated list item +\end{enumerate} +\end{DUclass} +"""], +# field list +["""\ +Not a docinfo. + +.. class:: fieldlist-class +.. _f-list: + +:field: list + + .. class:: field-class + .. _f-list-item: + +:name: body +""", +r""" +Not a docinfo. + +\phantomsection\label{f-list} +\begin{DUclass}{fieldlist-class} +\begin{DUfieldlist} +\item[{field:}] +list + +\phantomsection\label{f-list-item} +\item[{name:}] +body +\end{DUfieldlist} +\end{DUclass} +"""], +# line block +["""\ +.. class:: lineblock-class +.. _line-block: + +| line block +| second line +""", +r""" +\phantomsection\label{line-block} +\begin{DUclass}{lineblock-class} +\begin{DUlineblock}{0em} +\item[] line block +\item[] second line +\end{DUlineblock} +\end{DUclass} +"""], # literal block ["""\ .. class:: cls1 @@ -354,6 +527,28 @@ \end{quote} \end{DUclass} """], +# option list +["""\ +.. class:: o-list-class +.. _o-list: + +--an option list + + .. class:: option-class + .. _o-item: + +--another option +""", +r""" +\phantomsection\label{o-list} +\begin{DUclass}{o-list-class} +\begin{DUoptionlist} +\item[-{}-an] option list +\phantomsection\label{o-item} +\item[-{}-another] option +\end{DUoptionlist} +\end{DUclass} +"""], # table with IDs and custom + special class values ["""\ .. class:: cls1 @@ -377,6 +572,145 @@ \end{DUclass} \end{DUclass} """], +# directives +["""\ +.. compound:: + :class: compoundclass + :name: com-pound + + Compound paragraph 1 + + Compound paragraph 2 + +.. container:: containerclass + :name: con-tainer + + Container paragraph 1 + + Container paragraph 2 +""", +r""" +\phantomsection\label{com-pound} +\begin{DUclass}{compound} +\begin{DUclass}{compoundclass} +Compound paragraph 1 + +Compound paragraph 2 +\end{DUclass} +\end{DUclass} + +\phantomsection\label{con-tainer} +\begin{DUclass}{containerclass} +Container paragraph 1 + +Container paragraph 2 +\end{DUclass} +"""], +# figures and images +["""\ +.. figure:: parrot.png + :figclass: figureclass + :figname: fig-ure + + .. class:: f-caption-class + .. _f-caption: + + A figure with caption + + .. class:: legend-class + .. _le-gend: + + A figure legend + +.. image:: parrot.png + :class: imgclass TODO ignored! + :name: i-mage + :target: example.org/parrots +""", +r""" +\phantomsection\label{fig-ure} +\begin{DUclass}{figureclass} +\begin{figure} +\noindent\makebox[\linewidth][c]{\includegraphics{parrot.png}} +\caption{\label{f-caption}\DUrole{f-caption-class}{A figure with caption}} +\begin{DUlegend} +\phantomsection\label{le-gend} +\DUrole{legend-class}{A figure legend} +\end{DUlegend} +\end{figure} +\end{DUclass} + +\phantomsection\label{i-mage} +\href{example.org/parrots}{\includegraphics{parrot.png}} +"""], +["""\ +.. math:: x = 2^4 + :class: mathclass + :name: math-block + +.. note:: a specific admonition + :class: noteclass + :name: my-note + +.. _my-raw: +.. raw:: latex pseudoxml xml + :class: rawclass + + \\LaTeX + +.. sidebar:: sidebar title + :class: sideclass + :name: side-bar + + sidebar content + +.. topic:: topic heading + :class: topicclass + :name: to-pic + + topic content +""", +r"""% +\phantomsection +\DUrole{mathclass}{% +\begin{equation*} +x = 2^4 +\label{math-block} +\end{equation*} +} +\phantomsection\label{my-note} +\begin{DUclass}{noteclass} +\begin{DUclass}{note} +\begin{DUadmonition} +\DUtitle{Note} + +a specific admonition +\end{DUadmonition} +\end{DUclass} +\end{DUclass} + +\phantomsection\label{my-raw}\DUrole{rawclass}{\LaTeX} + +\phantomsection\label{side-bar} +\begin{DUclass}{sideclass} +\DUsidebar{ +\DUtitle{sidebar title} + +sidebar content +} +\end{DUclass} + +\phantomsection\label{to-pic} +\begin{DUclass}{topic} +\begin{DUclass}{topicclass} +\begin{quote} +\DUtitle{topic heading} + +topic content +\end{quote} +\end{DUclass} +\end{DUclass} +"""], ]) samples['latex_sectnum'] = ({'sectnum_xform': False}, [ Modified: trunk/docutils/test/test_writers/test_latex2e_parts.py =================================================================== --- trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-20 12:04:54 UTC (rev 10218) +++ trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-21 15:18:05 UTC (rev 10219) @@ -214,7 +214,6 @@ ------------------ """, {'body': r""" -\phantomsection\label{contents} \pdfbookmark[1]{Contents}{contents} \tableofcontents @@ -235,7 +234,6 @@ ------------- """, {'body': r""" -\phantomsection\label{contents} \pdfbookmark[1]{Contents}{contents} \tableofcontents @@ -256,7 +254,6 @@ ------------- """, {'body': r""" -\phantomsection\label{contents} \pdfbookmark[1]{Contents}{contents} \setcounter{tocdepth}{1} \tableofcontents @@ -286,7 +283,6 @@ \label{section-with-local-toc}% } -\phantomsection\label{contents} \mtcsettitle{secttoc}{} \secttoc @@ -596,7 +592,6 @@ ------------- """, {'body': r""" -\phantomsection\label{contents} \pdfbookmark[1]{Contents}{contents} \setcounter{tocdepth}{0} \tableofcontents @@ -703,7 +698,6 @@ Paragraph 2. """, {'body': r""" -\phantomsection\label{table-of-contents} \pdfbookmark[1]{Table of Contents}{table-of-contents} \begin{DUclass}{contents} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-20 12:04:57
|
Revision: 10218 http://sourceforge.net/p/docutils/code/10218 Author: milde Date: 2025-08-20 12:04:54 +0000 (Wed, 20 Aug 2025) Log Message: ----------- LaTeX writer: dont add `\phantomsection` for subtitle labels. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/writers/latex2e/__init__.py trunk/docutils/test/test_writers/test_latex2e_parts.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-20 12:04:42 UTC (rev 10217) +++ trunk/docutils/HISTORY.rst 2025-08-20 12:04:54 UTC (rev 10218) @@ -45,7 +45,7 @@ - Prepend ``\phantomsection`` to labelled math-blocks. - Fix cross-reference anchor placement in figures, images, - literal-blocks, and tables. + literal-blocks, tables, and (sub)titles. - Simplify code for nested image. Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:42 UTC (rev 10217) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:54 UTC (rev 10218) @@ -1381,7 +1381,8 @@ # ----------------- def stylesheet_call(self, path): - """Return code to reference or embed stylesheet file `path`""" + """Return code to reference or embed stylesheet file `path`.""" + path = Path(path) # is it a package (no extension or *.sty) or "normal" tex code: is_package = path.suffix in ('.sty', '') @@ -2518,8 +2519,9 @@ # Handle "ids" attribute: # do we need a \phantomsection? - set_anchor = not (isinstance(node.parent, (nodes.caption, nodes.title)) - or isinstance(node, nodes.caption)) + anchor_nodes = (nodes.caption, nodes.subtitle, nodes.title) + set_anchor = not (isinstance(node.parent, anchor_nodes) + or isinstance(node, anchor_nodes)) add_newline = isinstance(node, nodes.paragraph) self.out += self.ids_to_labels(node, set_anchor, newline=add_newline) # Handle "classes" attribute: Modified: trunk/docutils/test/test_writers/test_latex2e_parts.py =================================================================== --- trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-20 12:04:42 UTC (rev 10217) +++ trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-20 12:04:54 UTC (rev 10218) @@ -464,6 +464,45 @@ \date{} """ }], +# document title and subtitle with labels +["""\ +.. _top: + +The Document Title +================== + +.. _what-for: + +for test purposes +----------------- + +Links to top_ and what-for_. +""", + {'body': r""" +Links to \hyperref[top]{top} and \hyperref[what-for]{what-for}. +""", + 'body_pre_docinfo': '\\maketitle\n', + 'fallbacks': r""" +% subtitle (in document title) +\providecommand*{\DUdocumentsubtitle}[1]{{\large #1}} +""", + 'pdfsetup': DEFAULT_PARTS['pdfsetup'] + r"""\hypersetup{ + pdftitle={The Document Title}, +} +""", + 'subtitle': 'for test purposes', + 'title': 'The Document Title', + 'titledata': r"""\title{The Document Title% + \label{the-document-title}% + \label{top}% + \\% + \DUdocumentsubtitle{for test purposes}% + \label{for-test-purposes}% + \label{what-for}} +\author{} +\date{} +""" + }], # template ["""\ """, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-20 12:04:45
|
Revision: 10217 http://sourceforge.net/p/docutils/code/10217 Author: milde Date: 2025-08-20 12:04:42 +0000 (Wed, 20 Aug 2025) Log Message: ----------- LaTeX writer: Simplify code for nested image. Simplify logic for the insertion of newlines around the image inclusion macro. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/writers/latex2e/__init__.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-20 12:04:32 UTC (rev 10216) +++ trunk/docutils/HISTORY.rst 2025-08-20 12:04:42 UTC (rev 10217) @@ -46,6 +46,7 @@ - Prepend ``\phantomsection`` to labelled math-blocks. - Fix cross-reference anchor placement in figures, images, literal-blocks, and tables. + - Simplify code for nested image. Release 0.22 (2026-07-29) Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:32 UTC (rev 10216) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:42 UTC (rev 10217) @@ -1777,7 +1777,7 @@ self.out.append('}') def visit_caption(self, node) -> None: - self.out.append('\n\\caption{') + self.out.append('\\caption{') self.visit_inline(node) def depart_caption(self, node) -> None: @@ -2312,9 +2312,9 @@ # The LaTeX "figure" environment always uses the full linewidth, # so "outer alignment" is ignored. Just write a comment. # TODO: use the wrapfigure environment? - self.out.append('\\begin{figure} %% align = "%s"\n' % alignment) + self.out.append('\\begin{figure} %% align = "%s"' % alignment) else: - self.out.append('\\begin{figure}\n') + self.out.append('\\begin{figure}') def depart_figure(self, node) -> None: self.out.append('\\end{figure}\n') @@ -2444,6 +2444,8 @@ return f'{value}\\DU{unit}dimen' def visit_image(self, node) -> None: + # <image> can be inline element, body element, or nested in a <figure> + # in all three cases the <image> may also be nested in a <reference> self.requirements['graphicx'] = self.graphicx_package attrs = node.attributes # convert image URI to filesystem path, do not adjust relative path: @@ -2490,12 +2492,12 @@ f"width={self.to_latex_length(attrs['width'], node)}") pre.append(''.join(self.ids_to_labels(node, newline=True))) if not (self.is_inline(node) - or isinstance(node.parent, (nodes.figure, nodes.compound))): + or isinstance(node.parent, nodes.compound)): pre.append('\n') - if not (self.is_inline(node) - or isinstance(node.parent, nodes.figure)): + if not self.is_inline(node): post.append('\n') pre.reverse() + # now insert image code self.out.extend(pre) if imagepath.suffix == '.svg' and 'svg' in self.settings.stylesheet: cmd = 'includesvg' @@ -2880,7 +2882,7 @@ ord('%'): '\\%', ord('\\'): '\\\\', } - if not (self.is_inline(node) or isinstance(node.parent, nodes.figure)): + if not self.is_inline(node): self.out.append('\n') # external reference (URL) if 'refuri' in node: @@ -2910,7 +2912,7 @@ def depart_reference(self, node) -> None: self.out.append('}') - if not (self.is_inline(node) or isinstance(node.parent, nodes.figure)): + if not self.is_inline(node): self.out.append('\n') def visit_revision(self, node) -> None: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-20 12:04:35
|
Revision: 10216 http://sourceforge.net/p/docutils/code/10216 Author: milde Date: 2025-08-20 12:04:32 +0000 (Wed, 20 Aug 2025) Log Message: ----------- LaTeX writer: fix anchor placement for figures, images, literal blocks, tables. New attribute "pre_nl" for `ids_to_labels()`: prepend newline to label definitions if there are labels. Move anchor and label definition(s) for images, figures, literal blocks, and tables before class wrappers and element. Now, after activating a link to the element, it is fully visible in the PDF viewer. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/writers/latex2e/__init__.py trunk/docutils/test/functional/expected/latex_cornercases.tex trunk/docutils/test/functional/expected/latex_memoir.tex trunk/docutils/test/functional/expected/standalone_rst_latex.tex trunk/docutils/test/functional/expected/standalone_rst_xetex.tex trunk/docutils/test/test_writers/test_latex2e.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-20 12:04:19 UTC (rev 10215) +++ trunk/docutils/HISTORY.rst 2025-08-20 12:04:32 UTC (rev 10216) @@ -44,6 +44,8 @@ * docutils/writers/latex2e/__init__.py - Prepend ``\phantomsection`` to labelled math-blocks. + - Fix cross-reference anchor placement in figures, images, + literal-blocks, and tables. Release 0.22 (2026-07-29) Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:19 UTC (rev 10215) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:32 UTC (rev 10216) @@ -1560,19 +1560,25 @@ id for id in node['ids'])) def ids_to_labels(self, node, set_anchor=True, protect=False, - newline=False) -> list[str]: + newline=False, pre_nl=False) -> list[str]: """Return label definitions for all ids of `node`. If `set_anchor` is True, an anchor is set with \\phantomsection. If `protect` is True, the \\label cmd is made robust. If `newline` is True, a newline is added if there are labels. + If `pre_nl` is True, a newline is prepended if there are labels. + + Provisional. """ prefix = '\\protect' if protect else '' - labels = [prefix + '\\label{%s}' % id for id in node['ids']] - if set_anchor and labels: - labels.insert(0, '\\phantomsection') - if newline and labels: - labels.append('\n') + labels = [f'{prefix}\\label{{{id}}}' for id in node['ids']] + if labels: + if set_anchor: + labels.insert(0, '\\phantomsection') + if newline: + labels.append('\n') + if pre_nl: + labels.insert(0, '\n') return labels def set_align_from_classes(self, node) -> None: @@ -2297,6 +2303,7 @@ def visit_figure(self, node) -> None: self.requirements['float'] = PreambleCmds.float + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) # The 'align' attribute sets the "outer alignment", # for "inner alignment" use LaTeX default alignment (similar to HTML) @@ -2308,7 +2315,6 @@ self.out.append('\\begin{figure} %% align = "%s"\n' % alignment) else: self.out.append('\\begin{figure}\n') - self.out += self.ids_to_labels(node, newline=True) def depart_figure(self, node) -> None: self.out.append('\\end{figure}\n') @@ -2482,6 +2488,7 @@ if 'width' in attrs: include_graphics_options.append( f"width={self.to_latex_length(attrs['width'], node)}") + pre.append(''.join(self.ids_to_labels(node, newline=True))) if not (self.is_inline(node) or isinstance(node.parent, (nodes.figure, nodes.compound))): pre.append('\n') @@ -2501,7 +2508,7 @@ self.out.extend(post) def depart_image(self, node) -> None: - self.out += self.ids_to_labels(node, newline=True) + pass def visit_inline(self, node) -> None: # This function is also called by the visiting functions for @@ -2623,8 +2630,8 @@ _use_listings = (literal_env == 'lstlisting') and _use_env # Labels and classes: + self.out += self.ids_to_labels(node, pre_nl=True) self.duclass_open(node) - self.out += self.ids_to_labels(node, newline=True) # Highlight code? if (not _plaintext and 'code' in node['classes'] @@ -3058,7 +3065,6 @@ self.depart_admonition(node) def visit_table(self, node) -> None: - self.duclass_open(node) self.requirements['table'] = PreambleCmds.table if not self.settings.legacy_column_widths: self.requirements['table1'] = PreambleCmds.table_columnwidth @@ -3090,9 +3096,9 @@ # if it has no caption/title. # See visit_thead() for tables with caption. if not self.active_table.caption: - self.out.extend(self.ids_to_labels( - node, set_anchor=len(self.table_stack) != 1, - newline=True)) + set_anchor = (len(self.table_stack) != 1) + self.out += self.ids_to_labels(node, set_anchor, pre_nl=True) + self.duclass_open(node) # TODO: Don't use a longtable or add \noindent before # the next paragraph, when in a "compound paragraph". # Start a new line or a new paragraph? Modified: trunk/docutils/test/functional/expected/latex_cornercases.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_cornercases.tex 2025-08-20 12:04:19 UTC (rev 10215) +++ trunk/docutils/test/functional/expected/latex_cornercases.tex 2025-08-20 12:04:32 UTC (rev 10216) @@ -894,8 +894,8 @@ \hline \end{longtable} +\phantomsection\label{figure-label} \begin{figure} -\phantomsection\label{figure-label} \noindent\makebox[\linewidth][c]{\includegraphics{../../../docs/user/rst/images/biohazard.png}} \caption{Figure with % \label{hypertarget-in-figure-caption}hypertarget in figure caption.} @@ -905,8 +905,8 @@ \end{DUlegend} \end{figure} +\phantomsection\label{image-label} \includegraphics{../../../docs/user/rst/images/biohazard.png} -\phantomsection\label{image-label} See \hyperref[hypertarget-in-plain-text]{hypertarget in plain text}, \hyperref[table-label]{table label}, \hyperref[hypertarget-in-table-title]{hypertarget in table title}, Modified: trunk/docutils/test/functional/expected/latex_memoir.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-20 12:04:19 UTC (rev 10215) +++ trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-20 12:04:32 UTC (rev 10216) @@ -824,8 +824,8 @@ Image with multiple IDs: +\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1} \includegraphics{../../../docs/user/rst/images/biohazard.png} -\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1} A centered image: Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-20 12:04:19 UTC (rev 10215) +++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-20 12:04:32 UTC (rev 10216) @@ -818,8 +818,8 @@ Image with multiple IDs: +\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1} \includegraphics{../../../docs/user/rst/images/biohazard.png} -\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1} A centered image: Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-20 12:04:19 UTC (rev 10215) +++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-20 12:04:32 UTC (rev 10216) @@ -843,8 +843,8 @@ Image with multiple IDs: +\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1} \includegraphics{../../../docs/user/rst/images/biohazard.png} -\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1} A centered image: Modified: trunk/docutils/test/test_writers/test_latex2e.py =================================================================== --- trunk/docutils/test/test_writers/test_latex2e.py 2025-08-20 12:04:19 UTC (rev 10215) +++ trunk/docutils/test/test_writers/test_latex2e.py 2025-08-20 12:04:32 UTC (rev 10216) @@ -133,19 +133,27 @@ [""" .. image:: larch-mini.jpg :target: larch.jpg + :name: the-larch + :class: currently ignored :align: center """, r""" +\phantomsection\label{the-larch} \noindent\makebox[\linewidth][c]{\href{larch.jpg}{\includegraphics{larch-mini.jpg}}} """], ["""\ +.. _fig:larch: + .. figure:: larch-mini.jpg :target: larch.jpg + :name: the-larch The larch """, r""" +\phantomsection\label{fig-larch} \begin{figure} +\phantomsection\label{the-larch} \noindent\makebox[\linewidth][c]{\href{larch.jpg}{\includegraphics{larch-mini.jpg}}} \caption{The larch} \end{figure} @@ -327,6 +335,25 @@ \phantomsection\label{block-target} \DUrole{custom}{\DUrole{paragraph}{Next paragraph.}} """], +# literal block +["""\ +.. class:: cls1 +.. _block1: + +:: + + 1^2_3 +""", +r""" +\phantomsection\label{block1} +\begin{DUclass}{cls1} +\begin{quote} +\begin{alltt} +1^2_3 +\end{alltt} +\end{quote} +\end{DUclass} +"""], # table with IDs and custom + special class values ["""\ .. class:: cls1 @@ -341,9 +368,9 @@ = = """, r""" +\phantomsection\label{label2}\label{label1} \begin{DUclass}{cls2} \begin{DUclass}{cls1} -\phantomsection\label{label2}\label{label1} \begin{longtable*}{ll} Y & N \\ \end{longtable*} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-20 12:04:22
|
Revision: 10215 http://sourceforge.net/p/docutils/code/10215 Author: milde Date: 2025-08-20 12:04:19 +0000 (Wed, 20 Aug 2025) Log Message: ----------- LaTeX writer: set anchor for math-block with target/label. The "hyperref" LaTeX package sets an anchor for hyperlinks to math blocks, if they are numbered. The "math" directive generates unnumbered math blocks. Prepend a `\phantomsection` to manually set an anchor so that hyperlinks go to the right place. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/writers/latex2e/__init__.py trunk/docutils/test/functional/expected/latex_memoir.tex trunk/docutils/test/functional/expected/standalone_rst_latex.tex trunk/docutils/test/functional/expected/standalone_rst_xetex.tex Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-20 09:39:58 UTC (rev 10214) +++ trunk/docutils/HISTORY.rst 2025-08-20 12:04:19 UTC (rev 10215) @@ -41,7 +41,11 @@ - Better error reports for hyperlinks with embedded URI or alias. +* docutils/writers/latex2e/__init__.py + - Prepend ``\phantomsection`` to labelled math-blocks. + + Release 0.22 (2026-07-29) ========================= @@ -189,6 +193,7 @@ * docutils/transforms/references.py + - Make `AnonymousHyperlinks` transform idempotent. - New transform `CitationReferences`. Marks citation_references as resolved if BibTeX is used by the backend (LaTeX). @@ -196,10 +201,6 @@ - Removed `Compound` transform. -* docutils/transforms/references.py - - - Make `AnonymousHyperlinks` transform idempotent. - * docutils/transforms/universal.py - `Messages` transform now also handles "loose" system messages Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 09:39:58 UTC (rev 10214) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:19 UTC (rev 10215) @@ -2725,16 +2725,21 @@ def visit_math_block(self, node) -> None: self.requirements['amsmath'] = r'\usepackage{amsmath}' + math_env = pick_math_environment(node.astext()) + self.out.append('%\n') + if node['ids'] and math_env.endswith('*'): # non-numbered equation + self.out.append('\\phantomsection\n') for cls in node['classes']: self.provide_fallback('inline') - self.out.append(r'\DUrole{%s}{' % cls) - math_env = pick_math_environment(node.astext()) - self.out += [f'%\n\\begin{{{math_env}}}\n', + self.out.append(f'\\DUrole{{{cls}}}{{%\n') + self.out += [f'\\begin{{{math_env}}}\n', node.astext().translate(unichar2tex.uni2tex_table), '\n', *self.ids_to_labels(node, set_anchor=False, newline=True), f'\\end{{{math_env}}}'] - self.out.append('}' * len(node['classes'])) + if node['classes']: + self.out.append('\n') + self.out.append('}' * len(node['classes'])) raise nodes.SkipNode # content already processed def depart_math_block(self, node) -> None: Modified: trunk/docutils/test/functional/expected/latex_memoir.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-20 09:39:58 UTC (rev 10214) +++ trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-20 12:04:19 UTC (rev 10215) @@ -1807,6 +1807,7 @@ The determinant of the matrix % +\phantomsection \begin{equation*} \mathbf{M} = \left(\begin{matrix}a&b\\c&d\end{matrix}\right) \label{eq-m} @@ -1827,6 +1828,7 @@ The Schrödinger equation % +\phantomsection \begin{equation*} i\hbar \frac{\partial }{\partial t}\Psi = \hat{H}\Psi , \label{eq-schrodinger} Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-20 09:39:58 UTC (rev 10214) +++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-20 12:04:19 UTC (rev 10215) @@ -1822,6 +1822,7 @@ The determinant of the matrix % +\phantomsection \begin{equation*} \mathbf{M} = \left(\begin{matrix}a&b\\c&d\end{matrix}\right) \label{eq-m} @@ -1842,6 +1843,7 @@ The Schrödinger equation % +\phantomsection \begin{equation*} i\hbar \frac{\partial }{\partial t}\Psi = \hat{H}\Psi , \label{eq-schrodinger} Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-20 09:39:58 UTC (rev 10214) +++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-20 12:04:19 UTC (rev 10215) @@ -1851,6 +1851,7 @@ The determinant of the matrix % +\phantomsection \begin{equation*} \mathbf{M} = \left(\begin{matrix}a&b\\c&d\end{matrix}\right) \label{eq-m} @@ -1871,6 +1872,7 @@ The Schrödinger equation % +\phantomsection \begin{equation*} i\hbar \frac{\partial }{\partial t}\Psi = \hat{H}\Psi , \label{eq-schrodinger} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-20 09:40:02
|
Revision: 10214 http://sourceforge.net/p/docutils/code/10214 Author: milde Date: 2025-08-20 09:39:58 +0000 (Wed, 20 Aug 2025) Log Message: ----------- Documentation fixes. Fix docstring: `directives.misc.Include.insert_into_input_lines()` returns None. (The empty list is returned by `Include.run()`.) Small edits in the Document Tree Guide. Modified Paths: -------------- trunk/docutils/docs/ref/doctree.rst trunk/docutils/docutils/parsers/rst/directives/misc.py Modified: trunk/docutils/docs/ref/doctree.rst =================================================================== --- trunk/docutils/docs/ref/doctree.rst 2025-08-20 08:32:28 UTC (rev 10213) +++ trunk/docutils/docs/ref/doctree.rst 2025-08-20 09:39:58 UTC (rev 10214) @@ -4385,8 +4385,8 @@ The ``auto`` attribute is used to indicate automatically-numbered `\<footnote>`_, `\<footnote_reference>`_ and `\<title>`_ elements (via the `%auto.att`_ parameter entity). -In <footnote> and <footnote_reference> elements, it also carries information -about the label type: "1": auto-numbered_, "*": auto-symbol_. +In `\<footnote>`_ and `\<footnote_reference>`_ elements, it also carries +information about the label type ("1": auto-numbered_, "*": auto-symbol_). ``backrefs`` @@ -4925,11 +4925,14 @@ The ``title`` attribute is used in the `\<document>`_ element to store the document's *metadata title*. -It is set by the `"title" directive`_ or the `DocTitle transform`_. -This title is typically not part of the rendered document. -It is, for example, used as `HTML <title> element`_ and shown in a -browser's title bar, in a user's history or bookmarks, or in search results. +The attribute is set by the `"title" directive`_ or the +`DocTitle transform`_. It is typically not part of the rendered document +but, for example, used as `HTML <title> element`_ and shown in a +browser's title bar, a user's history or bookmarks, or search results. +Its value may may differ from the *displayed title* which is stored in a +`\<title>`_ element. + .. _HTML <title> element: https://html.spec.whatwg.org/multipage/semantics.html#the-title-element Modified: trunk/docutils/docutils/parsers/rst/directives/misc.py =================================================================== --- trunk/docutils/docutils/parsers/rst/directives/misc.py 2025-08-20 08:32:28 UTC (rev 10213) +++ trunk/docutils/docutils/parsers/rst/directives/misc.py 2025-08-20 09:39:58 UTC (rev 10214) @@ -236,8 +236,6 @@ def insert_into_input_lines(self, text: str) -> None: """Insert file content into the rST input of the calling parser. - Returns an empty list to comply with the API of `Directive.run()`. - Provisional. """ source = self.options['source'] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gr...@us...> - 2025-08-20 08:32:40
|
Revision: 10213 http://sourceforge.net/p/docutils/code/10213 Author: grubert Date: 2025-08-20 08:32:28 +0000 (Wed, 20 Aug 2025) Log Message: ----------- version string 0.23b.dev Modified Paths: -------------- trunk/sandbox/manpage-writer/expected/characters.man trunk/sandbox/manpage-writer/expected/compact_lists.man trunk/sandbox/manpage-writer/expected/docinfo-deu-l_de.man trunk/sandbox/manpage-writer/expected/docinfo-deu-l_en.man trunk/sandbox/manpage-writer/expected/docinfo-deu.man trunk/sandbox/manpage-writer/expected/docinfo-eng-l_de.man trunk/sandbox/manpage-writer/expected/docinfo-eng-l_en.man trunk/sandbox/manpage-writer/expected/docinfo-eng.man trunk/sandbox/manpage-writer/expected/dotted.man trunk/sandbox/manpage-writer/expected/indent.man trunk/sandbox/manpage-writer/expected/man-de.1.man trunk/sandbox/manpage-writer/expected/optionslisttest.man trunk/sandbox/manpage-writer/expected/optionstoo.man trunk/sandbox/manpage-writer/expected/optionstoo.ps trunk/sandbox/manpage-writer/expected/quotes.man trunk/sandbox/manpage-writer/expected/quotes.ps trunk/sandbox/manpage-writer/expected/ref-2025-urue.man trunk/sandbox/manpage-writer/expected/ref-2025.man trunk/sandbox/manpage-writer/expected/references.man trunk/sandbox/manpage-writer/expected/references.ps trunk/sandbox/manpage-writer/expected/refs-urue.man trunk/sandbox/manpage-writer/expected/refs.man trunk/sandbox/manpage-writer/expected/test.man trunk/sandbox/manpage-writer/expected/test.ps trunk/sandbox/manpage-writer/expected-mandoc/characters.man trunk/sandbox/manpage-writer/expected-mandoc/compact_lists.man trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_de.man trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_en.man trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu.man trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_de.man trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_en.man trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng.man trunk/sandbox/manpage-writer/expected-mandoc/dotted.man trunk/sandbox/manpage-writer/expected-mandoc/indent.man trunk/sandbox/manpage-writer/expected-mandoc/man-de.1.man trunk/sandbox/manpage-writer/expected-mandoc/optionslisttest.man trunk/sandbox/manpage-writer/expected-mandoc/optionstoo.man trunk/sandbox/manpage-writer/expected-mandoc/quotes.man trunk/sandbox/manpage-writer/expected-mandoc/references.man trunk/sandbox/manpage-writer/expected-mandoc/refs-urue.man trunk/sandbox/manpage-writer/expected-mandoc/test.man Modified: trunk/sandbox/manpage-writer/expected/characters.man =================================================================== --- trunk/sandbox/manpage-writer/expected/characters.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/characters.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/compact_lists.man =================================================================== --- trunk/sandbox/manpage-writer/expected/compact_lists.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/compact_lists.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/docinfo-deu-l_de.man =================================================================== --- trunk/sandbox/manpage-writer/expected/docinfo-deu-l_de.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/docinfo-deu-l_de.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/docinfo-deu-l_en.man =================================================================== --- trunk/sandbox/manpage-writer/expected/docinfo-deu-l_en.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/docinfo-deu-l_en.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/docinfo-deu.man =================================================================== --- trunk/sandbox/manpage-writer/expected/docinfo-deu.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/docinfo-deu.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/docinfo-eng-l_de.man =================================================================== --- trunk/sandbox/manpage-writer/expected/docinfo-eng-l_de.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/docinfo-eng-l_de.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/docinfo-eng-l_en.man =================================================================== --- trunk/sandbox/manpage-writer/expected/docinfo-eng-l_en.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/docinfo-eng-l_en.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/docinfo-eng.man =================================================================== --- trunk/sandbox/manpage-writer/expected/docinfo-eng.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/docinfo-eng.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/dotted.man =================================================================== --- trunk/sandbox/manpage-writer/expected/dotted.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/dotted.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/indent.man =================================================================== --- trunk/sandbox/manpage-writer/expected/indent.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/indent.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/man-de.1.man =================================================================== --- trunk/sandbox/manpage-writer/expected/man-de.1.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/man-de.1.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ '\" t .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/optionslisttest.man =================================================================== --- trunk/sandbox/manpage-writer/expected/optionslisttest.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/optionslisttest.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/optionstoo.man =================================================================== --- trunk/sandbox/manpage-writer/expected/optionstoo.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/optionstoo.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/optionstoo.ps =================================================================== --- trunk/sandbox/manpage-writer/expected/optionstoo.ps 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/optionstoo.ps 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ %!PS-Adobe-3.0 %%Creator: groff version 1.23.0 -%%CreationDate: Thu Sep 28 08:45:02 2023 +%%CreationDate: Tue Jul 8 09:52:34 2025 %%DocumentNeededResources: font Times-Italic %%+ font Times-Roman %%+ font Times-Bold Modified: trunk/sandbox/manpage-writer/expected/quotes.man =================================================================== --- trunk/sandbox/manpage-writer/expected/quotes.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/quotes.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/quotes.ps =================================================================== --- trunk/sandbox/manpage-writer/expected/quotes.ps 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/quotes.ps 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ %!PS-Adobe-3.0 %%Creator: groff version 1.23.0 -%%CreationDate: Mon Aug 23 15:17:01 2021 +%%CreationDate: Tue Jul 8 09:52:34 2025 %%DocumentNeededResources: font Times-Roman %%+ font Times-Bold %%DocumentSuppliedResources: procset grops 1.23 0 Modified: trunk/sandbox/manpage-writer/expected/ref-2025-urue.man =================================================================== --- trunk/sandbox/manpage-writer/expected/ref-2025-urue.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/ref-2025-urue.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/ref-2025.man =================================================================== --- trunk/sandbox/manpage-writer/expected/ref-2025.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/ref-2025.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/references.man =================================================================== --- trunk/sandbox/manpage-writer/expected/references.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/references.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/references.ps =================================================================== --- trunk/sandbox/manpage-writer/expected/references.ps 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/references.ps 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ %!PS-Adobe-3.0 %%Creator: groff version 1.23.0 -%%CreationDate: Tue Jun 10 19:58:59 2025 +%%CreationDate: Tue Jul 8 09:52:34 2025 %%DocumentNeededResources: font Times-Italic %%+ font Times-Roman %%+ font Times-Bold Modified: trunk/sandbox/manpage-writer/expected/refs-urue.man =================================================================== --- trunk/sandbox/manpage-writer/expected/refs-urue.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/refs-urue.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/refs.man =================================================================== --- trunk/sandbox/manpage-writer/expected/refs.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/refs.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/test.man =================================================================== --- trunk/sandbox/manpage-writer/expected/test.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/test.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ '\" t .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected/test.ps =================================================================== --- trunk/sandbox/manpage-writer/expected/test.ps 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected/test.ps 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ %!PS-Adobe-3.0 %%Creator: groff version 1.23.0 -%%CreationDate: Sat Mar 16 15:50:40 2024 +%%CreationDate: Tue Jul 8 09:52:34 2025 %%DocumentNeededResources: font Times-Italic %%+ font Times-Roman %%+ font Times-Bold Modified: trunk/sandbox/manpage-writer/expected-mandoc/characters.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/characters.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/characters.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/compact_lists.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/compact_lists.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/compact_lists.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_de.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_de.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_de.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_en.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_en.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu-l_en.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/docinfo-deu.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_de.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_de.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_de.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_en.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_en.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng-l_en.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/docinfo-eng.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/dotted.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/dotted.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/dotted.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/indent.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/indent.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/indent.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/man-de.1.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/man-de.1.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/man-de.1.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ '\" t .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/optionslisttest.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/optionslisttest.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/optionslisttest.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/optionstoo.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/optionstoo.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/optionstoo.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/quotes.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/quotes.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/quotes.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/references.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/references.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/references.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/refs-urue.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/refs-urue.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/refs-urue.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,5 +1,5 @@ .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 Modified: trunk/sandbox/manpage-writer/expected-mandoc/test.man =================================================================== --- trunk/sandbox/manpage-writer/expected-mandoc/test.man 2025-08-20 08:31:29 UTC (rev 10212) +++ trunk/sandbox/manpage-writer/expected-mandoc/test.man 2025-08-20 08:32:28 UTC (rev 10213) @@ -1,6 +1,6 @@ '\" t .\" Man page generated from reStructuredText -.\" by the Docutils 0.22rc6.dev manpage writer. +.\" by the Docutils 0.23b.dev manpage writer. . . .nr rst2man-indent-level 0 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gr...@us...> - 2025-08-20 08:31:35
|
Revision: 10212 http://sourceforge.net/p/docutils/code/10212 Author: grubert Date: 2025-08-20 08:31:29 +0000 (Wed, 20 Aug 2025) Log Message: ----------- set path to docutils dev code Modified Paths: -------------- trunk/sandbox/manpage-writer/runtest trunk/sandbox/manpage-writer/runtest-mandoc Modified: trunk/sandbox/manpage-writer/runtest =================================================================== --- trunk/sandbox/manpage-writer/runtest 2025-08-19 20:28:48 UTC (rev 10211) +++ trunk/sandbox/manpage-writer/runtest 2025-08-20 08:31:29 UTC (rev 10212) @@ -7,6 +7,8 @@ # Date: $Date$ # Copyright: This script has been placed in the public domain. +export PYTHONPATH=../../docutils + IN_DIR=input OUT_DIR=output EXP_DIR=expected Modified: trunk/sandbox/manpage-writer/runtest-mandoc =================================================================== --- trunk/sandbox/manpage-writer/runtest-mandoc 2025-08-19 20:28:48 UTC (rev 10211) +++ trunk/sandbox/manpage-writer/runtest-mandoc 2025-08-20 08:31:29 UTC (rev 10212) @@ -24,6 +24,8 @@ # Supported values for the output argument are ascii, html, the default # of locale, man, markdown, pdf, ps, tree, and utf8. +export PYTHONPATH=../../docutils + IN_DIR=input OUT_DIR=output-mandoc EXP_DIR=expected-mandoc This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-19 20:28:50
|
Revision: 10211 http://sourceforge.net/p/docutils/code/10211 Author: milde Date: 2025-08-19 20:28:48 +0000 (Tue, 19 Aug 2025) Log Message: ----------- Better error reports for hyperlinks with embedded URI or alias. Check and report for common typos when an undefined referencname contains a `<` or `>`. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/transforms/references.py trunk/docutils/test/test_transforms/test_hyperlinks.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-19 18:38:11 UTC (rev 10210) +++ trunk/docutils/HISTORY.rst 2025-08-19 20:28:48 UTC (rev 10211) @@ -35,9 +35,13 @@ - Better error messages for grid table markup errors (bug #504), based on patch #214 by Jynn Nelson. -__ RELEASE-NOTES.html#nested-parsing + __ RELEASE-NOTES.html#nested-parsing +* docutils/transforms/references.py + - Better error reports for hyperlinks with embedded URI or alias. + + Release 0.22 (2026-07-29) ========================= Modified: trunk/docutils/docutils/transforms/references.py =================================================================== --- trunk/docutils/docutils/transforms/references.py 2025-08-19 18:38:11 UTC (rev 10210) +++ trunk/docutils/docutils/transforms/references.py 2025-08-19 20:28:48 UTC (rev 10211) @@ -955,11 +955,29 @@ if refname in self.document.nameids: msg = self.document.reporter.error( 'Duplicate target name, cannot be used as a unique ' - 'reference: "%s".' % (node['refname']), base_node=node) + f'reference: "{refname}".', base_node=node) else: + if '<' in refname or '>' in refname: + hint = 'Did you want to embed a URI or alias?' + if '<' not in refname: + hint += '\nOpening bracket missing.' + elif ' <' not in refname: + hint += ('\nThe embedded reference must be preceded' + ' by whitespace.') + if '>' not in refname: + hint += '\nClosing bracket missing.' + elif not refname.endswith('>'): + hint += ('\nThe embedded reference must be the last text' + ' before the end string.') + if '< ' in refname or ' >' in refname: + hint += ('\nWhitespace around the embedded reference' + ' is not allowed.') + details = [nodes.paragraph('', hint)] + else: + details = [] msg = self.document.reporter.error( - f'Unknown target name: "{node["refname"]}".', - base_node=node) + f'Unknown target name: "{refname}".', + *details, base_node=node) msgid = self.document.set_id(msg) prb = nodes.problematic(node.rawsource, node.rawsource, refid=msgid) try: Modified: trunk/docutils/test/test_transforms/test_hyperlinks.py =================================================================== --- trunk/docutils/test/test_transforms/test_hyperlinks.py 2025-08-19 18:38:11 UTC (rev 10210) +++ trunk/docutils/test/test_transforms/test_hyperlinks.py 2025-08-19 20:28:48 UTC (rev 10211) @@ -432,6 +432,91 @@ . """], ["""\ +`link <address >`_ +""", +"""\ +<document source="test data"> + <paragraph> + <problematic ids="problematic-1" refid="system-message-1"> + `link <address >`_ + <system_message backrefs="problematic-1" ids="system-message-1" level="3" line="1" source="test data" type="ERROR"> + <paragraph> + Unknown target name: "link <address >". + <paragraph> + Did you want to embed a URI or alias? + Whitespace around the embedded reference is not allowed. +"""], +["""\ +`link < address>`_ +""", +"""\ +<document source="test data"> + <paragraph> + <problematic ids="problematic-1" refid="system-message-1"> + `link < address>`_ + <system_message backrefs="problematic-1" ids="system-message-1" level="3" line="1" source="test data" type="ERROR"> + <paragraph> + Unknown target name: "link < address>". + <paragraph> + Did you want to embed a URI or alias? + Whitespace around the embedded reference is not allowed. +"""], +["""\ +`link <address> e`_ +""", +"""\ +<document source="test data"> + <paragraph> + <problematic ids="problematic-1" refid="system-message-1"> + `link <address> e`_ + <system_message backrefs="problematic-1" ids="system-message-1" level="3" line="1" source="test data" type="ERROR"> + <paragraph> + Unknown target name: "link <address> e". + <paragraph> + Did you want to embed a URI or alias? + The embedded reference must be the last text before the end string. +"""], +["""\ +`link<address>`_ +""", +"""\ +<document source="test data"> + <paragraph> + <problematic ids="problematic-1" refid="system-message-1"> + `link<address>`_ + <system_message backrefs="problematic-1" ids="system-message-1" level="3" line="1" source="test data" type="ERROR"> + <paragraph> + Unknown target name: "link<address>". + <paragraph> + Did you want to embed a URI or alias? + The embedded reference must be preceded by whitespace. +"""], +["""\ +`link <address`_ +`link address>`_ +""", +"""\ +<document source="test data"> + <paragraph> + <problematic ids="problematic-1" refid="system-message-1"> + `link <address`_ + \n\ + <problematic ids="problematic-2" refid="system-message-2"> + `link address>`_ + <system_message backrefs="problematic-1" ids="system-message-1" level="3" line="1" source="test data" type="ERROR"> + <paragraph> + Unknown target name: "link <address". + <paragraph> + Did you want to embed a URI or alias? + Closing bracket missing. + <system_message backrefs="problematic-2" ids="system-message-2" level="3" line="1" source="test data" type="ERROR"> + <paragraph> + Unknown target name: "link address>". + <paragraph> + Did you want to embed a URI or alias? + Opening bracket missing. +"""], +["""\ Hyperlinks with angle-bracketed text need escaping. See `Element \\<a>`_, `Element <b\\>`_, and `Element <c>\\ `_. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-19 18:38:14
|
Revision: 10210 http://sourceforge.net/p/docutils/code/10210 Author: milde Date: 2025-08-19 18:38:11 +0000 (Tue, 19 Aug 2025) Log Message: ----------- latex writer: Fix/simplify footnote handling. In Docutils, reference names for footnotes, citations, and hyperlinks use the same namespace. Change the definitions of `\DUfootnotemark` and `\DUfootnotetext` (use `\label` and `\hyperref` instead of `\hypertarget` and `\hyperlink`) to get corresponding behaviour in LaTeX. This allows to remove the additional creation of a `\label` for footnotes with "autonumber-label". Adapt tests. Modified Paths: -------------- trunk/docutils/docutils/writers/latex2e/__init__.py trunk/docutils/docutils/writers/latex2e/docutils.sty trunk/docutils/test/functional/expected/latex_leavevmode.tex trunk/docutils/test/functional/expected/latex_literal_block.tex trunk/docutils/test/functional/expected/latex_literal_block_fancyvrb.tex trunk/docutils/test/functional/expected/latex_literal_block_listings.tex trunk/docutils/test/functional/expected/latex_literal_block_verbatim.tex trunk/docutils/test/functional/expected/latex_literal_block_verbatimtab.tex trunk/docutils/test/functional/expected/latex_memoir.tex trunk/docutils/test/functional/expected/length_units_latex.tex trunk/docutils/test/functional/expected/standalone_rst_latex.tex trunk/docutils/test/functional/expected/standalone_rst_xetex.tex trunk/docutils/test/test_writers/test_latex2e.py trunk/docutils/test/test_writers/test_latex2e_parts.py Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-19 18:38:11 UTC (rev 10210) @@ -1359,7 +1359,7 @@ else: # require a minimal version: self.fallbacks['_docutils.sty'] = ( - r'\usepackage{docutils}[2024-09-24]') + r'\usepackage{docutils}[2025-08-06]') self.stylesheet = [self.stylesheet_call(path) for path in stylesheet_list] @@ -2335,9 +2335,6 @@ num = '[%s]' % num self.out.append('%%\n\\DUfootnotetext{%s}{%s}{%s}{' % (node['ids'][0], backref, self.encode(num))) - if node['ids'] == [nodes.make_id(n) for n in node['names']]: - # autonumber-label: create anchor - self.out += self.ids_to_labels(node) # prevent spurious whitespace if footnote starts with paragraph: if len(node) > 1 and isinstance(node[1], nodes.paragraph): self.out.append('%') Modified: trunk/docutils/docutils/writers/latex2e/docutils.sty =================================================================== --- trunk/docutils/docutils/writers/latex2e/docutils.sty 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/docutils/writers/latex2e/docutils.sty 2025-08-19 18:38:11 UTC (rev 10210) @@ -22,7 +22,7 @@ \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{docutils} - [2024-09-24 macros for Docutils LaTeX output] + [2025-08-06 macros for Docutils LaTeX output] % Helpers % ------- @@ -124,15 +124,15 @@ % footnotes:: -% numerical or symbol footnotes with hyperlinks and backlinks +% numbered or symbol footnotes with hyperlinks and backlinks \providecommand*{\DUfootnotemark}[3]{% \raisebox{1em}{\hypertarget{#1}{}}% - \hyperlink{#2}{\textsuperscript{#3}}% + \hyperref[#2]{\textsuperscript{#3}}% } \providecommand{\DUfootnotetext}[4]{% \begingroup% \renewcommand{\thefootnote}{% - \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\phantomsection\protect\label{#1} \protect\hyperlink{#2}{#3}}% \footnotetext{#4}% \endgroup% Modified: trunk/docutils/test/functional/expected/latex_leavevmode.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_leavevmode.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/latex_leavevmode.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -74,15 +74,15 @@ {\enddescription\endquote} \fi -% numerical or symbol footnotes with hyperlinks and backlinks +% numbered or symbol footnotes with hyperlinks and backlinks \providecommand*{\DUfootnotemark}[3]{% \raisebox{1em}{\hypertarget{#1}{}}% - \hyperlink{#2}{\textsuperscript{#3}}% + \hyperref[#2]{\textsuperscript{#3}}% } \providecommand{\DUfootnotetext}[4]{% \begingroup% \renewcommand{\thefootnote}{% - \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\phantomsection\protect\label{#1} \protect\hyperlink{#2}{#3}}% \footnotetext{#4}% \endgroup% @@ -385,7 +385,7 @@ \end{DUclass} \item[{Footnote}] % -\DUfootnotetext{f1}{f1}{1}{\phantomsection\label{f1}% +\DUfootnotetext{f1}{f1}{1}{% This footnote will move to the bottom of the page. } Modified: trunk/docutils/test/functional/expected/latex_literal_block.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_literal_block.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/latex_literal_block.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -19,7 +19,7 @@ %%% User specified packages and stylesheets %%% Fallback definitions for Docutils-specific commands -\usepackage{docutils}[2024-09-24] +\usepackage{docutils}[2025-08-06] % character width in monospaced font \newlength{\ttemwidth} Modified: trunk/docutils/test/functional/expected/latex_literal_block_fancyvrb.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_literal_block_fancyvrb.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/latex_literal_block_fancyvrb.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -19,7 +19,7 @@ %%% User specified packages and stylesheets %%% Fallback definitions for Docutils-specific commands -\usepackage{docutils}[2024-09-24] +\usepackage{docutils}[2025-08-06] % character width in monospaced font \newlength{\ttemwidth} Modified: trunk/docutils/test/functional/expected/latex_literal_block_listings.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_literal_block_listings.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/latex_literal_block_listings.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -25,7 +25,7 @@ %%% User specified packages and stylesheets %%% Fallback definitions for Docutils-specific commands -\usepackage{docutils}[2024-09-24] +\usepackage{docutils}[2025-08-06] % character width in monospaced font \newlength{\ttemwidth} Modified: trunk/docutils/test/functional/expected/latex_literal_block_verbatim.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_literal_block_verbatim.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/latex_literal_block_verbatim.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -18,7 +18,7 @@ %%% User specified packages and stylesheets %%% Fallback definitions for Docutils-specific commands -\usepackage{docutils}[2024-09-24] +\usepackage{docutils}[2025-08-06] % character width in monospaced font \newlength{\ttemwidth} Modified: trunk/docutils/test/functional/expected/latex_literal_block_verbatimtab.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_literal_block_verbatimtab.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/latex_literal_block_verbatimtab.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -19,7 +19,7 @@ %%% User specified packages and stylesheets %%% Fallback definitions for Docutils-specific commands -\usepackage{docutils}[2024-09-24] +\usepackage{docutils}[2025-08-06] % character width in monospaced font \newlength{\ttemwidth} Modified: trunk/docutils/test/functional/expected/latex_memoir.tex =================================================================== --- trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -88,15 +88,15 @@ {\enddescription\endquote} \fi -% numerical or symbol footnotes with hyperlinks and backlinks +% numbered or symbol footnotes with hyperlinks and backlinks \providecommand*{\DUfootnotemark}[3]{% \raisebox{1em}{\hypertarget{#1}{}}% - \hyperlink{#2}{\textsuperscript{#3}}% + \hyperref[#2]{\textsuperscript{#3}}% } \providecommand{\DUfootnotetext}[4]{% \begingroup% \renewcommand{\thefootnote}{% - \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\phantomsection\protect\label{#1} \protect\hyperlink{#2}{#3}}% \footnotetext{#4}% \endgroup% @@ -709,7 +709,7 @@ This is the footnote's second paragraph. } % -\DUfootnotetext{label}{footnote-reference-3}{2}{\phantomsection\label{label}% +\DUfootnotetext{label}{footnote-reference-3}{2}{% Footnotes may be numbered, either manually (as in\DUfootnotemark{footnote-reference-5}{footnote-1}{1}) or automatically using a \textquotedbl{}\#\textquotedbl{}-prefixed label. This footnote has a label so it can be referred to from multiple places, both as a Modified: trunk/docutils/test/functional/expected/length_units_latex.tex =================================================================== --- trunk/docutils/test/functional/expected/length_units_latex.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/length_units_latex.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -22,7 +22,7 @@ \usepackage{nohyperref} %%% Fallback definitions for Docutils-specific commands -\usepackage{docutils}[2024-09-24] +\usepackage{docutils}[2025-08-06] \ifdefined\DUchdimen % lengh unit "ch": width of a zero char \else Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -92,15 +92,15 @@ {\enddescription\endquote} \fi -% numerical or symbol footnotes with hyperlinks and backlinks +% numbered or symbol footnotes with hyperlinks and backlinks \providecommand*{\DUfootnotemark}[3]{% \raisebox{1em}{\hypertarget{#1}{}}% - \hyperlink{#2}{\textsuperscript{#3}}% + \hyperref[#2]{\textsuperscript{#3}}% } \providecommand{\DUfootnotetext}[4]{% \begingroup% \renewcommand{\thefootnote}{% - \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\phantomsection\protect\label{#1} \protect\hyperlink{#2}{#3}}% \footnotetext{#4}% \endgroup% @@ -703,7 +703,7 @@ This is the footnote’s second paragraph. } % -\DUfootnotetext{label}{footnote-reference-3}{2}{\phantomsection\label{label}% +\DUfootnotetext{label}{footnote-reference-3}{2}{% Footnotes may be numbered, either manually (as in\DUfootnotemark{footnote-reference-5}{footnote-1}{1}) or automatically using a “\#”-prefixed label. This footnote has a label so it can be referred to from multiple places, both as a Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-19 18:38:11 UTC (rev 10210) @@ -33,7 +33,7 @@ %%% User specified packages and stylesheets %%% Fallback definitions for Docutils-specific commands -\usepackage{docutils}[2024-09-24] +\usepackage{docutils}[2025-08-06] \newcounter{enumv} \DUprovidelength{\pdfpxdimen}{1bp} @@ -695,7 +695,7 @@ This is the footnote’s second paragraph. } % -\DUfootnotetext{label}{footnote-reference-3}{2}{\phantomsection\label{label}% +\DUfootnotetext{label}{footnote-reference-3}{2}{% Footnotes may be numbered, either manually (as in\DUfootnotemark{footnote-reference-5}{footnote-1}{1}) or automatically using a “\#”-prefixed label. This footnote has a label so it can be referred to from multiple places, both as a Modified: trunk/docutils/test/test_writers/test_latex2e.py =================================================================== --- trunk/docutils/test/test_writers/test_latex2e.py 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/test_writers/test_latex2e.py 2025-08-19 18:38:11 UTC (rev 10210) @@ -7,7 +7,7 @@ """ Tests for latex2e writer. -This module test only the "body" part of the output. +This module tests only the "body" part of the output. For tests of constructs that change the "head", see test-latex2e_parts.py. """ @@ -377,7 +377,7 @@ Just a test citation [my_cite2006]_. .. [my_cite2006] - The underscore is mishandled. + Watch the underscore! """, r""" Just a test citation \cite{my_cite2006}. @@ -384,7 +384,7 @@ \begin{thebibliography}{my\_cite2006} \bibitem[my\_cite2006]{my_cite2006}{ -The underscore is mishandled. +Watch the underscore! } \end{thebibliography} """], @@ -499,6 +499,161 @@ """], ]) +samples['docutils-footnotes'] = ({}, [ +# different markup variants +[r""" +Paragraphs contain text and may contain footnote references (manually +numbered [1]_, anonymous auto-numbered [#]_, labeled auto-numbered +[#label]_, or symbolic [*]_). +.. [1] A footnote. + +.. [#label] Footnotes may be numbered, either manually or + automatically using a "#"-prefixed label. This footnote has a + label so it can be referred to from multiple places, both as a + footnote reference ([#label]_) and as a `hyperlink reference`__. + + __ label_ + +.. [#] This footnote is numbered automatically and anonymously using a + label of "#" only. + +.. [*] Footnotes may also use symbols, specified with a "*" label. +""", + r""" +Paragraphs contain text and may contain footnote references (manually +numbered\DUfootnotemark{footnote-reference-1}{footnote-1}{1}, anonymous auto-numbered\DUfootnotemark{footnote-reference-2}{footnote-2}{3}, labeled auto-numbered\DUfootnotemark{footnote-reference-3}{label}{2}, or symbolic\DUfootnotemark{footnote-reference-4}{footnote-3}{*}). +% +\DUfootnotetext{footnote-1}{footnote-reference-1}{1}{% +A footnote. +} +% +\DUfootnotetext{label}{footnote-reference-3}{2}{% +Footnotes may be numbered, either manually or +automatically using a \textquotedbl{}\#\textquotedbl{}-prefixed label. This footnote has a +label so it can be referred to from multiple places, both as a +footnote reference (\DUfootnotemark{footnote-reference-5}{label}{2}) and as a \hyperref[label]{hyperlink reference}. +} +% +\DUfootnotetext{footnote-2}{footnote-reference-2}{3}{% +This footnote is numbered automatically and anonymously using a +label of \textquotedbl{}\#\textquotedbl{} only. +} +% +\DUfootnotetext{footnote-3}{footnote-reference-4}{*}{% +Footnotes may also use symbols, specified with a \textquotedbl{}*\textquotedbl{} label. +} +"""], +# nested footnotes +["""\ +It's possible to produce nested footnotes in LaTeX. [#]_ + +.. [#] It takes some work, though. [#]_ +.. [#] And don't even get me started on how tricky recursive footnotes + would be. +""", +r""" +It's possible to produce nested footnotes in LaTeX.\DUfootnotemark{footnote-reference-1}{footnote-1}{1} +% +\DUfootnotetext{footnote-1}{footnote-reference-1}{1}{% +It takes some work, though.\DUfootnotemark{footnote-reference-2}{footnote-2}{2} +} +% +\DUfootnotetext{footnote-2}{footnote-reference-2}{2}{% +And don't even get me started on how tricky recursive footnotes +would be. +} +"""], +# chained footnotes +["""\ +It's possible to produce chained footnotes in LaTeX. [#]_ + +.. [#] They're just a special case of nested footnotes. [#]_ +.. [#] A nested footnote is a footnote on a footnote. [#]_ +.. [#] This is a footnote on a footnote on a footnote. +""", +r""" +It's possible to produce chained footnotes in LaTeX.\DUfootnotemark{footnote-reference-1}{footnote-1}{1} +% +\DUfootnotetext{footnote-1}{footnote-reference-1}{1}{% +They're just a special case of nested footnotes.\DUfootnotemark{footnote-reference-2}{footnote-2}{2} +} +% +\DUfootnotetext{footnote-2}{footnote-reference-2}{2}{% +A nested footnote is a footnote on a footnote.\DUfootnotemark{footnote-reference-3}{footnote-3}{3} +} +% +\DUfootnotetext{footnote-3}{footnote-reference-3}{3}{% +This is a footnote on a footnote on a footnote. +} +"""], +# multi-nested footnotes +["""\ +A footnote [#multi]_ + +.. [#multi] This is a footnote with nested [#]_ footnotes. [#]_ [#]_ + +.. [#] First nested [#]_ footnote. [#]_ + +.. [#] Second nested footnote. [#]_ + +.. [#] Third nested footnote. + +.. [#] First double-nested footnote. + +.. [#] Second double-nested footnote. + +.. [#] First triple-nested footnote. + +.. [#] Not nested, referenced after footnote text. + +Ref to a new footnote [#]_ + +A second reference to the first one [#multi]_. +We can also write a hyperlink to multi_. +""", +r""" +A footnote\DUfootnotemark{footnote-reference-1}{multi}{1} +% +\DUfootnotetext{multi}{footnote-reference-1}{1}{% +This is a footnote with nested\DUfootnotemark{footnote-reference-2}{footnote-1}{2} footnotes.\DUfootnotemark{footnote-reference-3}{footnote-2}{3}\DUfootnotemark{footnote-reference-4}{footnote-3}{4} +} +% +\DUfootnotetext{footnote-1}{footnote-reference-2}{2}{% +First nested\DUfootnotemark{footnote-reference-5}{footnote-4}{5} footnote.\DUfootnotemark{footnote-reference-6}{footnote-5}{6} +} +% +\DUfootnotetext{footnote-2}{footnote-reference-3}{3}{% +Second nested footnote.\DUfootnotemark{footnote-reference-7}{footnote-6}{7} +} +% +\DUfootnotetext{footnote-3}{footnote-reference-4}{4}{% +Third nested footnote. +} +% +\DUfootnotetext{footnote-4}{footnote-reference-5}{5}{% +First double-nested footnote. +} +% +\DUfootnotetext{footnote-5}{footnote-reference-6}{6}{% +Second double-nested footnote. +} +% +\DUfootnotetext{footnote-6}{footnote-reference-7}{7}{% +First triple-nested footnote. +} +% +\DUfootnotetext{footnote-7}{footnote-reference-8}{8}{% +Not nested, referenced after footnote text. +} + +Ref to a new footnote\DUfootnotemark{footnote-reference-8}{footnote-7}{8} + +A second reference to the first one\DUfootnotemark{footnote-reference-9}{multi}{1}. +We can also write a hyperlink to \hyperref[multi]{multi}. +"""], +]) + + if __name__ == '__main__': unittest.main() Modified: trunk/docutils/test/test_writers/test_latex2e_parts.py =================================================================== --- trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-19 18:37:57 UTC (rev 10209) +++ trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-19 18:38:11 UTC (rev 10210) @@ -191,15 +191,15 @@ } """, 'fallbacks': r""" -% numerical or symbol footnotes with hyperlinks and backlinks +% numbered or symbol footnotes with hyperlinks and backlinks \providecommand*{\DUfootnotemark}[3]{% \raisebox{1em}{\hypertarget{#1}{}}% - \hyperlink{#2}{\textsuperscript{#3}}% + \hyperref[#2]{\textsuperscript{#3}}% } \providecommand{\DUfootnotetext}[4]{% \begingroup% \renewcommand{\thefootnote}{% - \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\phantomsection\protect\label{#1} \protect\hyperlink{#2}{#3}}% \footnotetext{#4}% \endgroup% This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-19 18:38:00
|
Revision: 10209 http://sourceforge.net/p/docutils/code/10209 Author: milde Date: 2025-08-19 18:37:57 +0000 (Tue, 19 Aug 2025) Log Message: ----------- Use True/False instead of 1/0 for boolean `blank_finish`. Modified Paths: -------------- trunk/docutils/docutils/parsers/rst/states.py trunk/docutils/docutils/statemachine.py Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-19 18:37:49 UTC (rev 10208) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-19 18:37:57 UTC (rev 10209) @@ -1648,7 +1648,7 @@ self.state_machine.input_lines[offset:], input_offset=self.state_machine.abs_line_offset() + 1, node=block, initial_state='LineBlock', - blank_finish=0) + blank_finish=False) self.goto_line(new_line_offset) if not blank_finish: self.parent += self.reporter.warning( @@ -1743,7 +1743,7 @@ def isolate_grid_table(self): messages = [] - blank_finish = 1 + blank_finish = True try: block = self.state_machine.get_text_block(flush_left=True) except statemachine.UnexpectedIndentationError as err: @@ -1750,7 +1750,7 @@ block, src, srcline = err.args messages.append(self.reporter.error('Unexpected indentation.', source=src, line=srcline)) - blank_finish = 0 + blank_finish = False block.disconnect() # for East Asian chars: block.pad_double_width(self.double_width_pad_char) @@ -1758,7 +1758,7 @@ for i in range(len(block)): block[i] = block[i].strip() if block[i][0] not in '+|': # check left edge - blank_finish = 0 + blank_finish = False self.state_machine.previous_line(len(block) - i) del block[i:] break @@ -1768,7 +1768,7 @@ if self.grid_table_top_pat.match(block[i]): self.state_machine.previous_line(len(block) - i + 1) del block[i+1:] - blank_finish = 0 + blank_finish = False break else: detail = 'Bottom border missing or corrupt.' Modified: trunk/docutils/docutils/statemachine.py =================================================================== --- trunk/docutils/docutils/statemachine.py 2025-08-19 18:37:49 UTC (rev 10208) +++ trunk/docutils/docutils/statemachine.py 2025-08-19 18:37:57 UTC (rev 10209) @@ -1406,7 +1406,7 @@ stripped = line.lstrip() if not stripped: # blank line if until_blank: - blank_finish = 1 + blank_finish = True break elif block_indent is None: line_indent = len(line) - len(stripped) @@ -1416,7 +1416,7 @@ indent = min(indent, line_indent) end += 1 else: - blank_finish = 1 # block ends at end of lines + blank_finish = True # block ends at end of lines block = self[start:end] if first_indent is not None and block: block.data[0] = block.data[0][first_indent:] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-19 18:37:52
|
Revision: 10208 http://sourceforge.net/p/docutils/code/10208 Author: milde Date: 2025-08-19 18:37:49 +0000 (Tue, 19 Aug 2025) Log Message: ----------- Better error reporting for table markup. Based on [patches:#214] by Jynn Nelson. Solves [bugs:#504]. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/parsers/rst/states.py trunk/docutils/test/test_parsers/test_rst/test_tables.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-19 17:03:49 UTC (rev 10207) +++ trunk/docutils/HISTORY.rst 2025-08-19 18:37:49 UTC (rev 10208) @@ -32,6 +32,8 @@ - Ensure new "current node" is valid when switching section level (cf. bugs #508 and #509). - Use a `separate title style hierarchy for nested parsing`__. + - Better error messages for grid table markup errors (bug #504), + based on patch #214 by Jynn Nelson. __ RELEASE-NOTES.html#nested-parsing Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-19 17:03:49 UTC (rev 10207) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-19 18:37:49 UTC (rev 10208) @@ -1763,19 +1763,21 @@ del block[i:] break if not self.grid_table_top_pat.match(block[-1]): # find bottom - blank_finish = 0 # from second-last to third line of table: for i in range(len(block) - 2, 1, -1): if self.grid_table_top_pat.match(block[i]): self.state_machine.previous_line(len(block) - i + 1) del block[i+1:] + blank_finish = 0 break else: - messages.extend(self.malformed_table(block)) + detail = 'Bottom border missing or corrupt.' + messages.extend(self.malformed_table(block, detail, i)) return [], messages, blank_finish for i in range(len(block)): # check right edge if len(block[i]) != width or block[i][-1] not in '+|': - messages.extend(self.malformed_table(block)) + detail = 'Right border not aligned or missing.' + messages.extend(self.malformed_table(block, detail, i)) return [], messages, blank_finish return block, messages, blank_finish @@ -1795,8 +1797,8 @@ if len(line.strip()) != toplen: self.state_machine.next_line(i - start) messages = self.malformed_table( - lines[start:i+1], 'Bottom/header table border does ' - 'not match top border.') + lines[start:i+1], 'Bottom border or header rule does ' + 'not match top border.', i-start) return [], messages, i == limit or not lines[i+1].strip() found += 1 found_at = i @@ -1805,17 +1807,16 @@ break i += 1 else: # reached end of input_lines + details = 'No bottom table border found' if found: - extra = ' or no blank line after table bottom' + details += ' or no blank line after table bottom' self.state_machine.next_line(found_at - start) block = lines[start:found_at+1] else: - extra = '' self.state_machine.next_line(i - start - 1) block = lines[start:] - messages = self.malformed_table( - block, 'No bottom table border found%s.' % extra) - return [], messages, not extra + messages = self.malformed_table(block, details + '.') + return [], messages, not found self.state_machine.next_line(end - start) block = lines[start:end+1] # for East Asian chars: Modified: trunk/docutils/test/test_parsers/test_rst/test_tables.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_tables.py 2025-08-19 17:03:49 UTC (rev 10207) +++ trunk/docutils/test/test_parsers/test_rst/test_tables.py 2025-08-19 18:37:49 UTC (rev 10208) @@ -86,20 +86,89 @@ """], ["""\ +-----------------------+ -| A malformed table. | +| A misaligned table. | +-----------------------+ + ++-----------------------+ +| Right border missing. ++-----------------------+ """, """\ <document source="test data"> - <system_message level="3" line="1" source="test data" type="ERROR"> + <system_message level="3" line="2" source="test data" type="ERROR"> <paragraph> Malformed table. + Right border not aligned or missing. <literal_block xml:space="preserve"> +-----------------------+ - | A malformed table. | + | A misaligned table. | +-----------------------+ + <system_message level="3" line="6" source="test data" type="ERROR"> + <paragraph> + Malformed table. + Right border not aligned or missing. + <literal_block xml:space="preserve"> + +-----------------------+ + | Right border missing. + +-----------------------+ """], ["""\ ++-------------------------+ +| A table with one cell | +| and missing bottom. | +""", +"""\ +<document source="test data"> + <system_message level="3" line="3" source="test data" type="ERROR"> + <paragraph> + Malformed table. + Bottom border missing or corrupt. + <literal_block xml:space="preserve"> + +-------------------------+ + | A table with one cell | + | and missing bottom. | +"""], +["""\ ++-------------------------+ +| A table with one cell | +| and corrupt bottom. | ++------------------------ + +""", +"""\ +<document source="test data"> + <system_message level="3" line="3" source="test data" type="ERROR"> + <paragraph> + Malformed table. + Bottom border missing or corrupt. + <literal_block xml:space="preserve"> + +-------------------------+ + | A table with one cell | + | and corrupt bottom. | + +------------------------ + +"""], +["""\ ++-------------------------+ +| A table with one cell | +| and corrupt bottom. | +--------------------------+ +""", +"""\ +<document source="test data"> + <system_message level="3" line="4" source="test data" type="ERROR"> + <paragraph> + Malformed table. + Bottom border missing or corrupt. + <literal_block xml:space="preserve"> + +-------------------------+ + | A table with one cell | + | and corrupt bottom. | + <system_message level="2" line="4" source="test data" type="WARNING"> + <paragraph> + Blank line required after table. + <paragraph> + --------------------------+ +"""], +["""\ +------------------------+ | A well-formed | table. | +------------------------+ @@ -884,9 +953,9 @@ ["""\ ============== ====== A simple table cell 2 -cell 3 cell 4 ============== ====== -No blank line after table. +this could be cell content +or text after the table """, """\ <document source="test data"> @@ -897,13 +966,13 @@ <literal_block xml:space="preserve"> ============== ====== A simple table cell 2 - cell 3 cell 4 ============== ====== - <system_message level="2" line="5" source="test data" type="WARNING"> + <system_message level="2" line="4" source="test data" type="WARNING"> <paragraph> Blank line required after table. <paragraph> - No blank line after table. + this could be cell content + or text after the table """], ["""\ ============== ====== @@ -1117,10 +1186,10 @@ """, """\ <document source="test data"> - <system_message level="3" line="1" source="test data" type="ERROR"> + <system_message level="3" line="4" source="test data" type="ERROR"> <paragraph> Malformed table. - Bottom/header table border does not match top border. + Bottom border or header rule does not match top border. <literal_block xml:space="preserve"> ============== ====== A simple table this text extends to the right This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-19 17:03:58
|
Revision: 10207 http://sourceforge.net/p/docutils/code/10207 Author: milde Date: 2025-08-19 17:03:49 +0000 (Tue, 19 Aug 2025) Log Message: ----------- Simplify the determination of table opening LaTeX code. No change to the output. Modified Paths: -------------- trunk/docutils/docutils/writers/latex2e/__init__.py Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-19 16:53:27 UTC (rev 10206) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-19 17:03:49 UTC (rev 10207) @@ -937,6 +937,8 @@ return '' def get_opening(self, width=r'\linewidth'): + opening = [] + nr_of_cols = len(self._col_specs) align_map = {'left': '[l]', 'center': '[c]', 'right': '[r]', @@ -943,18 +945,17 @@ None: ''} align = align_map.get(self.get('align')) latex_type = self.get_latex_type() - if align and latex_type not in ("longtable", "longtable*"): - opening = [r'\noindent\makebox[\linewidth]%s{%%' % (align,), - r'\begin{%s}' % (latex_type,)] - else: - opening = [r'\begin{%s}%s' % (latex_type, align)] + if align and not latex_type.startswith("longtable"): + opening.append(r'\noindent\makebox[\linewidth]%s{%%' % align) + align = '' if not self.colwidths_auto: if self.borders == 'standard' and not self.legacy_column_widths: - opening.insert(-1, r'\setlength{\DUtablewidth}' + opening.append(r'\setlength{\DUtablewidth}' r'{\dimexpr%s-%i\arrayrulewidth\relax}%%' - % (width, len(self._col_specs)+1)) + % (width, nr_of_cols+1)) else: - opening.insert(-1, r'\setlength{\DUtablewidth}{%s}%%' % width) + opening.append(r'\setlength{\DUtablewidth}{%s}%%' % width) + opening.append(r'\begin{%s}%s' % (latex_type, align)) return '\n'.join(opening) def get_closing(self): @@ -965,7 +966,7 @@ # closing.append(r'\hline') closing.append(r'\end{%s}' % self.get_latex_type()) if (self.get('align') - and self.get_latex_type() not in ("longtable", "longtable*")): + and not self.get_latex_type().startswith("longtable")): closing.append('}') return '\n'.join(closing) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-19 16:53:39
|
Revision: 10206 http://sourceforge.net/p/docutils/code/10206 Author: milde Date: 2025-08-19 16:53:27 +0000 (Tue, 19 Aug 2025) Log Message: ----------- rST parser: ensure that nested parsing only appends to the provided base node. * Always start a new title style hierarchy for nested parsing. * Append sections to the base node also if it is not a `<document>` or `<section>` (revert [r10203]). This change makes the behaviour of nested parsing more uniform and predictable (independent of type and possible parents of the base node). It prevents problems with silently dropped higher level sections in Docutils < 0.22 (because the legacy "raise error and retry" algorithm of section parsing fails with nested parsing). It also prevents a wrong node order for sections in Docutils 0.22 (because the calling parser has no means to know the new "insertion point" and continues where it left before the nested parsing). Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/RELEASE-NOTES.rst trunk/docutils/docs/ref/rst/directives.rst trunk/docutils/docs/ref/rst/restructuredtext.rst trunk/docutils/docutils/parsers/rst/states.py trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-19 15:07:47 UTC (rev 10205) +++ trunk/docutils/HISTORY.rst 2025-08-19 16:53:27 UTC (rev 10206) @@ -24,17 +24,18 @@ * docutils/nodes.py - `nodes.Element.section_hierarchy()` now returns only elements - with non-empty `.parent`. + with non-empty "parent" attribute. * docutils/parsers/rst/states.py - - Relax "section title" system message from SEVERE to ERROR. + - Relax "section title" system messages from SEVERE to ERROR. - Ensure new "current node" is valid when switching section level (cf. bugs #508 and #509). - - `NestedStateMachine.run()` uses a separate title style hierarchy - if the base node is not attached to the document (cf. bug #508). + - Use a `separate title style hierarchy for nested parsing`__. +__ RELEASE-NOTES.html#nested-parsing + Release 0.22 (2026-07-29) ========================= Modified: trunk/docutils/RELEASE-NOTES.rst =================================================================== --- trunk/docutils/RELEASE-NOTES.rst 2025-08-19 15:07:47 UTC (rev 10205) +++ trunk/docutils/RELEASE-NOTES.rst 2025-08-19 16:53:27 UTC (rev 10206) @@ -261,12 +261,30 @@ ============================ reStructuredText parser: - Nested parsing uses a separate title style hierarchy - if the base node is not attached to the document. + _`Nested parsing` uses a separate section `title style hierarchy`_ if + `states.RSTState.nested_parsing()` is used with ``match_titles=True``. + Content included via nested parsing may use section title styles in + different order, all sections become sub-sections (or sub-sub-section...) + of the current section level. [#]_ + This ensures that all elements generated by the nested parsing are + added to the provided base node (without possible data loss as in + Docutils < 0.22). + No changes are required to document sources that work fine + in Docutils <= 0.22. + + .. [#] similar to Sphinx's `sphinx.util.node.nested_parse_with_titles()` + and overriding the ``keep_title_context`` argument of + `sphinx.util.parsing.nested_parse_to_nodes()`__ + + __ https://www.sphinx-doc.org/en/master/extdev/utils.html + #sphinx.util.parsing.nested_parse_to_nodes + Bugfixes and improvements (see HISTORY_). +.. _title style hierarchy: docs/ref/rst/restructuredtext.html#title-styles + Release 0.22 (2025-07-29) ========================= Modified: trunk/docutils/docs/ref/rst/directives.rst =================================================================== --- trunk/docutils/docs/ref/rst/directives.rst 2025-08-19 15:07:47 UTC (rev 10205) +++ trunk/docutils/docs/ref/rst/directives.rst 2025-08-19 16:53:27 UTC (rev 10206) @@ -1646,8 +1646,8 @@ Parse the included content with the specified parser. See the `"parser" configuration setting`_ for available parsers. - Starts a new "`section hierarchy`_" (all sections in the included - content become subsections of the current section). + Starts a new `section title style hierarchy`_ (all sections in the + included content become subsections of the current section). .. Caution:: There is is no check whether the inserted elements are valid at the @@ -2315,7 +2315,7 @@ .. _hyperlink target: restructuredtext.html#hyperlink-targets .. _reference name: .. _reference names: restructuredtext.html#reference-names -.. _section hierarchy: restructuredtext.html#sections +.. _section title style hierarchy: restructuredtext.html#title-styles .. _simple table: restructuredtext.html#simple-tables .. _supported length units: restructuredtext.html#length-units Modified: trunk/docutils/docs/ref/rst/restructuredtext.rst =================================================================== --- trunk/docutils/docs/ref/rst/restructuredtext.rst 2025-08-19 15:07:47 UTC (rev 10205) +++ trunk/docutils/docs/ref/rst/restructuredtext.rst 2025-08-19 16:53:27 UTC (rev 10206) @@ -596,6 +596,8 @@ next title of the same or higher level are included in a section (or subsection, etc.). +.. _title styles: + All section title styles need not be used, nor need any specific section title style be used. However, a document must be consistent in its use of section titles: once a hierarchy of title styles is Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-19 15:07:47 UTC (rev 10205) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-19 16:53:27 UTC (rev 10206) @@ -186,8 +186,7 @@ """ Parse `input_lines` and populate `node`. - Use a separate "title style hierarchy" if `node` is not - attached to the document (changed in Docutils 0.23). + Use a separate "title style hierarchy" (changed in Docutils 0.23). Extend `StateMachineWS.run()`: set up document-wide data. """ @@ -199,13 +198,11 @@ self.reporter = self.document.reporter self.node = node if match_titles: - # Start a new title style hierarchy if `node` is not - # a descendant of the `document`: - _root = node - while _root.parent is not None: - _root = _root.parent - if _root != self.document: - self.memo.title_styles = [] + # Use a separate section title style hierarchy; + # ensure all sections in the `input_lines` are treated as + # subsections of the current section by blocking lower + # section levels with a style that is impossible in rST: + self.memo.title_styles = ['x'] * len(node.section_hierarchy()) results = StateMachineWS.run(self, input_lines, input_offset) assert results == [], ('NestedStateMachine.run() results should be ' 'empty!') @@ -282,16 +279,13 @@ :input_offset: Line number at start of the block. :node: - Base node. Generated nodes will be appended to this node - (unless a new section with lower level is encountered, see below). + Base node. All generated nodes will be appended to this node. :match_titles: Allow section titles? - If the base `node` is attached to the document, new sections will - be appended according their level in the section hierarchy - (moving up the tree). - If the base `node` is *not* attached to the document, - a separate section title style hierarchy is used for the nested + A separate section title style hierarchy is used for the nested parsing (all sections are subsections of the current section). + The calling code should check whether sections are valid + children of the base node and move them or warn otherwise. :state_machine_class: Default: `NestedStateMachine`. :state_machine_kwargs: @@ -406,12 +400,7 @@ if newlevel > len(title_styles): title_styles.append(style) self.memo.section_level = newlevel - if newlevel > oldlevel: - # new section is a subsection: get the current section or base node - while self.parent.parent and not isinstance( - self.parent, (nodes.section, nodes.document)): - self.parent = self.parent.parent - else: + if newlevel <= oldlevel: # new section is sibling or higher up in the section hierarchy self.parent = parent_sections[newlevel-1].parent return True Modified: trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-19 15:07:47 UTC (rev 10205) +++ trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-19 16:53:27 UTC (rev 10206) @@ -1,6 +1,6 @@ #! /usr/bin/env python3 -# $Id$ -# Author: David Goodger <go...@py...> +# $id$ +# author: David Goodger <go...@py...> # Copyright: This module has been placed in the public domain. """ @@ -180,15 +180,13 @@ current1 ******** - sec1.2 + current1.1 ----------- - Sibling section appended 1 level up. - - sec2 - ========= + current1.1.1 + ============ Top-level section appended to document. -Succeeding paragraph. TODO: currently misplaced! +Succeeding paragraph. """, """\ <document source="test data"> @@ -201,18 +199,16 @@ <section ids="current1" names="current1"> <title> current1 + <section ids="current1-1" names="current1.1"> + <title> + current1.1 + <section ids="current1-1-1" names="current1.1.1"> + <title> + current1.1.1 + <paragraph> + Top-level section appended to document. <paragraph> - Succeeding paragraph. TODO: currently misplaced! - <section ids="sec1-2" names="sec1.2"> - <title> - sec1.2 - <paragraph> - Sibling section appended 1 level up. - <section ids="sec2" names="sec2"> - <title> - sec2 - <paragraph> - Top-level section appended to document. + Succeeding paragraph. """], # parse into attached wrapper node: ["""\ @@ -224,11 +220,10 @@ attached1 ********* - sec2 - ========= - Nested top-level section appended to document. + attached1.1 + =========== -Succeeding paragraph. TODO: currently misplaced! +Succeeding paragraph. """, """\ <document source="test data"> @@ -239,16 +234,14 @@ <title> sec1.1 <sidebar> - <section ids="attached1" names="attached1"> - <title> - attached1 + <section ids="attached1" names="attached1"> + <title> + attached1 + <section ids="attached1-1" names="attached1.1"> + <title> + attached1.1 <paragraph> - Succeeding paragraph. TODO: currently misplaced! - <section ids="sec2" names="sec2"> - <title> - sec2 - <paragraph> - Nested top-level section appended to document. + Succeeding paragraph. """], # detached base node -> start new section hierarchy ["""\ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-19 15:07:50
|
Revision: 10205 http://sourceforge.net/p/docutils/code/10205 Author: milde Date: 2025-08-19 15:07:47 +0000 (Tue, 19 Aug 2025) Log Message: ----------- Test nested parsing in a directive. Define/use sample directives to test nested parsing with section support. Advantages: * The tests are simpler and easier to comprehend. * Emulate possible use cases. Provide template for extension developers. * More comprehensive testing. * Allows test/comparision with the legacy section parsing algorithm (used up to 0.22). Tests revealed problems with section styles matching existing styles: legacy section parsing: DATA LOSS: Sections with a level outside the nested parsing are silently dropped! This happens also with a base node attached to the document. new section parsing: After nested parsing, the "insertion point" is restored to what it was before. If sibling sections or parent sections are attached according to their level, the insertion point after parsing is before these sections (the order of the nodes is mixed up). In a block-quote or other body element the current node is not attached to the document because the content is parsed with nested_parse into node that is appended after parsing. If the current node is used as base node, the section style hierarchy may be document-wide or local, depending on the placement of the directive. Modified Paths: -------------- trunk/docutils/test/test_parsers/test_rst/test_misc.py Added Paths: ----------- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py Modified: trunk/docutils/test/test_parsers/test_rst/test_misc.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-15 22:28:45 UTC (rev 10204) +++ trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-19 15:07:47 UTC (rev 10205) @@ -9,7 +9,6 @@ from pathlib import Path import sys -import types import unittest if __name__ == '__main__': @@ -16,9 +15,8 @@ # prepend the local "docutils root" to the Python library path sys.path.insert(0, Path(__file__).resolve().parents[2].as_posix()) -from docutils import frontend, nodes, statemachine, utils +from docutils import frontend, utils import docutils.parsers.rst -from docutils.parsers.rst import states class RstParserTests(unittest.TestCase): @@ -32,172 +30,5 @@ parser.parse(b'hol', document) -class RSTStateTests(unittest.TestCase): - - # state machine - machine = states.RSTStateMachine(state_classes=states.state_classes, - initial_state='Body') - # "generic" state - state = states.RSTState(machine) - - def title_markup(self, text, adornment='-'): - underline = adornment * len(text) - return statemachine.StringList([text, underline], 'section block') - - def setUp(self): - # state machine runtime initialization (cf. RSTStateMachine.run()) - # Only for test: - # don't use this low-level approach in production code! - - # empty <document> and settings: - settings = frontend.get_default_settings(docutils.parsers.rst.Parser) - settings.halt_level = 2 - settings.warning_stream = '' - document = self.document = utils.new_document('test data', settings) - # language module (localized directive and role names) - # self.machine.language = languages.get_language( - # document.settings.language_code) - # self.machine.match_titles = True # support sections - # "memo": Container for document-wide auxiliary data - inliner = states.Inliner() # rST parser for inline markup - inliner.init_customizations(document.settings) - self.machine.memo = types.SimpleNamespace(document=document, - reporter=document.reporter, - language='en', - title_styles=[], - inliner=inliner) - # self.machine.document = document - # self.machine.reporter = document.reporter - self.machine.node = document - # initialize `state` object - self.state.runtime_init() - - def test_nested_parse(self): - # parse a text block in a nested parser - text = statemachine.StringList(['test input'], 'nested block') - tip = nodes.tip('') # base `node` - - # plain text -> attach <paragraph> to `node`: - self.state.nested_parse(text, input_offset=0, node=tip) - self.assertEqual('<tip><paragraph>test input</paragraph></tip>', - str(tip)) - - # by default, section titles are not supported: - title = self.title_markup('top', '-') - with self.assertRaisesRegex(utils.SystemMessage, - 'Unexpected section title.'): - self.state.nested_parse(title, input_offset=0, node=tip) - - # Nested parsing with section markup is supported with - # ``match_titles=True`` but not used in Docutils. - # (However, Sphinx "autodoc" and some contributed extensions use it.) - def test_nested_parse_with_sections_base_attached(self): - # If the base `node` is attached to the `document`, - # the document-wide section style hierarchy is used. - - # base`node` is a <section> - section = nodes.section('') - self.document += section # attach to document - self.machine.memo.title_styles.append('-') # register title style - # parse level-2 section title - title = self.title_markup('sub', '~') - # -> append new <section> to the parsers base `node` - self.state.nested_parse(title, 0, node=section, match_titles=True) - self.assertEqual('<document source="test data">\n' - ' <section>\n' - ' <section ids="sub" names="sub">\n' - ' <title>\n' - ' sub\n', - str(self.document.pformat())) - self.assertEqual(['-', '~'], self.machine.memo.title_styles) - - # parse top-level section title - title = self.title_markup('top', '-') - # -> move 1 level up and attach <section> to document - self.state.nested_parse(title, 0, node=section, match_titles=True) - self.assertEqual('<document source="test data">\n' - ' <section>\n' - ' <section ids="sub" names="sub">\n' - ' <title>\n' - ' sub\n' - ' <section ids="top" names="top">\n' - ' <title>\n' - ' top\n', - str(self.document.pformat())) - - # base`node` is a <paragraph> - paragraph = nodes.paragraph('', 'base node') - section += paragraph # attach (indirectly) - # parse top-level section title - title = self.title_markup('top 2', '-') - # -> move 1 level up and attach <section> to document - self.state.nested_parse(title, 0, node=paragraph, match_titles=True) - self.assertEqual('<section ids="top-2" names="top\\ 2">\n' - ' <title>\n' - ' top 2\n', - self.document[-1].pformat()) - - # new (2nd-level) section title - title = self.title_markup('sub 2', '~') - self.state.nested_parse(title, 0, node=paragraph, match_titles=True) - self.assertEqual('<section>\n' - ' <section ids="sub" names="sub">\n' - ' <title>\n' - ' sub\n' - ' <paragraph>\n' - ' base node\n' - ' <section ids="sub-2" names="sub\\ 2">\n' - ' <title>\n' - ' sub 2\n', - section.pformat()) - - def test_nested_parse_with_sections_detached(self): - # The base `node` does not need to be attached to the document. - # "global" title style hierarchy (ignored with detached base node) - self.machine.memo.title_styles = ['-', '~'] - - # base `node` is a <paragraph> without parents - base = nodes.paragraph('') - base.document = self.document # this is not "attaching" - # level-2 title style - title = self.title_markup('sub', '~') - # new hierarchy -> attach <section> to base `node` - self.state.nested_parse(title, 0, node=base, match_titles=True) - self.assertEqual('<paragraph>\n' - ' <section ids="sub" names="sub">\n' - ' <title>\n' - ' sub\n', - base.pformat()) - # It is the users responsibility to ensure that the base node - # may contain a <section> (or move the section after parsing). - # You may check with `validate()`: - with self.assertRaises(nodes.ValidationError): - base.validate() - - # a new hierarchy is used in every call of nested_parse() - # parse 2 section titles - title = self.title_markup('new', '*') + self.title_markup('top', '-') - # new hierarchy -> attach section and sub-section to base node - self.state.nested_parse(title, 0, node=base, match_titles=True) - self.assertEqual( - '<paragraph>\n' - ' <section ids="sub" names="sub">\n' - ' <title>\n' - ' sub\n' - ' <section ids="new" names="new">\n' - ' <title>\n' - ' new\n' - ' <section ids="top" names="top">\n' - ' <title>\n' - ' top\n', - base.pformat()) - - # document-wide style hierarchy unchanged: - self.assertEqual(['-', '~'], self.machine.memo.title_styles) - - # print(self.document.pformat()) - # print(base.pformat()) - - if __name__ == '__main__': unittest.main() Added: trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py (rev 0) +++ trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py 2025-08-19 15:07:47 UTC (rev 10205) @@ -0,0 +1,327 @@ +#! /usr/bin/env python3 +# $Id$ +# Author: David Goodger <go...@py...> +# Copyright: This module has been placed in the public domain. + +""" +Tests for nested parsing with support for sections (cf. states.py). + +The method states.RSTState.nested_parse() provides the argument `match_titles`. +However, in Docutils, it is only used with `match_titles=False`. +None of the standard Docutils directives supports section titles in the +directive content. (Directives supporting sections in the content are, +e.g., defined by the "autodoc" and "kerneldoc" Sphinx extensions.) + +Up to Docutils 0.22, the section title styles were document-wide enforced and +sections with current level or higher were silently dropped! + +Sphinx uses the `sphinx.util.parsing._fresh_title_style_context` context +manager to provide a separate title style hierarchy for nested parsing. +""" + +from pathlib import Path +import sys +import unittest + +if __name__ == '__main__': + # prepend the "docutils root" to the Python library path + # so we import the local `docutils` package. + sys.path.insert(0, str(Path(__file__).resolve().parents[3])) + +from docutils import nodes +from docutils.frontend import get_default_settings +from docutils.parsers import rst +from docutils.utils import new_document + + +class ParseIntoDetachedNode(rst.Directive): + """A directive implementing nested parsing with support for sections. + """ + final_argument_whitespace = True + has_content = True + + def run(self): + # similar to sphinx.util.parsing.nested_parse_to_nodes() + node = nodes.Element() + node.document = self.state.document + self.state.nested_parse(self.content, input_offset=0, + node=node, match_titles=True) + return node.children + + +class ParseIntoCurrentNode(ParseIntoDetachedNode): + def run(self): + node = self.state.parent # the current "insertion point" + self.state.nested_parse(self.content, 0, node, match_titles=True) + return [] # nodes already attached to document + + +class ParseIntoAttachedNode(ParseIntoDetachedNode): + def run(self): + node = nodes.sidebar('') + self.state.parent.append(node) + self.state.nested_parse(self.content, 0, node, match_titles=True) + return [] # nodes already attached to document + + +class ParserTestCase(unittest.TestCase): + maxDiff = None + + def test_parser(self): + rst.directives.register_directive('nested-detached', + ParseIntoDetachedNode) + rst.directives.register_directive('nested-current', + ParseIntoCurrentNode) + rst.directives.register_directive('nested-attached', + ParseIntoAttachedNode) + parser = rst.Parser() + settings = get_default_settings(rst.Parser) + settings.warning_stream = '' + settings.halt_level = 5 + for name, cases in totest.items(): + for casenum, (case_input, case_expected) in enumerate(cases): + with self.subTest(id=f'totest[{name!r}][{casenum}]'): + document = new_document('test data', settings.copy()) + parser.parse(case_input, document) + output = document.pformat() + self.assertEqual(case_expected, output) + + +totest = {} + +# Parse into the base node: +totest['nested_parsing'] = [ +["""\ +Preceding paragraph. + +.. nested-attached:: + + .. hint:: this is nested. + +Succeeding paragraph. +""", +"""\ +<document source="test data"> + <paragraph> + Preceding paragraph. + <sidebar> + <hint> + <paragraph> + this is nested. + <paragraph> + Succeeding paragraph. +"""], +# detached base node -> start new section hierarchy with every nested parse +["""\ +sec1 +==== +sec1.1 +------ +.. nested-detached:: + + detached1 + ********* + detached1.1 + ----------- + detached1.1.1 + ============= + +.. nested-detached:: + + detached2 + --------- + detached2.1 + *********** + +Succeeding paragraph. + +sec2 +==== +The document-wide section title styles are kept. +""", +"""\ +<document source="test data"> + <section ids="sec1" names="sec1"> + <title> + sec1 + <section ids="sec1-1" names="sec1.1"> + <title> + sec1.1 + <section ids="detached1" names="detached1"> + <title> + detached1 + <section ids="detached1-1" names="detached1.1"> + <title> + detached1.1 + <section ids="detached1-1-1" names="detached1.1.1"> + <title> + detached1.1.1 + <section ids="detached2" names="detached2"> + <title> + detached2 + <section ids="detached2-1" names="detached2.1"> + <title> + detached2.1 + <paragraph> + Succeeding paragraph. + <section ids="sec2" names="sec2"> + <title> + sec2 + <paragraph> + The document-wide section title styles are kept. +"""], +# base node == current node -> keep section hierarchy +["""\ +sec1 +==== +sec1.1 +------ +.. nested-current:: + + current1 + ******** + sec1.2 + ----------- + Sibling section appended 1 level up. + + sec2 + ========= + Top-level section appended to document. + +Succeeding paragraph. TODO: currently misplaced! +""", +"""\ +<document source="test data"> + <section ids="sec1" names="sec1"> + <title> + sec1 + <section ids="sec1-1" names="sec1.1"> + <title> + sec1.1 + <section ids="current1" names="current1"> + <title> + current1 + <paragraph> + Succeeding paragraph. TODO: currently misplaced! + <section ids="sec1-2" names="sec1.2"> + <title> + sec1.2 + <paragraph> + Sibling section appended 1 level up. + <section ids="sec2" names="sec2"> + <title> + sec2 + <paragraph> + Top-level section appended to document. +"""], +# parse into attached wrapper node: +["""\ +sec1 +==== +sec1.1 +------ +.. nested-attached:: + + attached1 + ********* + sec2 + ========= + Nested top-level section appended to document. + +Succeeding paragraph. TODO: currently misplaced! +""", +"""\ +<document source="test data"> + <section ids="sec1" names="sec1"> + <title> + sec1 + <section ids="sec1-1" names="sec1.1"> + <title> + sec1.1 + <sidebar> + <section ids="attached1" names="attached1"> + <title> + attached1 + <paragraph> + Succeeding paragraph. TODO: currently misplaced! + <section ids="sec2" names="sec2"> + <title> + sec2 + <paragraph> + Nested top-level section appended to document. +"""], +# detached base node -> start new section hierarchy +["""\ +sec1 +==== +sec1.1 +------ +sec2 +==== +.. nested-detached:: + detached1 + ~~~~~~~~~ + detached1.1 + ----------- + +Succeeding paragraph. +""", +"""\ +<document source="test data"> + <section ids="sec1" names="sec1"> + <title> + sec1 + <section ids="sec1-1" names="sec1.1"> + <title> + sec1.1 + <section ids="sec2" names="sec2"> + <title> + sec2 + <section ids="detached1" names="detached1"> + <title> + detached1 + <section ids="detached1-1" names="detached1.1"> + <title> + detached1.1 + <paragraph> + Succeeding paragraph. +"""], +# base node == <blockquote> +["""\ +sec1 +==== + + A block-quote is parsed into a detached <blockquote> element. + + .. nested-current:: + + nested section + ============== + + The nested <section> becomes a child of the <blockquote> (sic.)! + +The calling directive should move the nested <section> or report +a validity violation. +""", +"""\ +<document source="test data"> + <section ids="sec1" names="sec1"> + <title> + sec1 + <block_quote> + <paragraph> + A block-quote is parsed into a detached <blockquote> element. + <section ids="nested-section" names="nested\\ section"> + <title> + nested section + <paragraph> + The nested <section> becomes a child of the <blockquote> (sic.)! + <paragraph> + The calling directive should move the nested <section> or report + a validity violation. +"""], +] + + +if __name__ == '__main__': + unittest.main() Property changes on: trunk/docutils/test/test_parsers/test_rst/test_nested_parsing.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Author Date Id Revision \ No newline at end of property This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-15 22:28:48
|
Revision: 10204 http://sourceforge.net/p/docutils/code/10204 Author: milde Date: 2025-08-15 22:28:45 +0000 (Fri, 15 Aug 2025) Log Message: ----------- rST parser: new title-style hierarchy for nested parsing with detached base node. Use a separate section title style hierarchy for nested parsing if the base node is not attached to the document. With a detached base, the parser can not move up in the section hierarchy, so it does not make sense to enforce the document-wide title style hierarchy. Separate title styles also fit better with directives that fetch their content from separate sources. Docutils does not switch on section support in nested parsing. Sphinx defines and uses a "_fresh_title_style_context" in nested parsing. Some contributed Sphinx extensions reported regeressions with the new section parsing algorightm because the old algorithm was forgiving in case of inconsistent title styles in nested parsing (cf. [bugs:#508]). Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/RELEASE-NOTES.rst trunk/docutils/docs/ref/rst/directives.rst trunk/docutils/docs/ref/rst/restructuredtext.rst trunk/docutils/docutils/parsers/rst/states.py trunk/docutils/test/test_parsers/test_rst/test_misc.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-15 17:38:10 UTC (rev 10203) +++ trunk/docutils/HISTORY.rst 2025-08-15 22:28:45 UTC (rev 10204) @@ -31,6 +31,8 @@ - Relax "section title" system message from SEVERE to ERROR. - Ensure new "current node" is valid when switching section level (cf. bugs #508 and #509). + - `NestedStateMachine.run()` uses a separate title style hierarchy + if the base node is not attached to the document (cf. bug #508). Release 0.22 (2026-07-29) Modified: trunk/docutils/RELEASE-NOTES.rst =================================================================== --- trunk/docutils/RELEASE-NOTES.rst 2025-08-15 17:38:10 UTC (rev 10203) +++ trunk/docutils/RELEASE-NOTES.rst 2025-08-15 22:28:45 UTC (rev 10204) @@ -260,6 +260,10 @@ Release 0.23b0 (unpublished) ============================ +reStructuredText parser: + Nested parsing uses a separate title style hierarchy + if the base node is not attached to the document. + Bugfixes and improvements (see HISTORY_). Modified: trunk/docutils/docs/ref/rst/directives.rst =================================================================== --- trunk/docutils/docs/ref/rst/directives.rst 2025-08-15 17:38:10 UTC (rev 10203) +++ trunk/docutils/docs/ref/rst/directives.rst 2025-08-15 22:28:45 UTC (rev 10204) @@ -1646,6 +1646,9 @@ Parse the included content with the specified parser. See the `"parser" configuration setting`_ for available parsers. + Starts a new "`section hierarchy`_" (all sections in the included + content become subsections of the current section). + .. Caution:: There is is no check whether the inserted elements are valid at the point of insertion. It is recommended to validate_ the document. @@ -2310,10 +2313,11 @@ .. _hyperlink references: restructuredtext.html#hyperlink-references .. _hyperlink targets: .. _hyperlink target: restructuredtext.html#hyperlink-targets -.. _supported length units: restructuredtext.html#length-units .. _reference name: .. _reference names: restructuredtext.html#reference-names +.. _section hierarchy: restructuredtext.html#sections .. _simple table: restructuredtext.html#simple-tables +.. _supported length units: restructuredtext.html#length-units .. _reStructuredText Interpreted Text Roles: .. _interpreted text role: roles.html Modified: trunk/docutils/docs/ref/rst/restructuredtext.rst =================================================================== --- trunk/docutils/docs/ref/rst/restructuredtext.rst 2025-08-15 17:38:10 UTC (rev 10203) +++ trunk/docutils/docs/ref/rst/restructuredtext.rst 2025-08-15 22:28:45 UTC (rev 10204) @@ -599,7 +599,7 @@ All section title styles need not be used, nor need any specific section title style be used. However, a document must be consistent in its use of section titles: once a hierarchy of title styles is -established, sections must use that hierarchy. +established, sections must use that hierarchy. [#]_ Each section title automatically generates a hyperlink target pointing to the section. The text of the hyperlink target (the "reference @@ -609,7 +609,14 @@ Sections may contain `body elements`_, transitions_, and nested sections. +.. [#] Directives_ may establish a separate hierarchy of title styles + for their content. This is handy for directives that include + content from separate sources, e.g., the directives provided by + the `"autodoc" Sphinx extension`__. + __ https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html + + Transitions ----------- Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-15 17:38:10 UTC (rev 10203) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-15 22:28:45 UTC (rev 10204) @@ -104,6 +104,7 @@ __docformat__ = 'reStructuredText' +import copy import re from types import FunctionType, MethodType from types import SimpleNamespace as Struct @@ -185,15 +186,26 @@ """ Parse `input_lines` and populate `node`. + Use a separate "title style hierarchy" if `node` is not + attached to the document (changed in Docutils 0.23). + Extend `StateMachineWS.run()`: set up document-wide data. """ self.match_titles = match_titles - self.memo = memo + self.memo = copy.copy(memo) self.document = memo.document self.attach_observer(self.document.note_source) self.language = memo.language self.reporter = self.document.reporter self.node = node + if match_titles: + # Start a new title style hierarchy if `node` is not + # a descendant of the `document`: + _root = node + while _root.parent is not None: + _root = _root.parent + if _root != self.document: + self.memo.title_styles = [] results = StateMachineWS.run(self, input_lines, input_offset) assert results == [], ('NestedStateMachine.run() results should be ' 'empty!') @@ -270,13 +282,16 @@ :input_offset: Line number at start of the block. :node: - Root node. Generated nodes will be appended to this node - (unless a new section with lower level is encountered). + Base node. Generated nodes will be appended to this node + (unless a new section with lower level is encountered, see below). :match_titles: Allow section titles? - If True, `node` should be attached to the document - so that section levels can be computed correctly - and moving up in the section hierarchy works. + If the base `node` is attached to the document, new sections will + be appended according their level in the section hierarchy + (moving up the tree). + If the base `node` is *not* attached to the document, + a separate section title style hierarchy is used for the nested + parsing (all sections are subsections of the current section). :state_machine_class: Default: `NestedStateMachine`. :state_machine_kwargs: @@ -326,9 +341,15 @@ state_machine_class=None, state_machine_kwargs=None): """ - Create a new StateMachine rooted at `node` and run it over the input - `block`. Also keep track of optional intermediate blank lines and the + Parse the input `block` with a nested state-machine rooted at `node`. + + Create a new StateMachine rooted at `node` and run it over the + input `block` (see also `nested_parse()`). + Also keep track of optional intermediate blank lines and the required final one. + + Return new offset and a boolean indicating whether there was a + blank final line. """ if state_machine_class is None: state_machine_class = self.nested_sm Modified: trunk/docutils/test/test_parsers/test_rst/test_misc.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-15 17:38:10 UTC (rev 10203) +++ trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-15 22:28:45 UTC (rev 10204) @@ -151,6 +151,53 @@ ' sub 2\n', section.pformat()) + def test_nested_parse_with_sections_detached(self): + # The base `node` does not need to be attached to the document. + # "global" title style hierarchy (ignored with detached base node) + self.machine.memo.title_styles = ['-', '~'] + # base `node` is a <paragraph> without parents + base = nodes.paragraph('') + base.document = self.document # this is not "attaching" + # level-2 title style + title = self.title_markup('sub', '~') + # new hierarchy -> attach <section> to base `node` + self.state.nested_parse(title, 0, node=base, match_titles=True) + self.assertEqual('<paragraph>\n' + ' <section ids="sub" names="sub">\n' + ' <title>\n' + ' sub\n', + base.pformat()) + # It is the users responsibility to ensure that the base node + # may contain a <section> (or move the section after parsing). + # You may check with `validate()`: + with self.assertRaises(nodes.ValidationError): + base.validate() + + # a new hierarchy is used in every call of nested_parse() + # parse 2 section titles + title = self.title_markup('new', '*') + self.title_markup('top', '-') + # new hierarchy -> attach section and sub-section to base node + self.state.nested_parse(title, 0, node=base, match_titles=True) + self.assertEqual( + '<paragraph>\n' + ' <section ids="sub" names="sub">\n' + ' <title>\n' + ' sub\n' + ' <section ids="new" names="new">\n' + ' <title>\n' + ' new\n' + ' <section ids="top" names="top">\n' + ' <title>\n' + ' top\n', + base.pformat()) + + # document-wide style hierarchy unchanged: + self.assertEqual(['-', '~'], self.machine.memo.title_styles) + + # print(self.document.pformat()) + # print(base.pformat()) + + if __name__ == '__main__': unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-15 17:38:12
|
Revision: 10203 http://sourceforge.net/p/docutils/code/10203 Author: milde Date: 2025-08-15 17:38:10 +0000 (Fri, 15 Aug 2025) Log Message: ----------- Look for valid parent before appending a section element. Don't append a `<section>` to an `<admonition>` or other body element, only to <section>` and `<document>`. Exception: the base node of a nested parser may become the new section's parent, if it has no parent (detached node). It is up to the users (or extension implementing a directive) to prevent or handle these cases. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/parsers/rst/states.py trunk/docutils/test/test_parsers/test_rst/test_misc.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2025-08-15 17:37:57 UTC (rev 10202) +++ trunk/docutils/HISTORY.rst 2025-08-15 17:38:10 UTC (rev 10203) @@ -21,6 +21,11 @@ - More consistent and concise command line help. +* docutils/nodes.py + + - `nodes.Element.section_hierarchy()` now returns only elements + with non-empty `.parent`. + * docutils/parsers/rst/states.py - Relax "section title" system message from SEVERE to ERROR. Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-15 17:37:57 UTC (rev 10202) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-15 17:38:10 UTC (rev 10203) @@ -385,7 +385,12 @@ if newlevel > len(title_styles): title_styles.append(style) self.memo.section_level = newlevel - if newlevel <= oldlevel: + if newlevel > oldlevel: + # new section is a subsection: get the current section or base node + while self.parent.parent and not isinstance( + self.parent, (nodes.section, nodes.document)): + self.parent = self.parent.parent + else: # new section is sibling or higher up in the section hierarchy self.parent = parent_sections[newlevel-1].parent return True Modified: trunk/docutils/test/test_parsers/test_rst/test_misc.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-15 17:37:57 UTC (rev 10202) +++ trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-15 17:38:10 UTC (rev 10203) @@ -138,7 +138,6 @@ self.document[-1].pformat()) # new (2nd-level) section title - # TODO: don't append <section> to <paragraph>! title = self.title_markup('sub 2', '~') self.state.nested_parse(title, 0, node=paragraph, match_titles=True) self.assertEqual('<section>\n' @@ -147,9 +146,9 @@ ' sub\n' ' <paragraph>\n' ' base node\n' - ' <section ids="sub-2" names="sub\\ 2">\n' - ' <title>\n' - ' sub 2\n', + ' <section ids="sub-2" names="sub\\ 2">\n' + ' <title>\n' + ' sub 2\n', section.pformat()) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-15 17:38:00
|
Revision: 10202 http://sourceforge.net/p/docutils/code/10202 Author: milde Date: 2025-08-15 17:37:57 +0000 (Fri, 15 Aug 2025) Log Message: ----------- Fixes for the section level determination. Simplify the fix in [r10200]. Don't add elements without valid parent to the list returned by `nodes.Element.section_hierarchy()` instead of removing them later. Remove the check/report for `new_parent is None` (we don't add elements with .parent None to the `parent_sections` list any more). Don't add a new section title style to the list of established title styles if it is inconsistent. Annotate and document arguments of `RSTState.nested_parse()`. Modified Paths: -------------- trunk/docutils/docutils/nodes.py trunk/docutils/docutils/parsers/rst/states.py trunk/docutils/test/test_parsers/test_rst/test_misc.py trunk/docutils/test/test_parsers/test_rst/test_section_headers.py Modified: trunk/docutils/docutils/nodes.py =================================================================== --- trunk/docutils/docutils/nodes.py 2025-08-15 17:37:47 UTC (rev 10201) +++ trunk/docutils/docutils/nodes.py 2025-08-15 17:37:57 UTC (rev 10202) @@ -820,18 +820,21 @@ def section_hierarchy(self) -> list[section]: """Return the element's section hierarchy. - Return a list of all <section> elements containing `self` - (including `self` if it is a <section>). + Return a list of all <section> elements that contain `self` + (including `self` if it is a <section>) and have a parent node. List item ``[i]`` is the parent <section> of level i+1 (1: section, 2: subsection, 3: subsubsection, ...). The length of the list is the element's section level. + See `docutils.parsers.rst.states.RSTState.check_subsection()` + for a usage example. + Provisional. May be changed or removed without warning. """ sections = [] node = self - while node is not None: + while node.parent is not None: if isinstance(node, section): sections.append(node) node = node.parent Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-15 17:37:47 UTC (rev 10201) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-15 17:37:57 UTC (rev 10202) @@ -121,7 +121,11 @@ from docutils.utils._roman_numerals import (InvalidRomanNumeralError, RomanNumeral) +TYPE_CHECKING = False +if TYPE_CHECKING: + from docutils.statemachine import StringList + class MarkupError(DataError): pass class UnknownInterpretedRoleError(DataError): pass class InterpretedRoleNotImplementedError(DataError): pass @@ -250,11 +254,37 @@ """Called at beginning of file.""" return [], [] - def nested_parse(self, block, input_offset, node, match_titles=False, - state_machine_class=None, state_machine_kwargs=None): + def nested_parse(self, + block: StringList, + input_offset: int, + node: nodes.Element, + match_titles: bool = False, + state_machine_class: StateMachineWS|None = None, + state_machine_kwargs: dict|None = None + ) -> int: """ - Create a new StateMachine rooted at `node` and run it over the input - `block`. + Parse the input `block` with a nested state-machine rooted at `node`. + + :block: + reStructuredText source extract. + :input_offset: + Line number at start of the block. + :node: + Root node. Generated nodes will be appended to this node + (unless a new section with lower level is encountered). + :match_titles: + Allow section titles? + If True, `node` should be attached to the document + so that section levels can be computed correctly + and moving up in the section hierarchy works. + :state_machine_class: + Default: `NestedStateMachine`. + :state_machine_kwargs: + Keyword arguments for the state-machine instantiation. + Default: `self.nested_sm_kwargs`. + + Create a new state-machine instance if required. + Return new offset. """ use_default = 0 if state_machine_class is None: @@ -263,8 +293,6 @@ if state_machine_kwargs is None: state_machine_kwargs = self.nested_sm_kwargs use_default += 1 - block_length = len(block) - state_machine = None if use_default == 2: try: @@ -274,8 +302,11 @@ if not state_machine: state_machine = state_machine_class(debug=self.debug, **state_machine_kwargs) + # run the statemachine and populate `node`: + block_length = len(block) state_machine.run(block, input_offset, memo=self.memo, node=node, match_titles=match_titles) + # clean up if use_default == 2: self.nested_sm_cache.append(state_machine) else: @@ -332,12 +363,6 @@ """ title_styles = self.memo.title_styles parent_sections = self.parent.section_hierarchy() - # Adding a new <section> at level "i" is done by appending to - # ``parent_sections[i-1].parent``. - # However, in nested parsing the root `node` may be a <section>. - # Then ``parent_sections[0]`` has no parent and must be discarded: - if parent_sections and parent_sections[0].parent is None: - parent_sections.pop(0) # current section level: (0 root, 1 section, 2 subsection, ...) oldlevel = len(parent_sections) # new section level: @@ -344,8 +369,7 @@ try: # check for existing title style newlevel = title_styles.index(style) + 1 except ValueError: # new title style - title_styles.append(style) - newlevel = len(title_styles) + newlevel = len(title_styles) + 1 # The new level must not be deeper than an immediate child # of the current level: if newlevel > oldlevel + 1: @@ -358,23 +382,12 @@ line=lineno) return False # Update parent state: + if newlevel > len(title_styles): + title_styles.append(style) self.memo.section_level = newlevel if newlevel <= oldlevel: # new section is sibling or higher up in the section hierarchy - new_parent = parent_sections[newlevel-1].parent - if new_parent is None: - styles = ' '.join('/'.join(style) for style in title_styles) - self.parent += self.reporter.error( - f'Cannot skip from level {oldlevel} to {newlevel}.' - ' Current element has only {len(self.parent_sections)}' - ' parent sections.' - ' (Mismatch of `memo.section_styles`,' - ' and the root node of a nested parser?)', - nodes.literal_block('', source), - nodes.paragraph('', f'Established title styles: {styles}'), - line=lineno) - return False - self.parent = new_parent + self.parent = parent_sections[newlevel-1].parent return True def title_inconsistent(self, sourcetext, lineno): Modified: trunk/docutils/test/test_parsers/test_rst/test_misc.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-15 17:37:47 UTC (rev 10201) +++ trunk/docutils/test/test_parsers/test_rst/test_misc.py 2025-08-15 17:37:57 UTC (rev 10202) @@ -9,6 +9,7 @@ from pathlib import Path import sys +import types import unittest if __name__ == '__main__': @@ -15,8 +16,9 @@ # prepend the local "docutils root" to the Python library path sys.path.insert(0, Path(__file__).resolve().parents[2].as_posix()) -from docutils import frontend, utils +from docutils import frontend, nodes, statemachine, utils import docutils.parsers.rst +from docutils.parsers.rst import states class RstParserTests(unittest.TestCase): @@ -30,5 +32,126 @@ parser.parse(b'hol', document) +class RSTStateTests(unittest.TestCase): + + # state machine + machine = states.RSTStateMachine(state_classes=states.state_classes, + initial_state='Body') + # "generic" state + state = states.RSTState(machine) + + def title_markup(self, text, adornment='-'): + underline = adornment * len(text) + return statemachine.StringList([text, underline], 'section block') + + def setUp(self): + # state machine runtime initialization (cf. RSTStateMachine.run()) + # Only for test: + # don't use this low-level approach in production code! + + # empty <document> and settings: + settings = frontend.get_default_settings(docutils.parsers.rst.Parser) + settings.halt_level = 2 + settings.warning_stream = '' + document = self.document = utils.new_document('test data', settings) + # language module (localized directive and role names) + # self.machine.language = languages.get_language( + # document.settings.language_code) + # self.machine.match_titles = True # support sections + # "memo": Container for document-wide auxiliary data + inliner = states.Inliner() # rST parser for inline markup + inliner.init_customizations(document.settings) + self.machine.memo = types.SimpleNamespace(document=document, + reporter=document.reporter, + language='en', + title_styles=[], + inliner=inliner) + # self.machine.document = document + # self.machine.reporter = document.reporter + self.machine.node = document + # initialize `state` object + self.state.runtime_init() + + def test_nested_parse(self): + # parse a text block in a nested parser + text = statemachine.StringList(['test input'], 'nested block') + tip = nodes.tip('') # base `node` + + # plain text -> attach <paragraph> to `node`: + self.state.nested_parse(text, input_offset=0, node=tip) + self.assertEqual('<tip><paragraph>test input</paragraph></tip>', + str(tip)) + + # by default, section titles are not supported: + title = self.title_markup('top', '-') + with self.assertRaisesRegex(utils.SystemMessage, + 'Unexpected section title.'): + self.state.nested_parse(title, input_offset=0, node=tip) + + # Nested parsing with section markup is supported with + # ``match_titles=True`` but not used in Docutils. + # (However, Sphinx "autodoc" and some contributed extensions use it.) + def test_nested_parse_with_sections_base_attached(self): + # If the base `node` is attached to the `document`, + # the document-wide section style hierarchy is used. + + # base`node` is a <section> + section = nodes.section('') + self.document += section # attach to document + self.machine.memo.title_styles.append('-') # register title style + # parse level-2 section title + title = self.title_markup('sub', '~') + # -> append new <section> to the parsers base `node` + self.state.nested_parse(title, 0, node=section, match_titles=True) + self.assertEqual('<document source="test data">\n' + ' <section>\n' + ' <section ids="sub" names="sub">\n' + ' <title>\n' + ' sub\n', + str(self.document.pformat())) + self.assertEqual(['-', '~'], self.machine.memo.title_styles) + + # parse top-level section title + title = self.title_markup('top', '-') + # -> move 1 level up and attach <section> to document + self.state.nested_parse(title, 0, node=section, match_titles=True) + self.assertEqual('<document source="test data">\n' + ' <section>\n' + ' <section ids="sub" names="sub">\n' + ' <title>\n' + ' sub\n' + ' <section ids="top" names="top">\n' + ' <title>\n' + ' top\n', + str(self.document.pformat())) + + # base`node` is a <paragraph> + paragraph = nodes.paragraph('', 'base node') + section += paragraph # attach (indirectly) + # parse top-level section title + title = self.title_markup('top 2', '-') + # -> move 1 level up and attach <section> to document + self.state.nested_parse(title, 0, node=paragraph, match_titles=True) + self.assertEqual('<section ids="top-2" names="top\\ 2">\n' + ' <title>\n' + ' top 2\n', + self.document[-1].pformat()) + + # new (2nd-level) section title + # TODO: don't append <section> to <paragraph>! + title = self.title_markup('sub 2', '~') + self.state.nested_parse(title, 0, node=paragraph, match_titles=True) + self.assertEqual('<section>\n' + ' <section ids="sub" names="sub">\n' + ' <title>\n' + ' sub\n' + ' <paragraph>\n' + ' base node\n' + ' <section ids="sub-2" names="sub\\ 2">\n' + ' <title>\n' + ' sub 2\n', + section.pformat()) + + if __name__ == '__main__': unittest.main() Modified: trunk/docutils/test/test_parsers/test_rst/test_section_headers.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_section_headers.py 2025-08-15 17:37:47 UTC (rev 10201) +++ trunk/docutils/test/test_parsers/test_rst/test_section_headers.py 2025-08-15 17:37:57 UTC (rev 10202) @@ -502,7 +502,7 @@ Title 4 ``````` <paragraph> - Established title styles: = - ` + Established title styles: = - <paragraph> Paragraph 4. """], @@ -556,7 +556,7 @@ Title 4 ``````` <paragraph> - Established title styles: =/= -/- `/` + Established title styles: =/= -/- <paragraph> Paragraph 4. """], This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2025-08-15 17:37:49
|
Revision: 10201 http://sourceforge.net/p/docutils/code/10201 Author: milde Date: 2025-08-15 17:37:47 +0000 (Fri, 15 Aug 2025) Log Message: ----------- Deprecate parsers.rst.states.RSTStateMachine.memo.reporter. The "reporter" is already attached to "memo.document", so we can simplify the "memo" a bit. Modified Paths: -------------- trunk/docutils/RELEASE-NOTES.rst trunk/docutils/docutils/parsers/rst/states.py Modified: trunk/docutils/RELEASE-NOTES.rst =================================================================== --- trunk/docutils/RELEASE-NOTES.rst 2025-08-11 12:07:22 UTC (rev 10200) +++ trunk/docutils/RELEASE-NOTES.rst 2025-08-15 17:37:47 UTC (rev 10201) @@ -208,10 +208,11 @@ in Docutils 2.0. Since Docutils 0.22, you may use "reader", "parser", and "writer" arguments for component names as well as instances. -* Remove `states.RSTStateMachine.memo.section_bubble_up_kludge`, +* Remove `states.RSTStateMachine.memo.reporter`, + `states.RSTStateMachine.memo.section_bubble_up_kludge`, `states.RSTStateMachine.memo.section_level`, `states.RSTState.title_inconsistent()`, and `states.Line.eofcheck` - in Docutils 2.0. Ignored since Docutils 0.22. + in Docutils 2.0. Ignored since Docutils 0.22.1. * Remove `parsers.rst.states.Struct` (obsoleted by `types.SimpleNamespace`) in Docutils 2.0. Modified: trunk/docutils/docutils/parsers/rst/states.py =================================================================== --- trunk/docutils/docutils/parsers/rst/states.py 2025-08-11 12:07:22 UTC (rev 10200) +++ trunk/docutils/docutils/parsers/rst/states.py 2025-08-15 17:37:47 UTC (rev 10201) @@ -151,16 +151,19 @@ if inliner is None: inliner = Inliner() inliner.init_customizations(document.settings) + # A collection of objects to share with nested parsers. + # The attributes `reporter`, `section_level`, and + # `section_bubble_up_kludge` will be removed in Docutils 2.0 self.memo = Struct(document=document, - reporter=document.reporter, + reporter=document.reporter, # ignored language=self.language, title_styles=[], - section_level=0, # ignored, to be removed in 2.0 - section_bubble_up_kludge=False, # ignored, "" + section_level=0, # ignored + section_bubble_up_kludge=False, # ignored inliner=inliner) self.document = document self.attach_observer(document.note_source) - self.reporter = self.memo.reporter + self.reporter = self.document.reporter self.node = document results = StateMachineWS.run(self, input_lines, input_offset, input_source=document['source']) @@ -184,8 +187,8 @@ self.memo = memo self.document = memo.document self.attach_observer(self.document.note_source) - self.reporter = memo.reporter self.language = memo.language + self.reporter = self.document.reporter self.node = node results = StateMachineWS.run(self, input_lines, input_offset) assert results == [], ('NestedStateMachine.run() results should be ' @@ -213,9 +216,9 @@ StateWS.runtime_init(self) memo = self.state_machine.memo self.memo = memo - self.reporter = memo.reporter + self.document = memo.document self.inliner = memo.inliner - self.document = memo.document + self.reporter = self.document.reporter self.parent = self.state_machine.node # enable the reporter to determine source and source-line if not hasattr(self.reporter, 'get_source_and_line'): @@ -637,9 +640,9 @@ :text: source string :lineno: absolute line number, cf. `statemachine.get_source_and_line()` """ - self.reporter = memo.reporter self.document = memo.document self.language = memo.language + self.reporter = self.document.reporter self.parent = parent pattern_search = self.patterns.initial.search dispatch = self.dispatch This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |