Skip to content

Commit

Permalink
Fix module names for custom components (#14317)
Browse files Browse the repository at this point in the history
* Fix module names for custom components

* Also set __package__ correctly

* bla

* Remove print
  • Loading branch information
balloob committed May 7, 2018
1 parent c48986a commit c4ec2e3
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 7 deletions.
23 changes: 19 additions & 4 deletions homeassistant/loader.py
Expand Up @@ -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)
Expand All @@ -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:
Expand All @@ -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


Expand Down
18 changes: 16 additions & 2 deletions tests/test_loader.py
Expand Up @@ -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."""
Expand Down Expand Up @@ -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'
Expand Up @@ -2,6 +2,6 @@
DOMAIN = 'test_package'


def setup(hass, config):
async def async_setup(hass, config):
"""Mock a successful setup."""
return True

0 comments on commit c4ec2e3

Please sign in to comment.