8000 0.29.6 by balloob · Pull Request #3641 · home-assistant/core · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

0.29.6 #3641

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

Merged
merged 3 commits into from
Oct 1, 2016
Merged

0.29.6 #3641

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions homeassistant/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,49 @@
from homeassistant.util.async import run_callback_threadsafe


def monkey_patch_asyncio():
"""Replace weakref.WeakSet to address Python 3 bug.

Under heavy threading operations that schedule calls into
the asyncio event loop, Task objects are created. Due to
a bug in Python, GC may have an issue when switching between
the threads and objects with __del__ (which various components
in HASS have).

This monkey-patch removes the weakref.Weakset, and replaces it
with an object that ignores the only call utilizing it (the
Task.__init__ which calls _all_tasks.add(self)). It also removes
the __del__ which could trigger the future objects __del__ at
unpredictable times.

The side-effect of this manipulation of the Task is that
Task.all_tasks() is no longer accurate, and there will be no
warning emitted if a Task is GC'd while in use.

On Python 3.6, after the bug is fixed, this monkey-patch can be
disabled.

See https://bugs.python.org/issue26617 for details of the Python
bug.
"""
# pylint: disable=no-self-use, too-few-public-methods, protected-access
# pylint: disable=bare-except
import asyncio.tasks

class IgnoreCalls:
"""Ignore add calls."""

def add(self, other):
"""No-op add."""
return

asyncio.tasks.Task._all_tasks = IgnoreCalls()
try:
del asyncio.tasks.Task.__del__
except:
pass


def validate_python() -> None:
"""Validate we're running the right Python version."""
if sys.version_info[:3] < REQUIRED_PYTHON_VER:
Expand Down Expand Up @@ -308,6 +351,8 @@ def try_to_restart() -> None:

def main() -> int:
"""Start Home Assistant."""
monkey_patch_asyncio()

validate_python()

args = get_arguments()
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 29
PATCH_VERSION = '5'
PATCH_VERSION = '6'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 4, 2)
Expand Down
26 changes: 11 additions & 15 deletions homeassistant/helpers/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,18 @@ def call_from_config(hass, config, blocking=False, variables=None,
domain, service_name = domain_service.split('.', 1)
service_data = dict(config.get(CONF_SERVICE_DATA, {}))

def _data_template_creator(value):
"""Recursive template creator helper function."""
if isinstance(value, list):
for idx, element in enumerate(value):
value[idx] = _data_template_creator(element)
return value
if isinstance(value, dict):
for key, element in value.items():
value[key] = _data_template_creator(element)
return value
value.hass = hass
return value.render(variables)

if CONF_SERVICE_DATA_TEMPLATE in config:
for key, value in config[CONF_SERVICE_DATA_TEMPLATE].items():
service_data[key] = _data_template_creator(value)
def _data_template_creator(value):
"""Recursive template creator helper function."""
if isinstance(value, list):
return [_data_template_creator(item) for item in value]
elif isinstance(value, dict):
return {key: _data_template_creator(item)
for key, item in value.items()}
value.hass = hass
return value.render(variables)
service_data.update(_data_template_creator(
config[CONF_SERVICE_DATA_TEMPLATE]))

if CONF_SERVICE_ENTITY_ID in config:
service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]
Expand Down
6 changes: 6 additions & 0 deletions homeassistant/helpers/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ def _ensure_compiled(self):

return self._compiled

def __eq__(self, other):
"""Compare template with another."""
return (self.__class__ == other.__class__ and
self.template == other.template and
self.hass == other.hass)


class AllStates(object):
"""Class to expose all HA states as attributes."""
Expand Down
29 changes: 17 additions & 12 deletions tests/helpers/test_service.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""Test service helpers."""
from copy import deepcopy
import unittest
from unittest.mock import patch

# To prevent circular import when running just this file
import homeassistant.components # noqa
from homeassistant import core as ha, loader
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID
from homeassistant.helpers import service
from homeassistant.helpers import service, template
import homeassistant.helpers.config_validation as cv

from tests.common import get_test_home_assistant, mock_service

Expand Down Expand Up @@ -97,22 +99,25 @@ def test_split_entity_string(self):

def test_not_mutate_input(self):
"""Test for immutable input."""
orig = {
config = cv.SERVICE_SCHEMA({
'service': 'test_domain.test_service',
'entity_id': 'hello.world, sensor.beer',
'data': {
'hello': 1,
},
}
service.call_from_config(self.hass, orig)
self.hass.block_till_done()
self.assertEqual({
'service': 'test_domain.test_service',
'entity_id': 'hello.world, sensor.beer',
'data': {
'hello': 1,
},
}, orig)
'data_template': {
'nested': {
'value': '{{ 1 + 1 }}'
}
}
})
orig = deepcopy(config)

# Only change after call is each template getting hass attached
template.attach(self.hass, orig)

service.call_from_config(self.hass, config, validate_config=False)
assert orig == config

@patch('homeassistant.helpers.service._LOGGER.error')
def test_fail_silently_if_no_service(self, mock_log):
Expand Down
0