Check for duplicate files across packages and compute size/path metrics. Iterates through all files in the provided package cache records to: 1. Detect duplicate files (same path in multiple packages) 2. Compute approximate tarball and extracted sizes 3. Track the longest relat
(
pc_recs: Iterable[PackageCacheRecord],
platform: str,
duplicate_files: Literal["error", "warn", "skip"] = "error",
env_prefixes: dict[PackageCacheRecord, str] | None = None,
)
| 150 | |
| 151 | |
| 152 | def check_duplicates_files( |
| 153 | pc_recs: Iterable[PackageCacheRecord], |
| 154 | platform: str, |
| 155 | duplicate_files: Literal["error", "warn", "skip"] = "error", |
| 156 | env_prefixes: dict[PackageCacheRecord, str] | None = None, |
| 157 | ) -> tuple[int, int, int]: |
| 158 | """ |
| 159 | Check for duplicate files across packages and compute size/path metrics. |
| 160 | |
| 161 | Iterates through all files in the provided package cache records to: |
| 162 | 1. Detect duplicate files (same path in multiple packages) |
| 163 | 2. Compute approximate tarball and extracted sizes |
| 164 | 3. Track the longest relative file path (for MAX_PATH validation on Windows) |
| 165 | |
| 166 | Args: |
| 167 | pc_recs: Package cache records to check. |
| 168 | platform: Target platform string (e.g., "win-64", "linux-64"). |
| 169 | duplicate_files: How to handle duplicates - "error", "warn", or "skip". |
| 170 | env_prefixes: Optional dict mapping PackageCacheRecord -> path prefix string. |
| 171 | Used to account for extra_envs paths which are installed under |
| 172 | "envs/<name>/" rather than the base install directory. Records not |
| 173 | in this dict are assumed to be in the base environment (no prefix). |
| 174 | A trailing separator is added automatically if missing. |
| 175 | |
| 176 | Returns: |
| 177 | Tuple of (approx_tarball_size, approx_extracted_size, max_relative_path_length) |
| 178 | """ |
| 179 | assert duplicate_files in ("warn", "skip", "error") |
| 180 | if env_prefixes is None: |
| 181 | env_prefixes = {} |
| 182 | for env, prefix in env_prefixes.items(): |
| 183 | if prefix and not prefix.endswith("/"): |
| 184 | env_prefixes[env] += "/" |
| 185 | |
| 186 | map_members_scase = defaultdict(set) |
| 187 | map_members_icase = defaultdict(lambda: {"files": set(), "fns": set()}) |
| 188 | |
| 189 | # Keep a min, 50MB buffer size |
| 190 | total_tarball_size = 52428800 |
| 191 | total_extracted_pkgs_size = 52428800 |
| 192 | max_relative_path_length = 0 |
| 193 | |
| 194 | for pc_rec in pc_recs: |
| 195 | fn = pc_rec.fn |
| 196 | extracted_package_dir = pc_rec.extracted_package_dir |
| 197 | |
| 198 | total_tarball_size += int(pc_rec.get("size", 0)) |
| 199 | |
| 200 | paths_data = read_paths_json(extracted_package_dir).paths |
| 201 | env_prefix_len = len(env_prefixes.get(pc_rec, "")) |
| 202 | for path_data in paths_data: |
| 203 | short_path = path_data.path |
| 204 | max_relative_path_length = max( |
| 205 | max_relative_path_length, env_prefix_len + len(short_path) |
| 206 | ) |
| 207 | try: |
| 208 | size = path_data.size_in_bytes or getsize(join(extracted_package_dir, short_path)) |
| 209 | except AttributeError: |