diff options
Diffstat (limited to 'nixpkgs/pkgs/servers/home-assistant/parse-requirements.py')
-rwxr-xr-x | nixpkgs/pkgs/servers/home-assistant/parse-requirements.py | 61 |
1 files changed, 50 insertions, 11 deletions
diff --git a/nixpkgs/pkgs/servers/home-assistant/parse-requirements.py b/nixpkgs/pkgs/servers/home-assistant/parse-requirements.py index 0e3d5d5553e7..b7bf2937a297 100755 --- a/nixpkgs/pkgs/servers/home-assistant/parse-requirements.py +++ b/nixpkgs/pkgs/servers/home-assistant/parse-requirements.py @@ -37,6 +37,7 @@ PKG_SET = "home-assistant.python.pkgs" # If some requirements are matched by multiple or no Python packages, the # following can be used to choose the correct one PKG_PREFERENCES = { + "ha-av": "av", "youtube_dl": "youtube-dl-light", "tensorflow": "tensorflow", "fiblary3": "fiblary3-fork", # https://github.com/home-assistant/core/issues/66466 @@ -98,13 +99,37 @@ def get_reqs(components: Dict[str, Dict[str, Any]], component: str, processed: S return requirements +def repository_root() -> str: + return os.path.abspath(sys.argv[0] + "/../../../..") + + +# For a package attribute and and an extra, check if the package exposes it via passthru.optional-dependencies +def has_extra(package: str, extra: str): + cmd = [ + "nix-instantiate", + repository_root(), + "-A", + f"{package}.optional-dependencies.{extra}", + ] + try: + subprocess.run( + cmd, + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + except subprocess.CalledProcessError: + return False + return True + + def dump_packages() -> Dict[str, Dict[str, str]]: # Store a JSON dump of Nixpkgs' python3Packages output = subprocess.check_output( [ "nix-env", "-f", - os.path.dirname(sys.argv[0]) + "/../../..", + repository_root(), "-qa", "-A", PKG_SET, @@ -130,7 +155,7 @@ def name_to_attr_path(req: str, packages: Dict[str, Dict[str, str]]) -> Optional # python(minor).(major)-(pname)-(version or unstable-date) # we need the version qualifier, or we'll have multiple matches # (e.g. pyserial and pyserial-asyncio when looking for pyserial) - pattern = re.compile(f"^python\\d\\.\\d-{name}-(?:\\d|unstable-.*)", re.I) + pattern = re.compile(f"^python\\d+\\.\\d+-{name}-(?:\\d|unstable-.*)", re.I) for attr_path, package in packages.items(): if pattern.match(package["name"]): attr_paths.append(attr_path) @@ -158,6 +183,7 @@ def main() -> None: outdated = {} for component in sorted(components.keys()): attr_paths = [] + extra_attrs = [] missing_reqs = [] reqs = sorted(get_reqs(components, component, set())) for req in reqs: @@ -165,9 +191,10 @@ def main() -> None: # Therefore, if there's a "#" in the line, only take the part after it req = req[req.find("#") + 1 :] name, required_version = req.split("==", maxsplit=1) - # Remove extra_require from name, e.g. samsungctl instead of - # samsungctl[websocket] + # Split package name and extra requires + extras = [] if name.endswith("]"): + extras = name[name.find("[")+1:name.find("]")].split(",") name = name[:name.find("[")] attr_path = name_to_attr_path(name, packages) if our_version := get_pkg_version(name, packages): @@ -178,11 +205,20 @@ def main() -> None: } if attr_path is not None: # Add attribute path without "python3Packages." prefix - attr_paths.append(attr_path[len(PKG_SET + ".") :]) + pname = attr_path[len(PKG_SET + "."):] + attr_paths.append(pname) + for extra in extras: + # Check if package advertises extra requirements + extra_attr = f"{pname}.optional-dependencies.{extra}" + if has_extra(attr_path, extra): + extra_attrs.append(extra_attr) + else: + missing_reqs.append(extra_attr) + else: missing_reqs.append(name) else: - build_inputs[component] = (attr_paths, missing_reqs) + build_inputs[component] = (attr_paths, extra_attrs, missing_reqs) with open(os.path.dirname(sys.argv[0]) + "/component-packages.nix", "w") as f: f.write("# Generated by parse-requirements.py\n") @@ -191,11 +227,14 @@ def main() -> None: f.write(f' version = "{version}";\n') f.write(" components = {\n") for component, deps in build_inputs.items(): - available, missing = deps + available, extras, missing = deps f.write(f' "{component}" = ps: with ps; [') if available: - f.write(" " + " ".join(available)) - f.write(" ];") + f.write("\n " + "\n ".join(available)) + f.write("\n ]") + if extras: + f.write("\n ++ " + "\n ++ ".join(extras)) + f.write(";") if len(missing) > 0: f.write(f" # missing inputs: {' '.join(missing)}") f.write("\n") @@ -203,13 +242,13 @@ def main() -> None: f.write(" # components listed in tests/components for which all dependencies are packaged\n") f.write(" supportedComponentsWithTests = [\n") for component, deps in build_inputs.items(): - available, missing = deps + available, extras, missing = deps if len(missing) == 0 and component in components_with_tests: f.write(f' "{component}"' + "\n") f.write(" ];\n") f.write("}\n") - supported_components = reduce(lambda n, c: n + (build_inputs[c][1] == []), + supported_components = reduce(lambda n, c: n + (build_inputs[c][2] == []), components.keys(), 0) total_components = len(components) print(f"{supported_components} / {total_components} components supported, " |