diff --git a/specfile/macro_definitions.py b/specfile/macro_definitions.py index 166c684..0d70dc5 100644 --- a/specfile/macro_definitions.py +++ b/specfile/macro_definitions.py @@ -4,6 +4,7 @@ import collections import copy import re +from enum import Enum, auto from typing import TYPE_CHECKING, List, Optional, Tuple, Union, overload from specfile.conditions import process_conditions @@ -14,20 +15,31 @@ from specfile.specfile import Specfile +class CommentOutStyle(Enum): + DNL = auto() + HASH = auto() + + class MacroDefinition: def __init__( self, name: str, body: str, is_global: bool, + commented_out: bool, + comment_out_style: CommentOutStyle, whitespace: Tuple[str, str, str, str], + dnl_whitespace: str = "", valid: bool = True, preceding_lines: Optional[List[str]] = None, ) -> None: self.name = name self.body = body self.is_global = is_global + self.commented_out = commented_out + self.comment_out_style = comment_out_style self._whitespace = whitespace + self._dnl_whitespace = dnl_whitespace self.valid = valid self._preceding_lines = ( preceding_lines.copy() if preceding_lines is not None else [] @@ -40,7 +52,10 @@ def __eq__(self, other: object) -> bool: self.name == other.name and self.body == other.body and self.is_global == other.is_global + and self.commented_out == other.commented_out + and self.comment_out_style == other.comment_out_style and self._whitespace == other._whitespace + and self._dnl_whitespace == other._dnl_whitespace and self._preceding_lines == other._preceding_lines ) @@ -48,13 +63,21 @@ def __eq__(self, other: object) -> bool: def __repr__(self) -> str: return ( f"MacroDefinition({self.name!r}, {self.body!r}, {self.is_global!r}, " - f"{self._whitespace!r}, {self.valid!r}, {self._preceding_lines!r})" + f"{self.commented_out!r}, {self.comment_out_style!r}, {self._whitespace!r}, " + f"{self.valid!r}, {self._preceding_lines!r})" ) def __str__(self) -> str: ws = self._whitespace - macro = "%global" if self.is_global else "%define" - return f"{ws[0]}{macro}{ws[1]}{self.name}{ws[2]}{self.body}{ws[3]}" + dnl = "" + sc = "%" + if self.commented_out: + if self.comment_out_style is CommentOutStyle.DNL: + dnl = f"%dnl{self._dnl_whitespace}" + elif self.comment_out_style is CommentOutStyle.HASH: + sc = "#" + macro = "global" if self.is_global else "define" + return f"{ws[0]}{dnl}{sc}{macro}{ws[1]}{self.name}{ws[2]}{self.body}{ws[3]}" def get_position(self, container: "MacroDefinitions") -> int: """ @@ -73,13 +96,20 @@ def get_position(self, container: "MacroDefinitions") -> int: def get_raw_data(self) -> List[str]: result = self._preceding_lines.copy() ws = self._whitespace - macro = "%global" if self.is_global else "%define" + dnl = "" + sc = "%" + if self.commented_out: + if self.comment_out_style is CommentOutStyle.DNL: + dnl = f"%dnl{self._dnl_whitespace}" + elif self.comment_out_style is CommentOutStyle.HASH: + sc = "#" + macro = "global" if self.is_global else "define" body = self.body.splitlines() if body: body[-1] += ws[3] else: body = [ws[3]] - result.append(f"{ws[0]}{macro}{ws[1]}{self.name}{ws[2]}{body[0]}") + result.append(f"{ws[0]}{dnl}{sc}{macro}{ws[1]}{self.name}{ws[2]}{body[0]}") result.extend(body[1:]) return result @@ -249,7 +279,9 @@ def count_brackets(s): r""" ^ (\s*) # optional preceding whitespace - (%(?:global|define)) # scope-defining macro definition + (%dnl\s+)? # optional DNL prefix + ((?(2)%|(?:%|\#))) # starting character + (global|define) # scope-defining macro definition (\s+) (\w+(?:\(.*?\))?) # macro name with optional arguments in parentheses (\s+) @@ -267,15 +299,16 @@ def count_brackets(s): line, valid = pop(lines) m = md_regex.match(line) if m: - ws0, macro, ws1, name, ws2, body, ws3 = m.groups() - if ws3 == "\\": - body += ws3 - ws3 = "" - bc, pc = count_brackets(body) - while (bc > 0 or pc > 0 or body.endswith("\\")) and lines: - line, _ = pop(lines) - body += "\n" + line + ws0, dnl, sc, macro, ws1, name, ws2, body, ws3 = m.groups() + if not dnl and sc == "%": + if ws3 == "\\": + body += ws3 + ws3 = "" bc, pc = count_brackets(body) + while (bc > 0 or pc > 0 or body.endswith("\\")) and lines: + line, _ = pop(lines) + body += "\n" + line + bc, pc = count_brackets(body) tokens = re.split(r"(\s+)$", body, maxsplit=1) if len(tokens) == 1: body = tokens[0] @@ -286,8 +319,11 @@ def count_brackets(s): MacroDefinition( name, body, - macro == "%global", + macro == "global", + bool(dnl or sc == "#"), + CommentOutStyle.HASH if sc == "#" else CommentOutStyle.DNL, (ws0, ws1, ws2, ws3), + dnl[4:] if dnl else "", valid, buffer, ) diff --git a/specfile/specfile.py b/specfile/specfile.py index 4fd11e5..34d606d 100644 --- a/specfile/specfile.py +++ b/specfile/specfile.py @@ -730,6 +730,7 @@ class Entity: ) for md in macro_definitions if md.valid + and not md.commented_out and not protected_regex.match(md.name) and not md.name.endswith(")") # skip macro definitions with options ] diff --git a/tests/unit/test_macro_definitions.py b/tests/unit/test_macro_definitions.py index 4673ca5..ce9da40 100644 --- a/tests/unit/test_macro_definitions.py +++ b/tests/unit/test_macro_definitions.py @@ -5,21 +5,39 @@ import pytest -from specfile.macro_definitions import MacroDefinition, MacroDefinitions +from specfile.macro_definitions import ( + CommentOutStyle, + MacroDefinition, + MacroDefinitions, +) def test_find(): macro_definitions = MacroDefinitions( [ - MacroDefinition("gitdate", "20160901", True, ("", " ", " ", "")), + MacroDefinition( + "gitdate", + "20160901", + True, + False, + CommentOutStyle.DNL, + ("", " ", " ", ""), + ), MacroDefinition( "commit", "9ab9717cf7d1be1a85b165a8eacb71b9e5831113", True, + False, + CommentOutStyle.DNL, ("", " ", " ", ""), ), MacroDefinition( - "shortcommit", "%(c=%{commit}; echo ${c:0:7})", True, ("", " ", " ", "") + "shortcommit", + "%(c=%{commit}; echo ${c:0:7})", + True, + False, + CommentOutStyle.DNL, + ("", " ", " ", ""), ), ] ) @@ -32,15 +50,29 @@ def test_find(): def test_get(): macro_definitions = MacroDefinitions( [ - MacroDefinition("gitdate", "20160901", True, ("", " ", " ", "")), + MacroDefinition( + "gitdate", + "20160901", + True, + False, + CommentOutStyle.DNL, + ("", " ", " ", ""), + ), MacroDefinition( "commit", "9ab9717cf7d1be1a85b165a8eacb71b9e5831113", True, + False, + CommentOutStyle.DNL, ("", " ", " ", ""), ), MacroDefinition( - "shortcommit", "%(c=%{commit}; echo ${c:0:7})", True, ("", " ", " ", "") + "shortcommit", + "%(c=%{commit}; echo ${c:0:7})", + True, + False, + CommentOutStyle.DNL, + ("", " ", " ", ""), ), ] ) @@ -59,6 +91,9 @@ def test_parse(): "%global commit 9ab9717cf7d1be1a85b165a8eacb71b9e5831113", "%global shortcommit %(c=%{commit}; echo ${c:0:7})", "", + "%dnl %global pre a1", + "#global prerel beta2", + "", "Name: test", "Version: 0.1.0", "", @@ -74,8 +109,14 @@ def test_parse(): assert macro_definitions[1].name == "commit" assert macro_definitions.commit.body == "9ab9717cf7d1be1a85b165a8eacb71b9e5831113" assert macro_definitions[2].name == "shortcommit" - assert macro_definitions[3].name == "desc(x)" - assert macro_definitions[3].body == ( + assert macro_definitions[3].name == "pre" + assert macro_definitions[3].commented_out + assert macro_definitions[3].comment_out_style is CommentOutStyle.DNL + assert macro_definitions[4].name == "prerel" + assert macro_definitions[4].commented_out + assert macro_definitions[4].comment_out_style is CommentOutStyle.HASH + assert macro_definitions[5].name == "desc(x)" + assert macro_definitions[5].body == ( "Test spec file containing several \\\n" "macro definitions in various formats (%?1)" ) @@ -90,21 +131,57 @@ def test_parse(): def test_get_raw_data(): macro_definitions = MacroDefinitions( [ - MacroDefinition("gitdate", "20160901", True, ("", " ", " ", "")), + MacroDefinition( + "gitdate", + "20160901", + True, + False, + CommentOutStyle.DNL, + ("", " ", " ", ""), + ), MacroDefinition( "commit", "9ab9717cf7d1be1a85b165a8eacb71b9e5831113", True, + False, + CommentOutStyle.DNL, ("", " ", " ", ""), ), MacroDefinition( - "shortcommit", "%(c=%{commit}; echo ${c:0:7})", True, ("", " ", " ", "") + "shortcommit", + "%(c=%{commit}; echo ${c:0:7})", + True, + False, + CommentOutStyle.DNL, + ("", " ", " ", ""), + ), + MacroDefinition( + "pre", + "a1", + True, + True, + CommentOutStyle.DNL, + ("", " ", " ", ""), + " ", + True, + [""], + ), + MacroDefinition( + "prerel", + "beta2", + True, + True, + CommentOutStyle.HASH, + ("", " ", " ", ""), ), MacroDefinition( "desc(x)", "Test spec file containing several \\\nmacro definitions in various formats (%?1)", False, + False, + CommentOutStyle.DNL, ("", " ", " ", ""), + "", True, [ "", @@ -119,7 +196,10 @@ def test_get_raw_data(): "This an example of a macro definition with body \n" "spawning across mutiple lines}", False, + False, + CommentOutStyle.DNL, ("", " ", " ", ""), + "", True, [""], ), @@ -130,6 +210,9 @@ def test_get_raw_data(): "%global commit 9ab9717cf7d1be1a85b165a8eacb71b9e5831113", "%global shortcommit %(c=%{commit}; echo ${c:0:7})", "", + "%dnl %global pre a1", + "#global prerel beta2", + "", "Name: test", "Version: 0.1.0", "", @@ -149,6 +232,8 @@ def test_copy_macro_definitions(): "commit", "9ab9717cf7d1be1a85b165a8eacb71b9e5831113", True, + False, + CommentOutStyle.DNL, ("", " ", " ", ""), ), ],