From 2a9da208d49017883c2f6f13ca309d27d0d01f09 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen <balloob@gmail.com>
Date: Wed, 26 Aug 2020 10:20:14 +0200
Subject: [PATCH] Allow disabling integrations in manifest, block uuid package
 being installed and disable ezviz (#38444)

---
 .github/workflows/ci.yaml                    |  4 ++--
 homeassistant/components/ezviz/camera.py     |  1 +
 homeassistant/components/ezviz/manifest.json |  1 +
 homeassistant/loader.py                      |  5 +++++
 homeassistant/package_constraints.txt        |  3 +++
 homeassistant/setup.py                       |  4 ++++
 requirements_all.txt                         |  3 ---
 script/gen_requirements_all.py               |  6 ++++++
 script/hassfest/manifest.py                  |  1 +
 script/hassfest/model.py                     |  5 +++++
 tests/test_setup.py                          | 12 ++++++++++++
 11 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 0f060724994..e337c019f52 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -46,7 +46,7 @@ jobs:
         run: |
           python -m venv venv
           . venv/bin/activate
-          pip install -U pip==20.1.1 setuptools
+          pip install -U pip setuptools
           pip install -r requirements.txt -r requirements_test.txt
           # Uninstalling typing as a workaround. Eventually we should make sure
           # all our dependencies drop typing.
@@ -603,7 +603,7 @@ jobs:
         run: |
           python -m venv venv
           . venv/bin/activate
-          pip install -U pip==20.1.1 setuptools wheel
+          pip install -U pip setuptools wheel
           pip install -r requirements_all.txt
           pip install -r requirements_test.txt
           # Uninstalling typing as a workaround. Eventually we should make sure
diff --git a/homeassistant/components/ezviz/camera.py b/homeassistant/components/ezviz/camera.py
index e7e6725e455..701af451496 100644
--- a/homeassistant/components/ezviz/camera.py
+++ b/homeassistant/components/ezviz/camera.py
@@ -2,6 +2,7 @@
 import asyncio
 import logging
 
+# pylint: disable=import-error
 from haffmpeg.tools import IMAGE_JPEG, ImageFrame
 from pyezviz.camera import EzvizCamera
 from pyezviz.client import EzvizClient, PyEzvizError
diff --git a/homeassistant/components/ezviz/manifest.json b/homeassistant/components/ezviz/manifest.json
index 651fd77619c..03bdfc5217c 100644
--- a/homeassistant/components/ezviz/manifest.json
+++ b/homeassistant/components/ezviz/manifest.json
@@ -1,4 +1,5 @@
 {
+  "disabled": "Dependency contains code that breaks Home Assistant.",
   "domain": "ezviz",
   "name": "Ezviz",
   "documentation": "https://www.home-assistant.io/integrations/ezviz",
diff --git a/homeassistant/loader.py b/homeassistant/loader.py
index b82f2c0109a..c5027710c47 100644
--- a/homeassistant/loader.py
+++ b/homeassistant/loader.py
@@ -271,6 +271,11 @@ class Integration:
         """Return name."""
         return cast(str, self.manifest["name"])
 
+    @property
+    def disabled(self) -> Optional[str]:
+        """Return reason integration is disabled."""
+        return cast(Optional[str], self.manifest.get("disabled"))
+
     @property
     def domain(self) -> str:
         """Return domain."""
diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt
index c6a543e94d7..261dd5dd34d 100644
--- a/homeassistant/package_constraints.txt
+++ b/homeassistant/package_constraints.txt
@@ -44,3 +44,6 @@ enum34==1000000000.0.0
 
 # This is a old unmaintained library and is replaced with pycryptodome
 pycrypto==1000000000.0.0
+
+# This is built-in and breaks pip if installed
+uuid==1000000000.0.0
diff --git a/homeassistant/setup.py b/homeassistant/setup.py
index 578cd33b097..341229a83b1 100644
--- a/homeassistant/setup.py
+++ b/homeassistant/setup.py
@@ -124,6 +124,10 @@ async def _async_setup_component(
         log_error("Integration not found.")
         return False
 
+    if integration.disabled:
+        log_error(f"dependency is disabled - {integration.disabled}")
+        return False
+
     # Validate all dependencies exist and there are no circular dependencies
     if not await integration.resolve_dependencies():
         return False
diff --git a/requirements_all.txt b/requirements_all.txt
index e543e325592..de5c87a07c7 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1334,9 +1334,6 @@ pyephember==0.3.1
 # homeassistant.components.everlights
 pyeverlights==0.1.0
 
-# homeassistant.components.ezviz
-pyezviz==0.1.5
-
 # homeassistant.components.fido
 pyfido==2.1.1
 
diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py
index b851983b6f6..25d4d3d3e7e 100755
--- a/script/gen_requirements_all.py
+++ b/script/gen_requirements_all.py
@@ -71,6 +71,9 @@ enum34==1000000000.0.0
 
 # This is a old unmaintained library and is replaced with pycryptodome
 pycrypto==1000000000.0.0
+
+# This is built-in and breaks pip if installed
+uuid==1000000000.0.0
 """
 
 IGNORE_PRE_COMMIT_HOOK_ID = (
@@ -178,6 +181,9 @@ def gather_requirements_from_manifests(errors, reqs):
             errors.append(f"The manifest for integration {domain} is invalid.")
             continue
 
+        if integration.disabled:
+            continue
+
         process_requirements(
             errors, integration.requirements, f"homeassistant.components.{domain}", reqs
         )
diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py
index cb592b63b53..cd3895f5f20 100644
--- a/script/hassfest/manifest.py
+++ b/script/hassfest/manifest.py
@@ -54,6 +54,7 @@ MANIFEST_SCHEMA = vol.Schema(
         vol.Optional("dependencies"): [str],
         vol.Optional("after_dependencies"): [str],
         vol.Required("codeowners"): [str],
+        vol.Optional("disabled"): str,
     }
 )
 
diff --git a/script/hassfest/model.py b/script/hassfest/model.py
index bb438f4d84f..c993689aaab 100644
--- a/script/hassfest/model.py
+++ b/script/hassfest/model.py
@@ -73,6 +73,11 @@ class Integration:
         """Integration domain."""
         return self.path.name
 
+    @property
+    def disabled(self) -> Optional[str]:
+        """List of disabled."""
+        return self.manifest.get("disabled")
+
     @property
     def requirements(self) -> List[str]:
         """List of requirements."""
diff --git a/tests/test_setup.py b/tests/test_setup.py
index abd9cecd9ac..8651308572a 100644
--- a/tests/test_setup.py
+++ b/tests/test_setup.py
@@ -583,3 +583,15 @@ async def test_parallel_entry_setup(hass):
     await setup.async_setup_component(hass, "comp", {})
 
     assert calls == [1, 2, 1, 2]
+
+
+async def test_integration_disabled(hass, caplog):
+    """Test we can disable an integration."""
+    disabled_reason = "Dependency contains code that breaks Home Assistant"
+    mock_integration(
+        hass,
+        MockModule("test_component1", partial_manifest={"disabled": disabled_reason}),
+    )
+    result = await setup.async_setup_component(hass, "test_component1", {})
+    assert not result
+    assert disabled_reason in caplog.text
-- 
GitLab