Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions specfile/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: MIT

import collections
import functools
import re
from enum import IntEnum
from typing import List, Optional
Expand Down Expand Up @@ -131,24 +132,36 @@ def dump(cls) -> List[Macro]:
return cls._parse([line.decode() for line in stderr])

@staticmethod
def expand(expression: str) -> str:
def expand(expression: str, safe: bool = True) -> str:
"""
Expands an expression in the global context.

Args:
expression: Expression to expand.
safe: Whether to disable macro manipulation during expansion.

Returns:
Expanded expression.

Raises:
RPMException: If expansion error occurs.
"""
cleanups = []
if safe:
# override macros that can affect other macros
# this should avoid any side-effects caused by the expansion
for macro in ("global", "define", "undefine"):
rpm.addMacro(macro, "%{nil}")
cleanups.append(functools.partial(rpm.delMacro, macro))
try:
with capture_stderr() as stderr:
return rpm.expandMacro(expression)
except rpm.error as e:
raise RPMException(stderr=stderr) from e
try:
with capture_stderr() as stderr:
return rpm.expandMacro(expression)
except rpm.error as e:
raise RPMException(stderr=stderr) from e
finally:
while cleanups:
cleanups.pop(0)()

@classmethod
def remove(cls, macro: str) -> None:
Expand All @@ -169,7 +182,10 @@ def remove(cls, macro: str) -> None:
while retry < MAX_REMOVAL_RETRIES:
rpm.delMacro(macro)
try:
if cls.expand(f"%{{{macro}}}") == f"%{{{macro.replace('%%', '%')}}}":
if (
cls.expand(f"%{{{macro}}}", safe=False)
== f"%{{{macro.replace('%%', '%')}}}"
):
break
except RPMException:
# the macro can't be expanded, but it still exists
Expand Down
17 changes: 17 additions & 0 deletions tests/unit/test_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,20 @@ def test_macros_define():
def test_macros_reinit():
Macros.reinit(MacroLevel.BUILTIN)
assert all(m.level == MacroLevel.BUILTIN for m in Macros.dump())


@pytest.mark.xfail(
rpm.__version__ <= "4.16.1.3",
reason="rpm <= 4.16.1.3 treats builtin macros specially and overriding them has no effect",
)
def test_macros_sideeffects():
rpm.reloadConfig()
rpm.addMacro("with_feature", "1")
assert rpm.expandMacro("%with_feature") == "1"
Macros.expand("%{expand:%global with_feature 0}", safe=False)
assert rpm.expandMacro("%with_feature") == "0"
rpm.reloadConfig()
rpm.addMacro("with_feature", "1")
assert rpm.expandMacro("%with_feature") == "1"
Macros.expand("%{expand:%global with_feature 0}", safe=True)
assert rpm.expandMacro("%with_feature") == "1"
Loading