@@ -269,6 +269,45 @@ def validate_package(self, package_name: str, context: str) -> None | Validation
269269 return None
270270
271271
272+ class BioconductorValidator :
273+ """Validate Bioconductor package names using the Bioconductor API."""
274+
275+ def __init__ (self , client : httpx .Client ) -> None :
276+ self .client = client
277+ self .validated_packages : set [str ] = set ()
278+
279+ def validate_package (self , package_name : str , context : str ) -> None | ValidationError :
280+ """Validate that a Bioconductor package exists.
281+
282+ Parameters
283+ ----------
284+ package_name
285+ The Bioconductor package name to validate
286+ context
287+ Context information for error messages (e.g., file being validated)
288+ """
289+ if package_name in self .validated_packages :
290+ return None
291+
292+ # Bioconductor packages can be checked via their web API
293+ try :
294+ response = self .client .head (f"https://bioconductor.org/packages/{ package_name } /" )
295+ except Exception as e :
296+ msg = f"{ context } : Failed to validate Bioconductor package '{ package_name } ': { e } "
297+ return ValidationError (msg )
298+
299+ if response .status_code == httpx .codes .NOT_FOUND :
300+ msg = f"{ context } : Bioconductor package '{ package_name } ' does not exist"
301+ return ValidationError (msg )
302+ if response .status_code != httpx .codes .OK :
303+ msg = f"{ context } : Failed to validate Bioconductor package '{ package_name } ' (error { response .status_code } )"
304+ return ValidationError (msg )
305+
306+ self .validated_packages .add (package_name )
307+ log .info (f"Validated Bioconductor package: { package_name } " )
308+ return None
309+
310+
272311def check_image (img_path : Path ) -> None | ValidationError :
273312 """Validates that the image exists and that it is either a SVG or fits into the 512x512 bounding box."""
274313 if not img_path .exists ():
@@ -290,7 +329,7 @@ def check_image(img_path: Path) -> None | ValidationError:
290329 return None
291330
292331
293- def validate_packages (
332+ def validate_packages ( # noqa: C901
294333 schema_file : Traversable , registry_dir : Path , github_token : str | None = None
295334) -> tuple [Mapping [str , Sequence [Exception ]], Sequence [ScverseEcosystemPackages ]]:
296335 """Find all package `meta.yaml` files in the registry dir and yield package records."""
@@ -310,6 +349,7 @@ def validate_packages(
310349 pypi_validator = PyPIValidator (retry_client )
311350 conda_validator = CondaValidator (retry_client )
312351 cran_validator = CRANValidator (retry_client )
352+ bioconductor_validator = BioconductorValidator (retry_client )
313353
314354 errors : defaultdict [str , ErrorList ] = defaultdict (ErrorList )
315355 package_metadata : list [ScverseEcosystemPackages ] = []
@@ -344,6 +384,8 @@ def validate_packages(
344384 pkg_errors .append (conda_validator .validate_package (conda_name , pkg_id ))
345385 if cran_name := install_info .get ("cran" ):
346386 pkg_errors .append (cran_validator .validate_package (cran_name , pkg_id ))
387+ if bioconductor_name := install_info .get ("bioconductor" ):
388+ pkg_errors .append (bioconductor_validator .validate_package (bioconductor_name , pkg_id ))
347389
348390 # Check logo (if available) and make path relative to root of registry
349391 if "logo" in tmp_meta :
0 commit comments