Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix module names for custom components #14317

Merged
merged 4 commits into from May 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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