Skip to content

Commit 721e64b

Browse files
committed
Add NEVRA utility class
Signed-off-by: Nikola Forró <[email protected]>
1 parent 229b26f commit 721e64b

File tree

3 files changed

+173
-3
lines changed

3 files changed

+173
-3
lines changed

specfile/constants.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,92 @@
102102
"prereq",
103103
"orderwithrequires",
104104
}
105+
106+
# canonical architecture names as defined in rpmrc.in in RPM source
107+
ARCH_NAMES = {
108+
"aarch64",
109+
"alpha",
110+
"alphaev5",
111+
"alphaev56",
112+
"alphaev6",
113+
"alphaev67",
114+
"alphapca56",
115+
"amd64",
116+
"armv3l",
117+
"armv4b",
118+
"armv4l",
119+
"armv5tejl",
120+
"armv5tel",
121+
"armv5tl",
122+
"armv6hl",
123+
"armv6l",
124+
"armv7hl",
125+
"armv7hnl",
126+
"armv7l",
127+
"armv8hl",
128+
"armv8l",
129+
"atariclone",
130+
"atarist",
131+
"atariste",
132+
"ataritt",
133+
"athlon",
134+
"em64t",
135+
"falcon",
136+
"geode",
137+
"hades",
138+
"i370",
139+
"i386",
140+
"i486",
141+
"i586",
142+
"i686",
143+
"ia32e",
144+
"ia64",
145+
"IP",
146+
"loongarch64",
147+
"m68k",
148+
"m68kmint",
149+
"milan",
150+
"mips",
151+
"mips64",
152+
"mips64el",
153+
"mips64r6",
154+
"mips64r6el",
155+
"mipsel",
156+
"mipsr6",
157+
"mipsr6el",
158+
"pentium3",
159+
"pentium4",
160+
"ppc",
161+
"ppc32dy4",
162+
"ppc64",
163+
"ppc64iseries",
164+
"ppc64le",
165+
"ppc64p7",
166+
"ppc64pseries",
167+
"ppc8260",
168+
"ppc8560",
169+
"ppciseries",
170+
"ppcpseries",
171+
"riscv",
172+
"riscv64",
173+
"rs6000",
174+
"s390",
175+
"s390x",
176+
"sh",
177+
"sh3",
178+
"sh4",
179+
"sh4a",
180+
"sparc",
181+
"sparc64",
182+
"sparc64v",
183+
"sparcv8",
184+
"sparcv9",
185+
"sparcv9v",
186+
"sun4",
187+
"sun4c",
188+
"sun4d",
189+
"sun4m",
190+
"sun4u",
191+
"x86_64",
192+
"xtensa",
193+
}

specfile/utils.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
import tempfile
1111
from typing import Iterator, List
1212

13+
from specfile.constants import ARCH_NAMES
1314
from specfile.exceptions import SpecfileException
1415

1516

1617
class EVR(collections.abc.Hashable):
1718
"""Class representing Epoch-Version-Release combination."""
1819

20+
_regex = r"(?:(\d+):)?([^-]+?)(?:-([^-]+))?"
21+
1922
def __init__(self, *, version: str, release: str = "", epoch: int = 0) -> None:
2023
self.epoch = epoch
2124
self.version = version
@@ -42,7 +45,7 @@ def __str__(self) -> str:
4245

4346
@classmethod
4447
def from_string(cls, evr: str) -> "EVR":
45-
m = re.match(r"^(?:(\d+):)?([^-]+?)(?:-([^-]+))?$", evr)
48+
m = re.match(f"^{cls._regex}$", evr)
4649
if not m:
4750
raise SpecfileException("Invalid EVR string.")
4851
e, v, r = m.groups()
@@ -52,6 +55,8 @@ def from_string(cls, evr: str) -> "EVR":
5255
class NEVR(EVR):
5356
"""Class representing Name-Epoch-Version-Release combination."""
5457

58+
_regex = r"(.+?)-" + EVR._regex
59+
5560
def __init__(
5661
self, *, name: str, version: str, release: str = "", epoch: int = 0
5762
) -> None:
@@ -72,13 +77,49 @@ def __str__(self) -> str:
7277

7378
@classmethod
7479
def from_string(cls, nevr: str) -> "NEVR":
75-
m = re.match(r"^(.+?)-(?:(\d+):)?([^-]+?)(?:-([^-]+))?$", nevr)
80+
m = re.match(f"^{cls._regex}$", nevr)
7681
if not m:
7782
raise SpecfileException("Invalid NEVR string.")
7883
n, e, v, r = m.groups()
7984
return cls(name=n, epoch=int(e) if e else 0, version=v, release=r or "")
8085

8186

87+
class NEVRA(NEVR):
88+
"""Class representing Name-Epoch-Version-Release-Arch combination."""
89+
90+
_arches_regex = "(" + "|".join(re.escape(a) for a in ARCH_NAMES | {"noarch"}) + ")"
91+
_regex = NEVR._regex + r"\." + _arches_regex
92+
93+
def __init__(
94+
self, *, name: str, version: str, release: str, arch: str, epoch: int = 0
95+
) -> None:
96+
if not re.match(f"^{self._arches_regex}$", arch):
97+
raise SpecfileException("Invalid architecture name.")
98+
self.arch = arch
99+
super().__init__(name=name, epoch=epoch, version=version, release=release)
100+
101+
def _key(self) -> tuple:
102+
return self.name, self.epoch, self.version, self.release, self.arch
103+
104+
def __repr__(self) -> str:
105+
return (
106+
f"NEVRA(name='{self.name}', epoch={self.epoch}, "
107+
f"version='{self.version}', release='{self.release}', "
108+
f"arch='{self.arch}')"
109+
)
110+
111+
def __str__(self) -> str:
112+
return super().__str__() + f".{self.arch}"
113+
114+
@classmethod
115+
def from_string(cls, nevra: str) -> "NEVRA":
116+
m = re.match(f"^{cls._regex}$", nevra)
117+
if not m:
118+
raise SpecfileException("Invalid NEVRA string.")
119+
n, e, v, r, a = m.groups()
120+
return cls(name=n, epoch=int(e) if e else 0, version=v, release=r, arch=a)
121+
122+
82123
@contextlib.contextmanager
83124
def capture_stderr() -> Iterator[List[bytes]]:
84125
"""

tests/unit/test_utils.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pytest
55

6-
from specfile.utils import EVR, NEVR, get_filename_from_location
6+
from specfile.utils import EVR, NEVR, NEVRA, get_filename_from_location
77

88

99
@pytest.mark.parametrize(
@@ -82,3 +82,43 @@ def test_EVR_from_string(evr, result):
8282
)
8383
def test_NEVR_from_string(nevr, result):
8484
assert NEVR.from_string(nevr) == result
85+
86+
87+
@pytest.mark.parametrize(
88+
"nevra, result",
89+
[
90+
(
91+
"package-12.0-1.x86_64",
92+
NEVRA(name="package", version="12.0", release="1", arch="x86_64"),
93+
),
94+
(
95+
"package-2:56.8-5.aarch64",
96+
NEVRA(name="package", epoch=2, version="56.8", release="5", arch="aarch64"),
97+
),
98+
(
99+
"package-0.8.0-1.fc37.armv6hl",
100+
NEVRA(name="package", version="0.8.0", release="1.fc37", arch="armv6hl"),
101+
),
102+
(
103+
"package-0.5.0~rc2-1.el9.noarch",
104+
NEVRA(name="package", version="0.5.0~rc2", release="1.el9", arch="noarch"),
105+
),
106+
(
107+
"package-devel-7.3-0.2.rc1.fc38.i686",
108+
NEVRA(
109+
name="package-devel", version="7.3", release="0.2.rc1.fc38", arch="i686"
110+
),
111+
),
112+
(
113+
"package-7.3~rc1^20200701gdeadf00f-12.fc38.riscv",
114+
NEVRA(
115+
name="package",
116+
version="7.3~rc1^20200701gdeadf00f",
117+
release="12.fc38",
118+
arch="riscv",
119+
),
120+
),
121+
],
122+
)
123+
def test_NEVRA_from_string(nevra, result):
124+
assert NEVRA.from_string(nevra) == result

0 commit comments

Comments
 (0)