feat: restore iterable method in parser

This commit is contained in:
arcanis committed 2026-10-24 12:20:29 +03:00
1 parent f776b33310
commit c38cb5f680
4 files changed
+28 -15

No files matched your search

@@ -21,7 +21,7 @@ import re
import shlex
import tree_sitter_bash
from collections.abc import Iterator
from collections.abc import Iterable, Iterator
from enum import StrEnum
from tree_sitter import Language, Node, Parser
from typing import IO, cast
@@ -78,7 +78,7 @@ class PkgbuildNodeField(StrEnum):
Value = "value"
class PkgbuildParser:
class PkgbuildParser(Iterable[PkgbuildPatch]):
"""
PKGBUILD parser implementation in python
@@ -97,24 +97,29 @@ class PkgbuildParser:
original source, with their formatting preserved. Assignments inside shell statements and control-flow nodes are
extracted without checking whether those statements would run.
Each iteration parses the stored source independently, starting from the beginning.
Examples:
Parse package assignments::
>>> source = b"pkgname=example\\npkgver=1\\n"
>>> for patch in PkgbuildParser(BytesIO(source)).parse():
>>> for patch in PkgbuildParser(BytesIO(source)):
... print(patch.key, patch.value)
Function bodies are returned as function patches::
>>> source = b"pkgver() { printf 1; }"
>>> for patch in list(PkgbuildParser(BytesIO(source)).parse())
>>> for patch in PkgbuildParser(BytesIO(source)):
... print(patch.key, patch.value)
"""
_BRACE_EXPANSION = re.compile(r"(.*)\{([^{}]+)\}(.*)")
def __init__(self, stream: IO[bytes]) -> None:
""""""
"""
Args:
stream(IO[bytes]): input PKGBUILD stream
"""
self._content = stream.read()
@staticmethod
@@ -256,3 +261,12 @@ class PkgbuildParser:
)
yield from self._read(tree.root_node)
def __iter__(self) -> Iterator[PkgbuildPatch]:
"""
iterate over the parsed entries
Returns:
Iterator[PkgbuildPatch]: parsed entries
"""
return self.parse()
+1 -2
View File
@@ -80,8 +80,7 @@ class Pkgbuild(Mapping[str, Any]):
Returns:
Self: constructed instance of self
"""
parser = PkgbuildParser(stream)
fields = {patch.key: patch for patch in parser.parse()}
fields = {patch.key: patch for patch in PkgbuildParser(stream)}
# pkgbase is optional field, the pkgname must be used instead if not set
# however, pkgname is not presented is "package()" functions which we are parsing here too,
@@ -71,7 +71,7 @@ def test_parse_array_exception() -> None:
"""
parser = PkgbuildParser(BytesIO(b"var=(first second"))
with pytest.raises(PkgbuildParserError):
assert list(parser.parse())
list(parser.parse())
def test_parse_function() -> None:
@@ -119,11 +119,11 @@ def test_parse_function_exception() -> None:
"""
parser = PkgbuildParser(BytesIO(b"var() echo hello world } "))
with pytest.raises(PkgbuildParserError):
assert list(parser.parse())
list(parser.parse())
parser = PkgbuildParser(BytesIO(b"var() { echo hello world"))
with pytest.raises(PkgbuildParserError):
assert list(parser.parse())
list(parser.parse())
def test_parse_function_inner_shell() -> None:
@@ -50,7 +50,7 @@ def test_from_io(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> None:
must correctly load from io
"""
load_mock = mocker.patch("ahriman.core.alpm.pkgbuild_parser.PkgbuildParser.parse",
return_value=pkgbuild_ahriman.fields.values())
return_value=iter(pkgbuild_ahriman.fields.values()))
assert Pkgbuild.from_io(BytesIO(b"mock")) == pkgbuild_ahriman
load_mock.assert_called_once_with()
@@ -60,9 +60,9 @@ def test_from_io_pkgbase(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> N
must assign missing pkgbase if pkgname is presented
"""
mocker.patch("ahriman.core.alpm.pkgbuild_parser.PkgbuildParser.parse", side_effect=[
[value for key, value in pkgbuild_ahriman.fields.items() if key not in ("pkgbase",)],
[value for key, value in pkgbuild_ahriman.fields.items() if key not in ("pkgbase", "pkgname",)],
[value for key, value in pkgbuild_ahriman.fields.items()] + [PkgbuildPatch("pkgbase", "pkgbase")],
iter([value for key, value in pkgbuild_ahriman.fields.items() if key not in ("pkgbase",)]),
iter([value for key, value in pkgbuild_ahriman.fields.items() if key not in ("pkgbase", "pkgname",)]),
iter([value for key, value in pkgbuild_ahriman.fields.items()] + [PkgbuildPatch("pkgbase", "pkgbase")]),
])
assert Pkgbuild.from_io(BytesIO(b"mock"))["pkgbase"] == pkgbuild_ahriman["pkgname"]
@@ -75,7 +75,7 @@ def test_from_io_empty(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> Non
must skip empty patches
"""
mocker.patch("ahriman.core.alpm.pkgbuild_parser.PkgbuildParser.parse",
return_value=list(pkgbuild_ahriman.fields.values()) + [PkgbuildPatch("", "")])
return_value=iter(list(pkgbuild_ahriman.fields.values()) + [PkgbuildPatch("", "")]))
assert Pkgbuild.from_io(BytesIO(b"mock")) == pkgbuild_ahriman