Skip to content

Commit

Permalink
Allow to comment out or uncomment a macro definition (#298)
Browse files Browse the repository at this point in the history
Allow to comment out or uncomment a macro definition

Necessary for Packit to be able to comment out or uncomment a pre-release suffix macro definition (example).
Related to packit/packit#2013.
RELEASE NOTES BEGIN
Macro definitions gained a new commented_out property indicating that a macro definition is commented out. Another new property, comment_out_style, determines if it is achieved by using a %dnl (discard next line) directive (e.g. %dnl %global prerelease beta2) or by replacing the starting % with # (e.g. #global prerelease beta2).
RELEASE NOTES END

Reviewed-by: František Lachman <[email protected]>
  • Loading branch information
2 parents 8ff7514 + 21027a3 commit 6abe398
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 24 deletions.
66 changes: 51 additions & 15 deletions specfile/macro_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 []
Expand All @@ -40,21 +52,32 @@ 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
)

@formatted
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:
"""
Expand All @@ -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

Expand Down Expand Up @@ -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+)
Expand All @@ -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]
Expand All @@ -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,
)
Expand Down
1 change: 1 addition & 0 deletions specfile/specfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
]
Expand Down
103 changes: 94 additions & 9 deletions tests/unit/test_macro_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
("", " ", " ", ""),
),
]
)
Expand All @@ -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,
("", " ", " ", ""),
),
]
)
Expand All @@ -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",
"",
Expand All @@ -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)"
)
Expand All @@ -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,
[
"",
Expand All @@ -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,
[""],
),
Expand All @@ -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",
"",
Expand All @@ -149,6 +232,8 @@ def test_copy_macro_definitions():
"commit",
"9ab9717cf7d1be1a85b165a8eacb71b9e5831113",
True,
False,
CommentOutStyle.DNL,
("", " ", " ", ""),
),
],
Expand Down

0 comments on commit 6abe398

Please sign in to comment.