Skip to content

Commit b48c999

Browse files
committed
Add detail_context and PulpMasterContext
[noissue]
1 parent 4774b67 commit b48c999

7 files changed

Lines changed: 111 additions & 73 deletions

File tree

CHANGES/pulp-glue/+cast.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added `detail_context` to master-detail contexts.

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ tests/cli.toml:
3434
@echo "In order to configure the tests to talk to your test server, you might need to edit $@ ."
3535

3636
test: | tests/cli.toml
37-
pytest -v tests
37+
pytest -v tests pulp-glue/tests
3838

3939
docs:
4040
pulp-docs build

pulp-glue/pulp_glue/common/context.py

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,36 @@ def needs_capability(self, capability: str) -> None:
10521052
)
10531053

10541054

1055-
class PulpRemoteContext(PulpEntityContext):
1055+
class PulpMasterContext(PulpEntityContext):
1056+
TYPE_REGISTRY: t.Final[t.ClassVar[t.Dict[str, t.Type["t.Self"]]]]
1057+
1058+
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1059+
super().__init_subclass__(**kwargs)
1060+
if not hasattr(cls, "RESOURCE_TYPE"):
1061+
cls.TYPE_REGISTRY = {}
1062+
elif hasattr(cls, "PLUGIN"):
1063+
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
1064+
1065+
def detail_context(self, pulp_href: str) -> "t.Self":
1066+
"""
1067+
Provide a detail context for a matching href.
1068+
"""
1069+
m = re.search(self.HREF_PATTERN, pulp_href)
1070+
if m is None:
1071+
raise PulpException(f"'{pulp_href}' is not an href for {self.ENTITY}.")
1072+
plugin = m.group("plugin")
1073+
resource_type = m.group("resource_type")
1074+
try:
1075+
detail_class = self.TYPE_REGISTRY[f"{plugin}:{resource_type}"]
1076+
except KeyError:
1077+
raise PulpException(
1078+
f"{self.ENTITY} with plugin '{plugin}' and"
1079+
f"resource type '{resource_type}' is unknown."
1080+
)
1081+
return detail_class(self.pulp_ctx, pulp_href=pulp_href)
1082+
1083+
1084+
class PulpRemoteContext(PulpMasterContext):
10561085
"""
10571086
Base class for remote contexts.
10581087
"""
@@ -1078,27 +1107,15 @@ class PulpRemoteContext(PulpEntityContext):
10781107
"sock_read_timeout",
10791108
"rate_limit",
10801109
}
1081-
TYPE_REGISTRY: t.Final[t.Dict[str, t.Type["PulpRemoteContext"]]] = {}
1082-
1083-
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1084-
super().__init_subclass__(**kwargs)
1085-
if hasattr(cls, "PLUGIN") and hasattr(cls, "RESOURCE_TYPE"):
1086-
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
10871110

10881111

1089-
class PulpPublicationContext(PulpEntityContext):
1112+
class PulpPublicationContext(PulpMasterContext):
10901113
"""Base class for publication contexts."""
10911114

10921115
ENTITY = _("publication")
10931116
ENTITIES = _("publications")
10941117
ID_PREFIX = "publications"
10951118
HREF_PATTERN = r"publications/(?P<plugin>[\w\-_]+)/(?P<resource_type>[\w\-_]+)/"
1096-
TYPE_REGISTRY: t.Final[t.Dict[str, t.Type["PulpPublicationContext"]]] = {}
1097-
1098-
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1099-
super().__init_subclass__(**kwargs)
1100-
if hasattr(cls, "PLUGIN") and hasattr(cls, "RESOURCE_TYPE"):
1101-
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
11021119

11031120
def list(self, limit: int, offset: int, parameters: t.Dict[str, t.Any]) -> t.List[t.Any]:
11041121
if parameters.get("repository") is not None:
@@ -1108,20 +1125,14 @@ def list(self, limit: int, offset: int, parameters: t.Dict[str, t.Any]) -> t.Lis
11081125
return super().list(limit, offset, parameters)
11091126

11101127

1111-
class PulpDistributionContext(PulpEntityContext):
1128+
class PulpDistributionContext(PulpMasterContext):
11121129
"""Base class for distribution contexts."""
11131130

11141131
ENTITY = _("distribution")
11151132
ENTITIES = _("distributions")
11161133
ID_PREFIX = "distributions"
11171134
HREF_PATTERN = r"distributions/(?P<plugin>[\w\-_]+)/(?P<resource_type>[\w\-_]+)/"
11181135
NULLABLES = {"content_guard", "publication", "remote", "repository", "repository_version"}
1119-
TYPE_REGISTRY: t.Final[t.Dict[str, t.Type["PulpDistributionContext"]]] = {}
1120-
1121-
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1122-
super().__init_subclass__(**kwargs)
1123-
if hasattr(cls, "PLUGIN") and hasattr(cls, "RESOURCE_TYPE"):
1124-
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
11251136

11261137

11271138
class PulpRepositoryVersionContext(PulpEntityContext):
@@ -1180,7 +1191,7 @@ def repair(self) -> t.Any:
11801191
return self.call("repair", parameters={self.HREF: self.pulp_href}, body={})
11811192

11821193

1183-
class PulpRepositoryContext(PulpEntityContext):
1194+
class PulpRepositoryContext(PulpMasterContext):
11841195
"""Base class for repository contexts."""
11851196

11861197
ENTITY = _("repository")
@@ -1189,12 +1200,6 @@ class PulpRepositoryContext(PulpEntityContext):
11891200
ID_PREFIX = "repositories"
11901201
VERSION_CONTEXT: t.ClassVar[t.Type[PulpRepositoryVersionContext]] = PulpRepositoryVersionContext
11911202
NULLABLES = {"description", "retain_repo_versions"}
1192-
TYPE_REGISTRY: t.Final[t.Dict[str, t.Type["PulpRepositoryContext"]]] = {}
1193-
1194-
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1195-
super().__init_subclass__(**kwargs)
1196-
if hasattr(cls, "PLUGIN") and hasattr(cls, "RESOURCE_TYPE"):
1197-
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
11981203

11991204
def get_version_context(
12001205
self,
@@ -1301,18 +1306,13 @@ def reclaim(
13011306
return self.call("reclaim_space_reclaim", body=body)
13021307

13031308

1304-
class PulpContentContext(PulpEntityContext):
1309+
class PulpContentContext(PulpMasterContext):
13051310
"""Base class for content contexts."""
13061311

13071312
ENTITY = _("content")
13081313
ENTITIES = _("content")
1314+
HREF_PATTERN = r"content/(?P<plugin>[\w\-_]+)/(?P<resource_type>[\w\-_]+)/"
13091315
ID_PREFIX = "content"
1310-
TYPE_REGISTRY: t.Final[t.Dict[str, t.Type["PulpContentContext"]]] = {}
1311-
1312-
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1313-
super().__init_subclass__(**kwargs)
1314-
if hasattr(cls, "PLUGIN") and hasattr(cls, "RESOURCE_TYPE"):
1315-
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
13161316

13171317
def upload(
13181318
self,
@@ -1353,38 +1353,26 @@ def upload(
13531353
return self.create(body=body)
13541354

13551355

1356-
class PulpACSContext(PulpEntityContext):
1356+
class PulpACSContext(PulpMasterContext):
13571357
"""Base class for ACS contexts."""
13581358

13591359
ENTITY = _("ACS")
13601360
ENTITIES = _("ACSes")
13611361
HREF_PATTERN = r"acs/(?P<plugin>[\w\-_]+)/(?P<resource_type>[\w\-_]+)/"
13621362
ID_PREFIX = "acs"
1363-
TYPE_REGISTRY: t.Final[t.Dict[str, t.Type["PulpACSContext"]]] = {}
1364-
1365-
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1366-
super().__init_subclass__(**kwargs)
1367-
if hasattr(cls, "PLUGIN") and hasattr(cls, "RESOURCE_TYPE"):
1368-
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
13691363

13701364
def refresh(self, href: t.Optional[str] = None) -> t.Any:
13711365
return self.call("refresh", parameters={self.HREF: href or self.pulp_href})
13721366

13731367

1374-
class PulpContentGuardContext(PulpEntityContext):
1368+
class PulpContentGuardContext(PulpMasterContext):
13751369
"""Base class for content guard contexts."""
13761370

13771371
ENTITY = "content guard"
13781372
ENTITIES = "content guards"
13791373
ID_PREFIX = "contentguards"
13801374
HREF_PATTERN = r"contentguards/(?P<plugin>[\w\-_]+)/(?P<resource_type>[\w\-_]+)/"
13811375
NULLABLES = {"description"}
1382-
TYPE_REGISTRY: t.Final[t.Dict[str, t.Type["PulpContentGuardContext"]]] = {}
1383-
1384-
def __init_subclass__(cls, **kwargs: t.Any) -> None:
1385-
super().__init_subclass__(**kwargs)
1386-
if hasattr(cls, "PLUGIN") and hasattr(cls, "RESOURCE_TYPE"):
1387-
cls.TYPE_REGISTRY[f"{cls.PLUGIN}:{cls.RESOURCE_TYPE}"] = cls
13881376

13891377

13901378
EntityFieldDefinition = t.Union[None, str, PulpEntityContext]

pulp-glue/tests/conftest.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import typing as t
2+
3+
import pytest
4+
5+
from pulp_glue.common.context import PulpContext
6+
from pulp_glue.common.openapi import BasicAuthProvider
7+
8+
9+
class PulpTestContext(PulpContext):
10+
# TODO check if we can just make the base class ignore echo.
11+
def echo(*args, **kwargs) -> None:
12+
return
13+
14+
15+
@pytest.fixture
16+
def pulp_ctx(pulp_cli_settings: t.Dict[str, t.Dict[str, t.Any]]) -> PulpContext:
17+
settings = pulp_cli_settings["cli"]
18+
return PulpTestContext(
19+
api_kwargs={
20+
"base_url": settings["base_url"],
21+
"auth_provider": BasicAuthProvider(settings.get("username"), settings.get("password")),
22+
},
23+
api_root=settings.get("api_root", "pulp/"),
24+
background_tasks=False,
25+
timeout=settings.get("timeout", 120),
26+
)
Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,18 @@
1-
import typing as t
21
from copy import deepcopy
32

43
import pytest
4+
55
from pulp_glue.common.context import _REGISTERED_API_QUIRKS, PulpContext
66

7+
pytestmark = pytest.mark.glue
8+
79

8-
@pytest.mark.glue
9-
def test_api_quirks_idempotent(
10-
pulp_cli_settings: t.Tuple[t.Any, t.Dict[str, t.Dict[str, t.Any]]]
11-
) -> None:
10+
def test_api_quirks_idempotent(pulp_ctx: PulpContext) -> None:
1211
"""
1312
Test, that the applied api quirks can be applied twice without failing.
1413
1514
This can let us hope they will not fail once the api is fixed upstream.
1615
"""
17-
settings = pulp_cli_settings[1]["cli"]
18-
pulp_ctx = PulpContext(
19-
api_kwargs={"base_url": settings["base_url"]},
20-
api_root=settings.get("api_root", "pulp/"),
21-
background_tasks=False,
22-
timeout=settings.get("timeout", 120),
23-
)
24-
2516
assert {
2617
"patch_content_in_query_filters",
2718
"patch_field_select_filters",
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import random
2+
import string
3+
import typing as t
4+
5+
import pytest
6+
7+
from pulp_glue.common.context import PulpContext, PulpRepositoryContext
8+
from pulp_glue.file.context import PulpFileRepositoryContext
9+
10+
pytestmark = pytest.mark.glue
11+
12+
13+
@pytest.fixture
14+
def file_repository(pulp_ctx: PulpContext) -> t.Dict[str, t.Any]:
15+
name = "".join(random.choices(string.ascii_letters, k=8))
16+
file_repository_ctx = PulpFileRepositoryContext(pulp_ctx)
17+
yield file_repository_ctx.create(body={"name": name})
18+
file_repository_ctx.delete()
19+
20+
21+
def test_detail_context(pulp_ctx: PulpContext, file_repository: t.Dict[str, t.Any]) -> None:
22+
master_ctx = PulpRepositoryContext(pulp_ctx)
23+
detail_ctx = master_ctx.detail_context(pulp_href=file_repository["pulp_href"])
24+
assert isinstance(detail_ctx, PulpFileRepositoryContext)
25+
assert detail_ctx.entity["name"] == file_repository["name"]

pytest_pulp_cli/__init__.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def pulp_cli_vars() -> t.Dict[str, str]:
8282

8383

8484
@pytest.fixture(scope="session")
85-
def pulp_cli_settings(tmp_path_factory: pytest.TempPathFactory) -> t.Tuple[pathlib.Path, t.Any]:
85+
def pulp_cli_settings() -> t.Dict[str, t.Dict[str, t.Any]]:
8686
"""
8787
This fixture will setup the config file once per session only.
8888
It is most likely not useful to be included standalone.
@@ -95,11 +95,18 @@ def pulp_cli_settings(tmp_path_factory: pytest.TempPathFactory) -> t.Tuple[pathl
9595
if os.environ.get("PULP_API_ROOT"):
9696
for key in settings:
9797
settings[key]["api_root"] = os.environ["PULP_API_ROOT"]
98+
return settings
99+
100+
101+
@pytest.fixture(scope="session")
102+
def pulp_cli_settings_path(
103+
tmp_path_factory: pytest.TempPathFactory, pulp_cli_settings: t.Dict[str, t.Dict[str, t.Any]]
104+
) -> pathlib.Path:
98105
settings_path = tmp_path_factory.mktemp("config", numbered=False)
99106
(settings_path / "pulp").mkdir(parents=True)
100107
with open(settings_path / "pulp" / "cli.toml", "w") as settings_file:
101-
toml.dump(settings, settings_file)
102-
return settings_path, settings
108+
toml.dump(pulp_cli_settings, settings_file)
109+
return settings_path
103110

104111

105112
@pytest.fixture(scope="session")
@@ -125,11 +132,12 @@ def pulp_cli_gnupghome(tmp_path_factory: pytest.TempPathFactory) -> pathlib.Path
125132

126133
@pytest.fixture
127134
def pulp_cli_env(
128-
pulp_cli_settings: t.Tuple[pathlib.Path, t.Dict[str, t.Any]],
135+
pulp_cli_settings: t.Dict[str, t.Dict[str, t.Any]],
136+
pulp_cli_settings_path: pathlib.Path,
129137
pulp_cli_vars: t.Dict[str, str],
130138
pulp_cli_gnupghome: pathlib.Path,
131139
monkeypatch: pytest.MonkeyPatch,
132-
) -> t.Dict[str, t.Any]:
140+
) -> None:
133141
"""
134142
This fixture will set up the environment for cli commands by:
135143
@@ -138,16 +146,15 @@ def pulp_cli_env(
138146
* pointing XDG_CONFIG_HOME accordingly
139147
* supplying other useful environment vars
140148
"""
141-
settings_path, settings = pulp_cli_settings
142-
monkeypatch.setenv("XDG_CONFIG_HOME", str(settings_path))
143-
monkeypatch.setenv("PULP_BASE_URL", settings["cli"]["base_url"])
144-
monkeypatch.setenv("VERIFY_SSL", str(settings["cli"].get("verify_ssl", True)).lower())
149+
monkeypatch.setenv("XDG_CONFIG_HOME", str(pulp_cli_settings_path))
150+
monkeypatch.setenv("PULP_BASE_URL", pulp_cli_settings["cli"]["base_url"])
151+
monkeypatch.setenv("VERIFY_SSL", str(pulp_cli_settings["cli"].get("verify_ssl", True)).lower())
145152
monkeypatch.setenv("GNUPGHOME", str(pulp_cli_gnupghome))
146153

147154
for key, value in pulp_cli_vars.items():
148155
monkeypatch.setenv(key, value)
149156

150-
return settings
157+
return None
151158

152159

153160
if "PULP_LOGGING" in os.environ:

0 commit comments

Comments
 (0)