summary refs log tree commit diff
path: root/pkgs/servers/home-assistant/parse-requirements.py
diff options
context:
space:
mode:
authorRobert Schütz <robert.schuetz@stud.uni-heidelberg.de>2018-05-25 17:14:30 +0200
committerRobert Schütz <robert.schuetz@stud.uni-heidelberg.de>2018-05-25 17:31:19 +0200
commitb1aa2b78902c087a20fc557ff699e233928f9f5b (patch)
tree84ebdec05e60e1e2ba2e96810e48224a45329e77 /pkgs/servers/home-assistant/parse-requirements.py
parent1c39035cad1bbcc7cf2a735ce8b81f74957fdab5 (diff)
downloadnixlib-b1aa2b78902c087a20fc557ff699e233928f9f5b.tar
nixlib-b1aa2b78902c087a20fc557ff699e233928f9f5b.tar.gz
nixlib-b1aa2b78902c087a20fc557ff699e233928f9f5b.tar.bz2
nixlib-b1aa2b78902c087a20fc557ff699e233928f9f5b.tar.lz
nixlib-b1aa2b78902c087a20fc557ff699e233928f9f5b.tar.xz
nixlib-b1aa2b78902c087a20fc557ff699e233928f9f5b.tar.zst
nixlib-b1aa2b78902c087a20fc557ff699e233928f9f5b.zip
home-assistant: include requirements of dependencies
Diffstat (limited to 'pkgs/servers/home-assistant/parse-requirements.py')
-rwxr-xr-xpkgs/servers/home-assistant/parse-requirements.py69
1 files changed, 36 insertions, 33 deletions
diff --git a/pkgs/servers/home-assistant/parse-requirements.py b/pkgs/servers/home-assistant/parse-requirements.py
index 5beeaccf0881..4518d4dd406a 100755
--- a/pkgs/servers/home-assistant/parse-requirements.py
+++ b/pkgs/servers/home-assistant/parse-requirements.py
@@ -1,5 +1,5 @@
 #! /usr/bin/env nix-shell
-#! nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ ])"
+#! nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ requests pyyaml pytz pip jinja2 voluptuous typing aiohttp async-timeout astral certifi attrs ])"
 #
 # This script downloads https://github.com/home-assistant/home-assistant/blob/master/requirements_all.txt.
 # This file contains lines of the form
@@ -14,15 +14,17 @@
 # Then, a Nix attribute set mapping component name to dependencies is created.
 
 from urllib.request import urlopen
-from collections import OrderedDict
+import tempfile
+from io import BytesIO
+import tarfile
+import importlib
 import subprocess
 import os
 import sys
 import json
 import re
 
-GENERAL_PREFIX = '# homeassistant.'
-COMPONENT_PREFIX = GENERAL_PREFIX + 'components.'
+COMPONENT_PREFIX = 'homeassistant.components'
 PKG_SET = 'python3Packages'
 
 # If some requirements are matched by multiple python packages,
@@ -37,28 +39,32 @@ def get_version():
         m = re.search('hassVersion = "([\\d\\.]+)";', f.read())
         return m.group(1)
 
-def fetch_reqs(version='master'):
-    requirements = {}
-    with urlopen('https://github.com/home-assistant/home-assistant/raw/{}/requirements_all.txt'.format(version)) as response:
-        components = []
-        for line in response.read().decode().splitlines():
-            if line == '':
-                components = []
-            elif line[:len(COMPONENT_PREFIX)] == COMPONENT_PREFIX:
-                component = line[len(COMPONENT_PREFIX):]
-                components.append(component)
-                if component not in requirements:
-                    requirements[component] = []
-            elif line[:len(GENERAL_PREFIX)] != GENERAL_PREFIX: # skip lines like "# homeassistant.scripts.xyz"
-                # Some dependencies are commented out because they don't build on all platforms
-                # Since they are still required for running the component, don't skip them
-                if line[:2] == '# ':
-                    line = line[2:]
-                # Some requirements are specified by url, e.g. https://example.org/foobar#xyz==1.0.0
-                # Therefore, if there's a "#" in the line, only take the part after it
-                line = line[line.find('#') + 1:]
-                for component in components:
-                    requirements[component].append(line)
+def parse_components(version='master'):
+    components = {}
+    with tempfile.TemporaryDirectory() as tmp:
+        with urlopen('https://github.com/home-assistant/home-assistant/archive/{}.tar.gz'.format(version)) as response:
+            tarfile.open(fileobj=BytesIO(response.read())).extractall(tmp)
+        # Use part of a script from the Home Assistant codebase
+        sys.path.append(tmp + '/home-assistant-{}'.format(version))
+        from script.gen_requirements_all import explore_module
+        for package in explore_module(COMPONENT_PREFIX, True):
+            # Remove 'homeassistant.components.' prefix
+            component = package[len(COMPONENT_PREFIX + '.'):]
+            try:
+                module = importlib.import_module(package)
+                components[component] = {}
+                components[component]['requirements'] = getattr(module, 'REQUIREMENTS', [])
+                components[component]['dependencies'] = getattr(module, 'DEPENDENCIES', [])
+            # If there is an ImportError, the imported file is not the main file of the component
+            except ImportError:
+                continue
+    return components
+
+# Recursively get the requirements of a component and its dependencies
+def get_reqs(components, component):
+    requirements = set(components[component]['requirements'])
+    for dependency in components[component]['dependencies']:
+        requirements.update(get_reqs(components, dependency))
     return requirements
 
 # Store a JSON dump of Nixpkgs' python3Packages
@@ -95,11 +101,11 @@ def name_to_attr_path(req):
 
 version = get_version()
 print('Generating component-packages.nix for version {}'.format(version))
-requirements = fetch_reqs(version=version)
+components = parse_components(version=version)
 build_inputs = {}
-for component, reqs in OrderedDict(sorted(requirements.items())).items():
+for component in sorted(components.keys()):
     attr_paths = []
-    for req in reqs:
+    for req in get_reqs(components, component):
         name = req.split('==')[0]
         attr_path = name_to_attr_path(name)
         if attr_path is not None:
@@ -108,11 +114,8 @@ for component, reqs in OrderedDict(sorted(requirements.items())).items():
     else:
         build_inputs[component] = attr_paths
 
-# Only select components which have any dependency
-#build_inputs = {k: v for k, v in build_inputs.items() if len(v) > 0}
-
 with open(os.path.dirname(sys.argv[0]) + '/component-packages.nix', 'w') as f:
-    f.write('# Generated from parse-requirements.py\n')
+    f.write('# Generated by parse-requirements.py\n')
     f.write('# Do not edit!\n\n')
     f.write('{\n')
     f.write('  version = "{}";\n'.format(version))