From 16bc9fd97020be9e09ec08d22abc136b5d0b084a Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Tue, 3 Feb 2026 15:13:46 +0000 Subject: [PATCH 1/2] #3314 new test plus WIP fixing bug [skip ci] --- src/psyclone/line_length.py | 18 +++++++++++++++++- src/psyclone/tests/line_length_test.py | 16 ++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/psyclone/line_length.py b/src/psyclone/line_length.py index 521a8a2804..1fe8f00214 100644 --- a/src/psyclone/line_length.py +++ b/src/psyclone/line_length.py @@ -41,6 +41,9 @@ import re +from fparser.common.readfortran import Comment, FortranStringReader +from fparser.common.sourceinfo import FortranFormat + from psyclone.errors import InternalError @@ -179,6 +182,19 @@ def process(self, fortran_in): break_point = find_break_point( line, self._line_length-len(c_end), key_list) + import pdb; pdb.set_trace() + if line_type != "comment": + line_no_indent = line.lstrip() + indent_size = len(line) - len(line_no_indent) + # FortranStringReader will return separate Line and Comment + # objects for a source line containing an in-line comment. + freader = FortranStringReader(line, ignore_comments=False) + freader.set_format(FortranFormat(True, True)) + fline = freader.next() + if ((break_point - indent_size) > len(fline.line) and + isinstance(freader.next(), Comment)): + line_type = "comment" + fortran_out += line[:break_point] + c_end + "\n" line = line[break_point:] while len(line) + len(c_start) > self._line_length: @@ -195,7 +211,7 @@ def process(self, fortran_in): # We add an extra newline so remove it when we return return fortran_out[:-1] - def _get_line_type(self, line): + def _get_line_type(self, line) -> str: ''' Classes lines into different types. This is required as directives need different continuation characters to fortran statements. It also enables us to know a little about the diff --git a/src/psyclone/tests/line_length_test.py b/src/psyclone/tests/line_length_test.py index 2cb7965800..7eb1775844 100644 --- a/src/psyclone/tests/line_length_test.py +++ b/src/psyclone/tests/line_length_test.py @@ -211,6 +211,18 @@ def test_multiple_lines_comment(): assert output_file == expected_output +def test_inline_comment(): + '''Test that an in-line comment that takes us over the length limit is + wrapped correctly.''' + input_code = (" wfx_err_sub(ji,jj) = wfx_err_sub(ji,jj) - " + "pevap_rema(ji,jj) * a_i(ji,jj,jl_cat) * r1_Dt_ice ! " + "<=0 (net evap for the ocean in kg.m-2.s-1)") + fll = FortLineLength(line_length=132) + output = fll.process(input_code) + print(output) + assert "for the &\n!& ocean in kg.m-2.s-1)\n" in output + + def test_exception_line_too_long(): ''' Test that output lines are not longer than the maximum specified''' @@ -245,8 +257,6 @@ def test_break_types_multi_line(): fll = FortLineLength(line_length=24) output_file = fll.process(input_file) - print("("+output_file+")") - print(expected_output) assert output_file == expected_output @@ -266,8 +276,6 @@ def test_edge_conditions_statements(): "INTEGER &\n&INTEGER\n") fll = FortLineLength(line_length=len("INTEGER INTEGE")) output_string = fll.process(input_string) - print(output_string) - print(expected_output) assert output_string == expected_output input_string = ( From 29eaa01f575bb8d050d1028630237ebcdedac110 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Tue, 3 Feb 2026 17:07:01 +0000 Subject: [PATCH 2/2] #3314 fix test and add TODO --- src/psyclone/line_length.py | 38 ++++++++++++++------------ src/psyclone/tests/line_length_test.py | 17 ++++++++++-- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/psyclone/line_length.py b/src/psyclone/line_length.py index 1fe8f00214..d5d627795e 100644 --- a/src/psyclone/line_length.py +++ b/src/psyclone/line_length.py @@ -102,15 +102,9 @@ class FortLineLength(): split within a string. One known situation that could cause an instance of the - :class:`line_length.FortLineLength` class to fail is when an inline - comment is used at the end of a line to make it longer than the 132 - character limit. Whilst PSyclone does not generate such code for the - PSy-layer, this might occur in Algorithm-layer code, even if the - Algorithm-layer code conforms to the 132 line length limit. The reason - for this is that PSyclone's internal parser concatenates lines - together, thus a long line correctly split with continuation characters - in the Algorithm-layer becomes a line that needs to be split by an - instance of the :class:`line_length.FortLineLength` class. + :class:`line_length.FortLineLength` class to fail is when an *inline* + comment at the end of a line containing a *directive* takes it over + the 132-character limit. (TODO fparser/#468) ''' # pylint: disable=too-many-instance-attributes @@ -146,18 +140,17 @@ def long_lines(self, fortran_in): return False @property - def length(self): - ''' returns the maximum allowed line length''' + def length(self) -> int: + ''':returns: the maximum allowed line length.''' return self._line_length - def process(self, fortran_in): + def process(self, fortran_in: str) -> str: ''' Processes unlimited line-length Fortran code into Fortran code with long lines wrapped appropriately. - :param str fortran_in: Fortran code to be line wrapped. + :param fortran_in: Fortran code to be line wrapped. - :returns: line wrapped Fortran code. - :rtype: str + :returns: line-wrapped Fortran code. ''' fortran_out = "" @@ -182,18 +175,29 @@ def process(self, fortran_in): break_point = find_break_point( line, self._line_length-len(c_end), key_list) - import pdb; pdb.set_trace() if line_type != "comment": + # Check whether the proposed break point falls within an + # in-line comment. line_no_indent = line.lstrip() indent_size = len(line) - len(line_no_indent) # FortranStringReader will return separate Line and Comment # objects for a source line containing an in-line comment. - freader = FortranStringReader(line, ignore_comments=False) + freader = FortranStringReader(line, ignore_comments=False, + process_directives=True) + # Use free format. freader.set_format(FortranFormat(True, True)) fline = freader.next() + # This won't work for a directive with an in-line comment + # as FortranStringReader returns a single Comment object + # for the whole thing (TODO fparser/#468). if ((break_point - indent_size) > len(fline.line) and isinstance(freader.next(), Comment)): + # Breakpoint is inside a comment so change the chars + # used for the line-continuation end and start. line_type = "comment" + c_start = self._cont_start[line_type] + c_end = self._cont_end[line_type] + key_list = self._key_lists[line_type] fortran_out += line[:break_point] + c_end + "\n" line = line[break_point:] diff --git a/src/psyclone/tests/line_length_test.py b/src/psyclone/tests/line_length_test.py index 7eb1775844..4847f8e019 100644 --- a/src/psyclone/tests/line_length_test.py +++ b/src/psyclone/tests/line_length_test.py @@ -219,10 +219,21 @@ def test_inline_comment(): "<=0 (net evap for the ocean in kg.m-2.s-1)") fll = FortLineLength(line_length=132) output = fll.process(input_code) - print(output) - assert "for the &\n!& ocean in kg.m-2.s-1)\n" in output + assert "for the \n!& ocean in kg.m-2.s-1)" in output + input_code = (" wfx_err_sub(ji,jj) = wfx_err_sub(ji,jj) - " + "pevap_rema(ji,jj) * a_i(ji,jj,jl_cat) * r1_Dt_ice ! " + "<=0 (net evap for the ocean! in kg.m-2.s-1)") + output = fll.process(input_code) + assert "for the \n!& ocean! in kg.m-2.s-1)" in output + # Test when the comment is on a directive. + input_code = (f"!$acc kernels ! A very {' '.join(30*['long'])} comment") + output = fll.process(input_code) + if "long \n!$ long long" not in output: + pytest.xfail( + reason="TODO fparser/#468 - fparser.common.FortranReader " + "represents directives as Comments.") + - def test_exception_line_too_long(): ''' Test that output lines are not longer than the maximum specified'''