Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/fix-extends-dependents-set-merge.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `AttributeError: 'set' object has no attribute 'extend'` in `resolve_extends()` when a service used as an `extends:` target was itself depended on by another service.
1 change: 1 addition & 0 deletions podman_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -2343,6 +2343,7 @@ def resolve_extends(
del from_service["extends"]
except KeyError:
pass
from_service.pop(DependField.DEPENDENTS, None)
new_service = rec_merge({}, from_service, service)
services[name] = new_service

Expand Down
46 changes: 46 additions & 0 deletions tests/unit/test_resolve_extends_dependents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# SPDX-License-Identifier: GPL-2.0
from __future__ import annotations

import unittest
from typing import Any

from podman_compose import flat_deps
from podman_compose import normalize
from podman_compose import resolve_extends


class TestResolveExtendsDependents(unittest.TestCase):
"""Regression test: resolve_extends used to crash with
AttributeError: 'set' object has no attribute 'extend'
whenever a service that is itself depended on by another service
was also used as the target of an `extends:`.

flat_deps(services, with_extends=True) populates a "_dependents" set on
every service that has a dependent. resolve_extends() already stripped
the internal "_deps" set from the extends source before merging it, but
not "_dependents" - so when both the extends source and the extending
service carried a "_dependents" set, rec_merge_one() tried to
list.extend() a set and blew up.
"""

def test_extends_target_with_dependents_does_not_crash(self) -> None:
compose: dict[str, Any] = {
"services": {
"base": {"image": "busybox"},
"app": {
"extends": {"service": "base"},
"depends_on": ["base"],
},
"app2": {
"image": "busybox",
"depends_on": ["app"],
},
}
}
services = normalize(compose)["services"]
flat_deps(services, with_extends=True)

# should not raise AttributeError: 'set' object has no attribute 'extend'
resolve_extends(services, list(services.keys()), {})

self.assertEqual(services["app"]["image"], "busybox")