Skip to content
Merged
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
24 changes: 12 additions & 12 deletions libs/code/deepagents_code/extras_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -1085,11 +1085,7 @@ def format_known_extras() -> str:


ExtrasStatus = dict[str, list[tuple[str, str]]]
"""Mapping from extra name to `(package, installed_version)` tuples.

Only packages that are actually installed are included. Extras whose
declared packages are all missing are omitted entirely.
"""
"""Mapping from ready extra names to `(package, installed_version)` tuples."""


@dataclass(frozen=True)
Expand Down Expand Up @@ -1128,13 +1124,13 @@ def _extract_extra_name(marker_str: str) -> str | None:
def get_extras_status(
distribution_name: str = "deepagents-code",
) -> ExtrasStatus:
"""Return installed optional dependencies grouped by extra.
"""Return ready optional dependencies grouped by extra.

Reads `Requires-Dist` metadata from the named distribution, groups the
entries gated by `extra == "..."` markers under their extra name, and
resolves each package's installed version via `importlib.metadata`.
Packages that are not installed are omitted; extras whose entire
package list is absent are dropped.
Partially installed extras are omitted so shared transitive dependencies
do not make unavailable integrations appear installed.

Composite meta-extras that only bundle other extras (see
`_COMPOSITE_EXTRAS`) and self-references to the distribution itself
Expand All @@ -1144,13 +1140,12 @@ def get_extras_status(
distribution_name: Name of the installed distribution to inspect.

Returns:
Mapping from extra name to a sorted list of `(package, version)`
tuples for packages that are currently installed. An empty
mapping is returned when the distribution itself is not found.
Mapping from ready extra names to sorted `(package, version)` tuples. An
empty mapping is returned when the distribution itself is not found.
"""
result: ExtrasStatus = {}
for extra in get_optional_dependency_status(distribution_name):
if extra.installed:
if extra.ready:
result[extra.name] = list(extra.installed)
return result

Expand All @@ -1162,6 +1157,11 @@ def installed_extra_names(
) -> set[str]:
"""Return extras with at least one installed dependency.

Deliberately looser than `get_extras_status`, which requires every declared
package to be present. Callers here rebuild install commands, so a partially
installed extra must still be preserved across an upgrade; dropping it would
silently uninstall what the user asked for. Do not harmonize the two.

Args:
distribution_name: Name of the installed distribution to inspect.
strict: Raise when the distribution metadata cannot be read or parsed
Expand Down
41 changes: 32 additions & 9 deletions libs/code/tests/unit_tests/test_extras_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,20 +130,43 @@ def test_real_distribution_skips_self_references() -> None:
assert pkg_name.lower() != "deepagents-code"


def test_missing_packages_are_omitted() -> None:
def test_incomplete_extras_are_omitted() -> None:
mock_dist = MagicMock()
mock_dist.requires = [
"langchain-anthropic>=1.0.0 ; extra == 'anthropic'",
"fake-absent-package>=1.0.0 ; extra == 'custom'",
"partially-present>=1.0.0 ; extra == 'mixed'",
"also-missing>=1.0.0 ; extra == 'mixed'",
"aiohttp>=3.14.3 ; extra == 'nvidia'",
"langchain-nvidia-ai-endpoints>=1.4.3 ; extra == 'nvidia'",
]

def fake_version(name: str) -> str:
if name == "langchain-anthropic":
return "1.4.0"
if name == "partially-present":
return "2.0.0"
if name == "aiohttp":
return "3.14.3"
raise PackageNotFoundError(name)

with (
patch("deepagents_code.extras_info.distribution", return_value=mock_dist),
patch("deepagents_code.extras_info.pkg_version", side_effect=fake_version),
):
extras = get_extras_status()

assert extras == {"anthropic": [("langchain-anthropic", "1.4.0")]}


def test_complete_multi_package_extras_are_kept() -> None:
mock_dist = MagicMock()
mock_dist.requires = [
"aiohttp>=3.14.3 ; extra == 'nvidia'",
"langchain-nvidia-ai-endpoints>=1.4.3 ; extra == 'nvidia'",
]

def fake_version(name: str) -> str:
if name == "aiohttp":
return "3.14.3"
if name == "langchain-nvidia-ai-endpoints":
return "1.4.3"
raise PackageNotFoundError(name)

with (
Expand All @@ -152,11 +175,11 @@ def fake_version(name: str) -> str:
):
extras = get_extras_status()

# Fully absent extras disappear; partially present extras keep only
# the installed packages.
assert extras == {
"anthropic": [("langchain-anthropic", "1.4.0")],
"mixed": [("partially-present", "2.0.0")],
"nvidia": [
("aiohttp", "3.14.3"),
("langchain-nvidia-ai-endpoints", "1.4.3"),
],
}


Expand Down