Skip to content

Commit 9a322ed

Browse files
committed
Implement sparse keyword for containers.list()
Defaults to True for Libpod calls and False for Docker Compat calls This ensures: 1. Docker API compatibility 2. No breaking changes with Libpod It also providest 1. Possibility to inspect containers on demand for list calls 2. Safer behavior if container hangs 3. Fewer expensive calls to the API by default Signed-off-by: Nicola Sella <[email protected]>
1 parent c4aad1b commit 9a322ed

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

podman/domain/containers_manager.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,26 @@ def list(self, **kwargs) -> list[Container]:
6767
Give the container name or id.
6868
- since (str): Only containers created after a particular container.
6969
Give container name or id.
70-
sparse: Ignored
70+
sparse: If False, return basic container information without additional
71+
inspection requests. This improves performance when listing many containers
72+
but might provide less detail. You can call Container.reload() on individual
73+
containers later to retrieve complete attributes. Default: True.
74+
When Docker compatibility is enabled with `compatible=True`: Default: False.
7175
ignore_removed: If True, ignore failures due to missing containers.
7276
7377
Raises:
7478
APIError: when service returns an error
7579
"""
80+
compatible = kwargs.get("compatible", False)
81+
# Libpod behavior: default is sparse=True and containers require a reload call
82+
# to get full details
83+
# Docker behavior: default is sparse=False and containers are inspected during
84+
# list calls
7685
params = {
7786
"all": kwargs.get("all"),
7887
"filters": kwargs.get("filters", {}),
7988
"limit": kwargs.get("limit"),
89+
"sparse": kwargs.get("sparse", not compatible),
8090
}
8191
if "before" in kwargs:
8292
params["filters"]["before"] = kwargs.get("before")
@@ -86,10 +96,21 @@ def list(self, **kwargs) -> list[Container]:
8696
# filters formatted last because some kwargs may need to be mapped into filters
8797
params["filters"] = api.prepare_filters(params["filters"])
8898

89-
response = self.client.get("/containers/json", params=params)
99+
response = self.client.get("/containers/json", params=params, compatible=compatible)
90100
response.raise_for_status()
91101

92-
return [self.prepare_model(attrs=i) for i in response.json()]
102+
containers: list[Container] = [self.prepare_model(attrs=i) for i in response.json()]
103+
104+
# If sparse is False (default), reload each container to get full details
105+
if not kwargs.get("sparse", False):
106+
for container in containers:
107+
try:
108+
container.reload()
109+
except APIError:
110+
# Skip containers that might have been removed
111+
pass
112+
113+
return containers
93114

94115
def prune(self, filters: Mapping[str, str] = None) -> dict[str, Any]:
95116
"""Delete stopped containers.

podman/tests/unit/test_containersmanager.py

+16
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ def test_list_no_filters(self, mock):
154154
actual[1].id, "6dc84cc0a46747da94e4c1571efcc01a756b4017261440b4b8985d37203c3c03"
155155
)
156156

157+
@requests_mock.Mocker()
158+
def test_list_sparse_with_compat(self, mock):
159+
mock.get(
160+
tests.COMPATIBLE_URL + "/containers/json?sparse=False",
161+
json=[FIRST_CONTAINER, SECOND_CONTAINER],
162+
)
163+
actual = self.client.containers.list(compatible=True)
164+
self.assertIsInstance(actual, list)
165+
166+
self.assertEqual(
167+
actual[0].id, "87e1325c82424e49a00abdd4de08009eb76c7de8d228426a9b8af9318ced5ecd"
168+
)
169+
self.assertEqual(
170+
actual[1].id, "6dc84cc0a46747da94e4c1571efcc01a756b4017261440b4b8985d37203c3c03"
171+
)
172+
157173
@requests_mock.Mocker()
158174
def test_prune(self, mock):
159175
mock.post(

0 commit comments

Comments
 (0)