Skip to content

Commit 485ee9b

Browse files
committed
EAPI: add functionality for handling PMS eapis
This adds eapi.get_PMS_eapis? functionality which is intended to simplify any tooling requiring PMS EAPIs to have that access. While doing this, add a test_fixture to protect against bleed through from test_eapi, and wire the ebd/Makefile to defer to pkgcore rather than having it's own list of EAPIs. Signed-off-by: Brian Harring <ferringb@gmail.com>
1 parent 98d46a3 commit 485ee9b

File tree

3 files changed

+114
-13
lines changed

3 files changed

+114
-13
lines changed

data/lib/pkgcore/ebd/Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ PYTHONPATH ?= ../../../../src
33
PYTHON_CALL = PYTHONPATH="$(PYTHONPATH)" PYTHONDONTWRITEBYTECODE=1 $(PYTHON)
44

55
TARGET = .generated
6-
KNOWN_EAPI = 0 1 2 3 4 5 6 7 8
6+
KNOWN_EAPI = $(shell $(PYTHON_CALL) -c 'from pkgcore.ebuild.eapi import EAPI; print(" ".join(EAPI.known_eapis))')
77

88
all: funcs cmds libs
99

10+
.PHONY: eapis
11+
eapis:
12+
@echo $(KNOWN_EAPI)
13+
1014
clean:
1115
$(RM) -r $(TARGET)
1216

src/pkgcore/ebuild/eapi.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from snakeoil.osutils import pjoin
1313
from snakeoil.process.spawn import bash_version
1414

15+
from typing import Iterable
16+
1517
LATEST_PMS_EAPI_VER = "8"
1618

1719

@@ -155,6 +157,7 @@ def __init__(
155157
archive_exts=(),
156158
optionals=None,
157159
ebd_env_options=None,
160+
pms=False,
158161
):
159162
sf = object.__setattr__
160163

@@ -194,6 +197,7 @@ def __init__(
194197
if ebd_env_options is None:
195198
ebd_env_options = ()
196199
sf(self, "_ebd_env_options", ebd_env_options)
200+
sf(self, "pms", pms)
197201

198202
@classmethod
199203
def register(cls, *args, **kwds):
@@ -479,8 +483,13 @@ def is_valid_use_flag(self, s: str) -> bool:
479483
return _valid_use_flag.match(s) is not None
480484

481485

482-
def get_eapi(magic, suppress_unsupported=True):
483-
"""Return EAPI object for a given identifier."""
486+
def get_eapi(magic, suppress_unsupported=True) -> None | EAPI:
487+
"""Return EAPI object for a given identifier.
488+
489+
If given suppress_unsupported, generate a fake EAPI and return that.
490+
Use sparingly, this implicitly means pkgcore doesn't know the rules
491+
for that metadata- so it's likely going to be wrong.
492+
"""
484493
if _valid_EAPI_regex.match(magic) is None:
485494
eapi_str = f" {magic!r}" if magic else ""
486495
raise ValueError(f"invalid EAPI{eapi_str}")
@@ -490,9 +499,21 @@ def get_eapi(magic, suppress_unsupported=True):
490499
if eapi is None:
491500
eapi = EAPI(magic=magic, optionals={"is_supported": False})
492501
EAPI.unknown_eapis[eapi._magic] = eapi
502+
493503
return eapi
494504

495505

506+
def get_PMS_eapi(magic) -> None | EAPI:
507+
"""If a PMS EAPI is supported, return it."""
508+
eapi = get_eapi(magic, suppress_unsupported=False)
509+
return eapi if eapi.pms else None
510+
511+
512+
def get_PMS_eapis() -> Iterable[EAPI]:
513+
"""Returns all known EAPIs that are defined by PMS"""
514+
return (eapi for eapi in EAPI.known_eapis.values() if eapi.pms)
515+
516+
496517
def _shorten_phase_name(func_name):
497518
if func_name.startswith(("src_", "pkg_")):
498519
return func_name[4:]
@@ -633,6 +654,7 @@ def _combine_dicts(*mappings):
633654
archive_exts=common_archive_exts,
634655
optionals=eapi_optionals,
635656
ebd_env_options=common_env_optionals,
657+
pms=True,
636658
)
637659

638660
eapi1 = EAPI.register(
@@ -654,6 +676,7 @@ def _combine_dicts(*mappings):
654676
),
655677
),
656678
ebd_env_options=eapi0._ebd_env_options,
679+
pms=True,
657680
)
658681

659682
eapi2 = EAPI.register(
@@ -682,6 +705,7 @@ def _combine_dicts(*mappings):
682705
),
683706
),
684707
ebd_env_options=eapi1._ebd_env_options,
708+
pms=True,
685709
)
686710

687711
eapi3 = EAPI.register(
@@ -702,6 +726,7 @@ def _combine_dicts(*mappings):
702726
),
703727
),
704728
ebd_env_options=eapi2._ebd_env_options,
729+
pms=True,
705730
)
706731

707732
eapi4 = EAPI.register(
@@ -731,6 +756,7 @@ def _combine_dicts(*mappings):
731756
),
732757
),
733758
ebd_env_options=eapi3._ebd_env_options,
759+
pms=True,
734760
)
735761

736762
eapi5 = EAPI.register(
@@ -757,6 +783,7 @@ def _combine_dicts(*mappings):
757783
),
758784
),
759785
ebd_env_options=eapi4._ebd_env_options,
786+
pms=True,
760787
)
761788

762789
eapi6 = EAPI.register(
@@ -782,6 +809,7 @@ def _combine_dicts(*mappings):
782809
),
783810
),
784811
ebd_env_options=eapi5._ebd_env_options,
812+
pms=True,
785813
)
786814

787815
eapi7 = EAPI.register(
@@ -810,6 +838,7 @@ def _combine_dicts(*mappings):
810838
),
811839
),
812840
ebd_env_options=eapi6._ebd_env_options,
841+
pms=True,
813842
)
814843

815844
eapi8 = EAPI.register(
@@ -846,4 +875,5 @@ def _combine_dicts(*mappings):
846875
),
847876
),
848877
ebd_env_options=eapi7._ebd_env_options,
878+
pms=True,
849879
)

tests/ebuild/test_eapi.py

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,35 @@
44
import pytest
55
from pkgcore.const import EBD_PATH
66
from pkgcore.ebuild import eapi
7-
from pkgcore.ebuild.eapi import EAPI, eapi6, get_eapi
7+
from pkgcore.ebuild.eapi import EAPI, get_eapi
8+
9+
10+
# Seperate these so we can test the fixture implementation.
11+
def _protect_eapi_registration():
12+
"""Protect EAPI.known_eapi so any test manipulations can't persist"""
13+
prior = EAPI.known_eapis.copy()
14+
yield
15+
EAPI.known_eapis.clear()
16+
EAPI.known_eapis.update(prior)
17+
18+
19+
protect_eapi_registration = pytest.fixture(scope="function", autouse=True)(
20+
_protect_eapi_registration
21+
)
22+
23+
24+
def test_eapi_registry_fixture():
25+
prior = set(list(EAPI.known_eapis))
26+
fixture = _protect_eapi_registration()
27+
# start the fixture
28+
next(fixture)
29+
assert prior == set(EAPI.known_eapis), EAPI.known_eapis
30+
# known_eapis is a weakval dict, thus we have to hold the reference
31+
_x = EAPI.register("foon")
32+
assert len(EAPI.known_eapis) - 1 == len(prior)
33+
# finish the fixture via exausting the iterator
34+
list(fixture)
35+
assert set(EAPI.known_eapis) == prior
836

937

1038
def test_get_eapi():
@@ -15,21 +43,59 @@ def test_get_eapi():
1543
assert unknown_eapi == get_eapi("unknown")
1644

1745
# known EAPI
18-
eapi = get_eapi("6")
19-
assert eapi6 == eapi
46+
assert get_eapi("6") == eapi.eapi6
47+
48+
49+
def test_get_PMS_eapi():
50+
# test PMS filtration
51+
assert get_eapi("6") is eapi.eapi6
52+
# hold the reference, known_eapis is weakval
53+
temp = EAPI.register("1234")
54+
# confirm it's visable
55+
assert get_eapi("1234", suppress_unsupported=False) is temp
56+
assert eapi.get_PMS_eapi("1234") is None
57+
temp2 = EAPI.register("9999", pms=True)
58+
assert eapi.get_PMS_eapi("9999") is temp2
59+
60+
61+
def test_get_PMS_eapis():
62+
pms_eapis = set(eapi.get_PMS_eapis())
63+
expected = set(x for x in EAPI.known_eapis.values() if x.pms)
64+
assert pms_eapis == expected
65+
66+
67+
def test_get_latest_pms_eapi():
68+
# if it's not in there, the magic constant isn't in alignment
69+
assert eapi.get_latest_PMS_eapi() in list(eapi.get_PMS_eapis())
70+
71+
# Note, this can false positive while a new EAPI is being developed. If this
72+
# is an actual issue, then introduce a flag on EAPI objects that indicates 'latest pms',
73+
# and update get_latest_PMS_eapi to scan for that, and register() to block duplicate.
74+
assert (
75+
eapi.get_latest_PMS_eapi()
76+
is sorted(
77+
(x for x in EAPI.known_eapis.values() if x.pms),
78+
key=lambda e: int(e._magic),
79+
reverse=True,
80+
)[0]
81+
)
2082

2183

2284
class TestEAPI:
85+
def test_pms_default_off(self):
86+
assert EAPI("asdf").pms == False
87+
assert EAPI.register("asdf").pms == False
88+
2389
def test_register(self, tmp_path):
2490
# re-register known EAPI
2591
with pytest.raises(ValueError):
2692
EAPI.register(magic="0")
2793

2894
mock_ebd_temp = str(shutil.copytree(EBD_PATH, tmp_path / "ebd"))
29-
with mock.patch(
30-
"pkgcore.ebuild.eapi.bash_version"
31-
) as bash_version, mock.patch.dict(eapi.EAPI.known_eapis), mock.patch(
32-
"pkgcore.ebuild.eapi.const.EBD_PATH", mock_ebd_temp
95+
with (
96+
mock.patch("pkgcore.ebuild.eapi.bash_version") as bash_version,
97+
mock.patch.dict(eapi.EAPI.known_eapis),
98+
mock.patch("pkgcore.ebuild.eapi.const.EBD_PATH", mock_ebd_temp),
3399
):
34100
# inadequate bash version
35101
bash_version.return_value = "3.1"
@@ -49,11 +115,12 @@ def test_register(self, tmp_path):
49115
assert test_eapi._magic == "test1"
50116

51117
def test_is_supported(self, tmp_path, caplog):
52-
assert eapi6.is_supported
118+
assert eapi.eapi6.is_supported
53119

54120
mock_ebd_temp = str(shutil.copytree(EBD_PATH, tmp_path / "ebd"))
55-
with mock.patch.dict(eapi.EAPI.known_eapis), mock.patch(
56-
"pkgcore.ebuild.eapi.const.EBD_PATH", mock_ebd_temp
121+
with (
122+
mock.patch.dict(eapi.EAPI.known_eapis),
123+
mock.patch("pkgcore.ebuild.eapi.const.EBD_PATH", mock_ebd_temp),
57124
):
58125
# partially supported EAPI is flagged as such
59126
test_eapi = EAPI.register("test", optionals={"is_supported": False})

0 commit comments

Comments
 (0)