Skip to content

Commit

Permalink
Make it possible to undefine macros and add a macros example to the R…
Browse files Browse the repository at this point in the history
…EADME (#244)

Make it possible to undefine macros and add a macros example to the README

Fixes #243.

Reviewed-by: František Lachman
  • Loading branch information
softwarefactory-project-zuul[bot] authored Jun 30, 2023
2 parents bad19f0 + 37677e7 commit a1248a6
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 9 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ with Specfile('test.spec') as specfile:
...
```

### Defining and undefining macros

```python
# override macros loaded from system macro files
specfile = Specfile('test.spec', macros=[('fedora', '38'), ('dist', '.fc38')])

# undefine a system macro (in case it's defined)
specfile = Specfile('test.spec', macros=[('rhel', None)])
```

### Low-level manipulation

```python
Expand Down
2 changes: 1 addition & 1 deletion specfile/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def remove(cls, macro: str) -> None:
while retry < MAX_REMOVAL_RETRIES:
rpm.delMacro(macro)
try:
if cls.expand(f"%{macro}") == f"%{macro}":
if cls.expand(f"%{{{macro}}}") == f"%{{{macro.replace('%%', '%')}}}":
break
except RPMException:
# the macro can't be expanded, but it still exists
Expand Down
15 changes: 11 additions & 4 deletions specfile/spec_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class SpecParser:
def __init__(
self,
sourcedir: Path,
macros: Optional[List[Tuple[str, str]]] = None,
macros: Optional[List[Tuple[str, Optional[str]]]] = None,
force_parse: bool = False,
) -> None:
self.sourcedir = sourcedir
Expand Down Expand Up @@ -170,7 +170,9 @@ def restore(key):
restore(key)

def _do_parse(
self, content: str, extra_macros: Optional[List[Tuple[str, str]]] = None
self,
content: str,
extra_macros: Optional[List[Tuple[str, Optional[str]]]] = None,
) -> Tuple[rpm.spec, bool]:
"""
Parses the content of a spec file.
Expand All @@ -190,7 +192,10 @@ def _do_parse(
def get_rpm_spec(content, flags):
Macros.reinit()
for name, value in self.macros + (extra_macros or []):
Macros.define(name, value)
if value is None:
Macros.remove(name)
else:
Macros.define(name, value)
Macros.define("_sourcedir", str(self.sourcedir))
with tempfile.NamedTemporaryFile() as tmp:
tmp.write(content.encode())
Expand Down Expand Up @@ -318,7 +323,9 @@ def collect_loaded_sources(content):
return get_rpm_spec(content, rpm.RPMSPEC_ANYARCH), tainted

def parse(
self, content: str, extra_macros: Optional[List[Tuple[str, str]]] = None
self,
content: str,
extra_macros: Optional[List[Tuple[str, Optional[str]]]] = None,
) -> None:
"""
Parses the content of a spec file and updates the `spec` and `tainted` attributes.
Expand Down
6 changes: 3 additions & 3 deletions specfile/specfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(
path: Union[Path, str],
sourcedir: Optional[Union[Path, str]] = None,
autosave: bool = False,
macros: Optional[List[Tuple[str, str]]] = None,
macros: Optional[List[Tuple[str, Optional[str]]]] = None,
force_parse: bool = False,
) -> None:
"""
Expand Down Expand Up @@ -124,7 +124,7 @@ def sourcedir(self, value: Union[Path, str]) -> None:
self._parser.sourcedir = Path(value)

@property
def macros(self) -> List[Tuple[str, str]]:
def macros(self) -> List[Tuple[str, Optional[str]]]:
"""List of extra macro definitions."""
return self._parser.macros

Expand Down Expand Up @@ -167,7 +167,7 @@ def save(self) -> None:
def expand(
self,
expression: str,
extra_macros: Optional[List[Tuple[str, str]]] = None,
extra_macros: Optional[List[Tuple[str, Optional[str]]]] = None,
skip_parsing: bool = False,
) -> str:
"""
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/test_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,19 @@ def test_macros_remove():
Macros.remove("test")
Macros.remove("non_existent_macro")
assert Macros.dump() == macros
rpm.addMacro("%test", "1")
rpm.addMacro("te%st%%", "2")
Macros.remove("%test")
Macros.remove("te%st%%")
assert Macros.dump() == macros


def test_macros_remove_failure():
# Expansion and removal of built-in macros is broken before rpm 4.17,
# ensure that we are not stuck in an infinite loop
rpm.reloadConfig()
rpm.addMacro("foo", "bar")
flexmock(rpm).should_receive("expandMacro").with_args("%foo").and_raise(rpm.error)
flexmock(rpm).should_receive("expandMacro").with_args("%{foo}").and_raise(rpm.error)
with pytest.raises(MacroRemovalException):
Macros.remove("foo")

Expand Down
25 changes: 25 additions & 0 deletions tests/unit/test_spec_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pathlib import Path

import rpm
from flexmock import flexmock

from specfile.spec_parser import SpecParser

Expand Down Expand Up @@ -39,3 +40,27 @@ def test_copy_spec_parser():
deep_copy = copy.deepcopy(parser)
assert deep_copy == parser
assert deep_copy is not parser


def test_spec_parser_macros():
flexmock(rpm).should_call("delMacro").with_args(
"fedora"
).at_least().once().ordered()
flexmock(rpm).should_call("delMacro").with_args("rhel").at_least().once().ordered()
flexmock(rpm).should_call("addMacro").with_args("rhel", "9").once().ordered()
# we don't care about the rest
flexmock(rpm).should_call("addMacro")
flexmock(rpm).should_call("delMacro")
parser = SpecParser(Path("."), macros=[("fedora", None), ("rhel", "9")])
spec, _ = parser._do_parse(
(
"Name: test\n"
"Version: 0.1\n"
"Release: 1%{?dist}\n"
"Summary: Test package\n"
"License: MIT\n"
"\n"
"%description\n"
"Test package\n"
),
)

0 comments on commit a1248a6

Please sign in to comment.