40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 | class PythonRuntimeEnvironment(RuntimeEnvironment):
_kiara_model_id: ClassVar = "info.runtime.python"
environment_type: Literal["python"]
python_version: str = Field(description="The version of Python.")
packages: List[PythonPackage] = Field(
description="The packages installed in the Python (virtual) environment."
)
# python_config: typing.Dict[str, str] = Field(
# description="Configuration details about the Python installation."
# )
def _create_renderable_for_field(
self, field_name: str, for_summary: bool = False
) -> Union[RenderableType, None]:
if field_name != "packages":
return extract_renderable(getattr(self, field_name))
if for_summary:
return ", ".join(p.name for p in self.packages)
table = Table(show_header=True, box=box.SIMPLE)
table.add_column("package name")
table.add_column("version")
for package in self.packages:
table.add_row(package.name, package.version)
return table
def _retrieve_sub_profile_env_data(self) -> Mapping[str, Any]:
only_packages = [p.name for p in self.packages]
full = {k.name: k.version for k in self.packages}
return {
"package_names": only_packages,
"packages": full,
"package_names_incl_python_version": {
"python_version": self.python_version,
"packages": only_packages,
},
"packages_incl_python_version": {
"python_version": self.python_version,
"packages": full,
},
}
@classmethod
def retrieve_environment_data(cls) -> Dict[str, Any]:
packages: Dict[str, str] = {}
# Method 1: Use packages_distributions (your current approach)
all_packages = packages_distributions()
for name, pkgs in all_packages.items():
for pkg in pkgs:
try:
dist = distribution(pkg)
packages[pkg] = dist.version
except Exception: # noqa
continue
# Method 2: Use distributions() to catch packages that might be missed
for dist in distributions():
name = dist.metadata["Name"]
version = dist.version
packages[name] = version
# # Method 3: Check for local/editable packages by scanning sys.path
# # This is particularly useful for uv workspace packages
# for path_str in sys.path:
# path = Path(path_str)
# if path.exists() and path.is_dir():
# # Look for .egg-info or .dist-info directories
# for info_dir in path.glob('*.egg-info'):
# try:
# pkg_name = info_dir.stem.split('-')[0]
# if pkg_name not in packages:
# # Try to get version from PKG-INFO or METADATA
# pkg_info_file = info_dir / 'PKG-INFO'
# metadata_file = info_dir / 'METADATA'
#
# version = 'unknown'
# for info_file in [metadata_file, pkg_info_file]:
# if info_file.exists():
# content = info_file.read_text()
# for line in content.split('\n'):
# if line.startswith('Version:'):
# version = line.split(':', 1)[1].strip()
# break
# if version != 'unknown':
# break
#
# packages[pkg_name] = version
# except Exception:
# continue
#
# # Also check for .dist-info directories
# for info_dir in path.glob('*.dist-info'):
# try:
# pkg_name = info_dir.stem.split('-')[0]
# if pkg_name not in packages:
# metadata_file = info_dir / 'METADATA'
# version = 'unknown'
#
# if metadata_file.exists():
# content = metadata_file.read_text()
# for line in content.split('\n'):
# if line.startswith('Version:'):
# version = line.split(':', 1)[1].strip()
# break
#
# packages[pkg_name] = version
# except Exception:
# continue
return {
"python_version": sys.version,
"packages": [
{"name": p, "version": packages[p]}
for p in sorted(packages.keys(), key=lambda x: x.lower())
],
}
|