From c4ec2e3434e3981476de6581bb578cf88dd01693 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 6 May 2018 20:54:56 -0400 Subject: [PATCH] Fix module names for custom components (#14317) * Fix module names for custom components * Also set __package__ correctly * bla * Remove print --- homeassistant/loader.py | 23 +++++++++++++++---- tests/test_loader.py | 18 +++++++++++++-- .../test_package/__init__.py | 2 +- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 322870952f207a..b6dabb1d88397b 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -73,13 +73,15 @@ def get_component(hass, comp_or_platform): # Try custom component module = _load_module(hass.config.path(PATH_CUSTOM_COMPONENTS), - comp_or_platform) + PATH_CUSTOM_COMPONENTS, comp_or_platform) if module is None: try: module = importlib.import_module( '{}.{}'.format(PACKAGE_COMPONENTS, comp_or_platform)) + _LOGGER.debug('Loaded %s (built-in)', comp_or_platform) except ImportError: + _LOGGER.warning('Unable to find %s', comp_or_platform) module = None cache = hass.data.get(DATA_KEY) @@ -102,18 +104,20 @@ def _find_spec(path, name): return None -def _load_module(path, name): +def _load_module(path, base_module, name): """Load a module based on a folder and a name.""" + mod_name = "{}.{}".format(base_module, name) spec = _find_spec([path], name) # Special handling if loading platforms and the folder is a namespace # (namespace is a folder without __init__.py) if spec is None and '.' in name: - parent_spec = _find_spec([path], name.split('.')[0]) + mod_parent_name = name.split('.')[0] + parent_spec = _find_spec([path], mod_parent_name) if (parent_spec is None or parent_spec.submodule_search_locations is None): return None - spec = _find_spec(parent_spec.submodule_search_locations, name) + spec = _find_spec(parent_spec.submodule_search_locations, mod_name) # Not found if spec is None: @@ -123,8 +127,19 @@ def _load_module(path, name): if spec.loader is None: return None + _LOGGER.debug('Loaded %s (%s)', name, base_module) + module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) + # A hack, I know. Don't currently know how to work around it. + if not module.__name__.startswith(base_module): + module.__name__ = "{}.{}".format(base_module, name) + + if not module.__package__: + module.__package__ = base_module + elif not module.__package__.startswith(base_module): + module.__package__ = "{}.{}".format(base_module, name) + return module diff --git a/tests/test_loader.py b/tests/test_loader.py index 646526e94ea64a..e8a79c6501f33d 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -30,8 +30,7 @@ def test_set_component(self): comp = object() loader.set_component(self.hass, 'switch.test_set', comp) - self.assertEqual(comp, - loader.get_component(self.hass, 'switch.test_set')) + assert loader.get_component(self.hass, 'switch.test_set') is comp def test_get_component(self): """Test if get_component works.""" @@ -106,3 +105,18 @@ def discovery_callback(service, discovered): yield from hass.async_block_till_done() assert result == ['hello'] + + +async def test_custom_component_name(hass): + """Test the name attribte of custom components.""" + comp = loader.get_component(hass, 'test_standalone') + assert comp.__name__ == 'custom_components.test_standalone' + assert comp.__package__ == 'custom_components' + + comp = loader.get_component(hass, 'test_package') + assert comp.__name__ == 'custom_components.test_package' + assert comp.__package__ == 'custom_components.test_package' + + comp = loader.get_component(hass, 'light.test') + assert comp.__name__ == 'custom_components.light.test' + assert comp.__package__ == 'custom_components.light' diff --git a/tests/testing_config/custom_components/test_package/__init__.py b/tests/testing_config/custom_components/test_package/__init__.py index 528f056948b675..ee669c6c9b59fc 100644 --- a/tests/testing_config/custom_components/test_package/__init__.py +++ b/tests/testing_config/custom_components/test_package/__init__.py @@ -2,6 +2,6 @@ DOMAIN = 'test_package' -def setup(hass, config): +async def async_setup(hass, config): """Mock a successful setup.""" return True