Migrate to cherrypy wsgi from eventlet (#2387)

This commit is contained in:
Paulus Schoutsen 2016-06-30 09:02:12 -07:00 committed by GitHub
parent 7582eb9f63
commit d1f4901d53
13 changed files with 168 additions and 153 deletions

View File

@ -6,7 +6,7 @@ https://home-assistant.io/developers/api/
""" """
import json import json
import logging import logging
from time import time import queue
import homeassistant.core as ha import homeassistant.core as ha
import homeassistant.remote as rem import homeassistant.remote as rem
@ -72,19 +72,14 @@ class APIEventStream(HomeAssistantView):
def get(self, request): def get(self, request):
"""Provide a streaming interface for the event bus.""" """Provide a streaming interface for the event bus."""
from eventlet.queue import LightQueue, Empty
import eventlet
cur_hub = eventlet.hubs.get_hub()
request.environ['eventlet.minimum_write_chunk_size'] = 0
to_write = LightQueue()
stop_obj = object() stop_obj = object()
to_write = queue.Queue()
restrict = request.args.get('restrict') restrict = request.args.get('restrict')
if restrict: if restrict:
restrict = restrict.split(',') restrict = restrict.split(',') + [EVENT_HOMEASSISTANT_STOP]
def thread_forward_events(event): def forward_events(event):
"""Forward events to the open request.""" """Forward events to the open request."""
if event.event_type == EVENT_TIME_CHANGED: if event.event_type == EVENT_TIME_CHANGED:
return return
@ -99,28 +94,20 @@ class APIEventStream(HomeAssistantView):
else: else:
data = json.dumps(event, cls=rem.JSONEncoder) data = json.dumps(event, cls=rem.JSONEncoder)
cur_hub.schedule_call_global(0, lambda: to_write.put(data)) to_write.put(data)
def stream(): def stream():
"""Stream events to response.""" """Stream events to response."""
self.hass.bus.listen(MATCH_ALL, thread_forward_events) self.hass.bus.listen(MATCH_ALL, forward_events)
_LOGGER.debug('STREAM %s ATTACHED', id(stop_obj)) _LOGGER.debug('STREAM %s ATTACHED', id(stop_obj))
last_msg = time()
# Fire off one message right away to have browsers fire open event # Fire off one message right away to have browsers fire open event
to_write.put(STREAM_PING_PAYLOAD) to_write.put(STREAM_PING_PAYLOAD)
while True: while True:
try: try:
# Somehow our queue.get sometimes takes too long to payload = to_write.get(timeout=STREAM_PING_INTERVAL)
# be notified of arrival of data. Probably
# because of our spawning on hub in other thread
# hack. Because current goal is to get this out,
# We just timeout every second because it will
# return right away if qsize() > 0.
# So yes, we're basically polling :(
payload = to_write.get(timeout=1)
if payload is stop_obj: if payload is stop_obj:
break break
@ -129,15 +116,13 @@ class APIEventStream(HomeAssistantView):
_LOGGER.debug('STREAM %s WRITING %s', id(stop_obj), _LOGGER.debug('STREAM %s WRITING %s', id(stop_obj),
msg.strip()) msg.strip())
yield msg.encode("UTF-8") yield msg.encode("UTF-8")
last_msg = time() except queue.Empty:
except Empty:
if time() - last_msg > 50:
to_write.put(STREAM_PING_PAYLOAD) to_write.put(STREAM_PING_PAYLOAD)
except GeneratorExit: except GeneratorExit:
_LOGGER.debug('STREAM %s RESPONSE CLOSED', id(stop_obj))
break break
self.hass.bus.remove_listener(MATCH_ALL, thread_forward_events) _LOGGER.debug('STREAM %s RESPONSE CLOSED', id(stop_obj))
self.hass.bus.remove_listener(MATCH_ALL, forward_events)
return self.Response(stream(), mimetype='text/event-stream') return self.Response(stream(), mimetype='text/event-stream')

View File

@ -6,6 +6,7 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera/ https://home-assistant.io/components/camera/
""" """
import logging import logging
import time
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
@ -81,8 +82,6 @@ class Camera(Entity):
def mjpeg_stream(self, response): def mjpeg_stream(self, response):
"""Generate an HTTP MJPEG stream from camera images.""" """Generate an HTTP MJPEG stream from camera images."""
import eventlet
def stream(): def stream():
"""Stream images as mjpeg stream.""" """Stream images as mjpeg stream."""
try: try:
@ -99,7 +98,7 @@ class Camera(Entity):
last_image = img_bytes last_image = img_bytes
eventlet.sleep(0.5) time.sleep(0.5)
except GeneratorExit: except GeneratorExit:
pass pass

View File

@ -13,19 +13,19 @@ import re
import ssl import ssl
import voluptuous as vol import voluptuous as vol
import homeassistant.core as ha
import homeassistant.remote as rem import homeassistant.remote as rem
from homeassistant import util from homeassistant import util
from homeassistant.const import ( from homeassistant.const import (
SERVER_PORT, HTTP_HEADER_HA_AUTH, HTTP_HEADER_CACHE_CONTROL, SERVER_PORT, HTTP_HEADER_HA_AUTH, HTTP_HEADER_CACHE_CONTROL,
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, ALLOWED_CORS_HEADERS) HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, ALLOWED_CORS_HEADERS,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START)
from homeassistant.helpers.entity import split_entity_id from homeassistant.helpers.entity import split_entity_id
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
DOMAIN = "http" DOMAIN = "http"
REQUIREMENTS = ("eventlet==0.19.0", "static3==0.7.0", "Werkzeug==0.11.10") REQUIREMENTS = ("cherrypy==6.0.2", "static3==0.7.0", "Werkzeug==0.11.10")
CONF_API_PASSWORD = "api_password" CONF_API_PASSWORD = "api_password"
CONF_SERVER_HOST = "server_host" CONF_SERVER_HOST = "server_host"
@ -118,11 +118,17 @@ def setup(hass, config):
cors_origins=cors_origins cors_origins=cors_origins
) )
hass.bus.listen_once( def start_wsgi_server(event):
ha.EVENT_HOMEASSISTANT_START, """Start the WSGI server."""
lambda event: server.start()
threading.Thread(target=server.start, daemon=True,
name='WSGI-server').start()) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_wsgi_server)
def stop_wsgi_server(event):
"""Stop the WSGI server."""
server.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_wsgi_server)
hass.wsgi = server hass.wsgi = server
hass.config.api = rem.API(server_host if server_host != '0.0.0.0' hass.config.api = rem.API(server_host if server_host != '0.0.0.0'
@ -241,6 +247,7 @@ class HomeAssistantWSGI(object):
self.server_port = server_port self.server_port = server_port
self.cors_origins = cors_origins self.cors_origins = cors_origins
self.event_forwarder = None self.event_forwarder = None
self.server = None
def register_view(self, view): def register_view(self, view):
"""Register a view with the WSGI server. """Register a view with the WSGI server.
@ -308,17 +315,34 @@ class HomeAssistantWSGI(object):
def start(self): def start(self):
"""Start the wsgi server.""" """Start the wsgi server."""
from eventlet import wsgi from cherrypy import wsgiserver
import eventlet from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter
# pylint: disable=too-few-public-methods,super-init-not-called
class ContextSSLAdapter(BuiltinSSLAdapter):
"""SSL Adapter that takes in an SSL context."""
def __init__(self, context):
self.context = context
# pylint: disable=no-member
self.server = wsgiserver.CherryPyWSGIServer(
(self.server_host, self.server_port), self,
server_name='Home Assistant')
sock = eventlet.listen((self.server_host, self.server_port))
if self.ssl_certificate: if self.ssl_certificate:
context = ssl.SSLContext(SSL_VERSION) context = ssl.SSLContext(SSL_VERSION)
context.options |= SSL_OPTS context.options |= SSL_OPTS
context.set_ciphers(CIPHERS) context.set_ciphers(CIPHERS)
context.load_cert_chain(self.ssl_certificate, self.ssl_key) context.load_cert_chain(self.ssl_certificate, self.ssl_key)
sock = context.wrap_socket(sock, server_side=True) self.server.ssl_adapter = ContextSSLAdapter(context)
wsgi.server(sock, self, log=_LOGGER)
threading.Thread(target=self.server.start, daemon=True,
name='WSGI-server').start()
def stop(self):
"""Stop the wsgi server."""
self.server.stop()
def dispatch_request(self, request): def dispatch_request(self, request):
"""Handle incoming request.""" """Handle incoming request."""
@ -365,6 +389,10 @@ class HomeAssistantWSGI(object):
"""Handle a request for base app + extra apps.""" """Handle a request for base app + extra apps."""
from werkzeug.wsgi import DispatcherMiddleware from werkzeug.wsgi import DispatcherMiddleware
if not self.hass.is_running:
from werkzeug.exceptions import BadRequest
return BadRequest()(environ, start_response)
app = DispatcherMiddleware(self.base_app, self.extra_apps) app = DispatcherMiddleware(self.base_app, self.extra_apps)
# Strip out any cachebusting MD5 fingerprints # Strip out any cachebusting MD5 fingerprints
fingerprinted = _FINGERPRINT.match(environ.get('PATH_INFO', '')) fingerprinted = _FINGERPRINT.match(environ.get('PATH_INFO', ''))

View File

@ -49,6 +49,19 @@ MIN_WORKER_THREAD = 2
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class CoreState(enum.Enum):
"""Represent the current state of Home Assistant."""
not_running = "NOT_RUNNING"
starting = "STARTING"
running = "RUNNING"
stopping = "STOPPING"
def __str__(self):
"""Return the event."""
return self.value
class HomeAssistant(object): class HomeAssistant(object):
"""Root object of the Home Assistant home automation.""" """Root object of the Home Assistant home automation."""
@ -59,14 +72,23 @@ class HomeAssistant(object):
self.services = ServiceRegistry(self.bus, pool) self.services = ServiceRegistry(self.bus, pool)
self.states = StateMachine(self.bus) self.states = StateMachine(self.bus)
self.config = Config() self.config = Config()
self.state = CoreState.not_running
@property
def is_running(self):
"""Return if Home Assistant is running."""
return self.state == CoreState.running
def start(self): def start(self):
"""Start home assistant.""" """Start home assistant."""
_LOGGER.info( _LOGGER.info(
"Starting Home Assistant (%d threads)", self.pool.worker_count) "Starting Home Assistant (%d threads)", self.pool.worker_count)
self.state = CoreState.starting
create_timer(self) create_timer(self)
self.bus.fire(EVENT_HOMEASSISTANT_START) self.bus.fire(EVENT_HOMEASSISTANT_START)
self.pool.block_till_done()
self.state = CoreState.running
def block_till_stopped(self): def block_till_stopped(self):
"""Register service homeassistant/stop and will block until called.""" """Register service homeassistant/stop and will block until called."""
@ -113,8 +135,10 @@ class HomeAssistant(object):
def stop(self): def stop(self):
"""Stop Home Assistant and shuts down all threads.""" """Stop Home Assistant and shuts down all threads."""
_LOGGER.info("Stopping") _LOGGER.info("Stopping")
self.state = CoreState.stopping
self.bus.fire(EVENT_HOMEASSISTANT_STOP) self.bus.fire(EVENT_HOMEASSISTANT_STOP)
self.pool.stop() self.pool.stop()
self.state = CoreState.not_running
class JobPriority(util.OrderedEnum): class JobPriority(util.OrderedEnum):

View File

@ -11,6 +11,7 @@ from datetime import datetime
import enum import enum
import json import json
import logging import logging
import time
import threading import threading
import urllib.parse import urllib.parse
@ -123,6 +124,7 @@ class HomeAssistant(ha.HomeAssistant):
self.services = ha.ServiceRegistry(self.bus, pool) self.services = ha.ServiceRegistry(self.bus, pool)
self.states = StateMachine(self.bus, self.remote_api) self.states = StateMachine(self.bus, self.remote_api)
self.config = ha.Config() self.config = ha.Config()
self.state = ha.CoreState.not_running
self.config.api = local_api self.config.api = local_api
@ -134,17 +136,20 @@ class HomeAssistant(ha.HomeAssistant):
raise HomeAssistantError( raise HomeAssistantError(
'Unable to setup local API to receive events') 'Unable to setup local API to receive events')
self.state = ha.CoreState.starting
ha.create_timer(self) ha.create_timer(self)
self.bus.fire(ha.EVENT_HOMEASSISTANT_START, self.bus.fire(ha.EVENT_HOMEASSISTANT_START,
origin=ha.EventOrigin.remote) origin=ha.EventOrigin.remote)
# Give eventlet time to startup # Ensure local HTTP is started
import eventlet self.pool.block_till_done()
eventlet.sleep(0.1) self.state = ha.CoreState.running
time.sleep(0.05)
# Setup that events from remote_api get forwarded to local_api # Setup that events from remote_api get forwarded to local_api
# Do this after we fire START, otherwise HTTP is not started # Do this after we are running, otherwise HTTP is not started
# or requests are blocked
if not connect_remote_events(self.remote_api, self.config.api): if not connect_remote_events(self.remote_api, self.config.api):
raise HomeAssistantError(( raise HomeAssistantError((
'Could not setup event forwarding from api {} to ' 'Could not setup event forwarding from api {} to '
@ -153,6 +158,7 @@ class HomeAssistant(ha.HomeAssistant):
def stop(self): def stop(self):
"""Stop Home Assistant and shuts down all threads.""" """Stop Home Assistant and shuts down all threads."""
_LOGGER.info("Stopping") _LOGGER.info("Stopping")
self.state = ha.CoreState.stopping
self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP, self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP,
origin=ha.EventOrigin.remote) origin=ha.EventOrigin.remote)
@ -161,6 +167,7 @@ class HomeAssistant(ha.HomeAssistant):
# Disconnect master event forwarding # Disconnect master event forwarding
disconnect_remote_events(self.remote_api, self.config.api) disconnect_remote_events(self.remote_api, self.config.api)
self.state = ha.CoreState.not_running
class EventBus(ha.EventBus): class EventBus(ha.EventBus):

View File

@ -5,7 +5,6 @@ pytz>=2016.4
pip>=7.0.0 pip>=7.0.0
jinja2>=2.8 jinja2>=2.8
voluptuous==0.8.9 voluptuous==0.8.9
eventlet==0.19.0
# homeassistant.components.isy994 # homeassistant.components.isy994
PyISY==1.0.6 PyISY==1.0.6
@ -48,6 +47,9 @@ blockchain==1.3.3
# homeassistant.components.notify.aws_sqs # homeassistant.components.notify.aws_sqs
boto3==1.3.1 boto3==1.3.1
# homeassistant.components.http
cherrypy==6.0.2
# homeassistant.components.notify.xmpp # homeassistant.components.notify.xmpp
dnspython3==1.12.0 dnspython3==1.12.0
@ -61,9 +63,6 @@ eliqonline==1.0.12
# homeassistant.components.enocean # homeassistant.components.enocean
enocean==0.31 enocean==0.31
# homeassistant.components.http
eventlet==0.19.0
# homeassistant.components.thermostat.honeywell # homeassistant.components.thermostat.honeywell
evohomeclient==0.2.5 evohomeclient==0.2.5

View File

@ -17,7 +17,6 @@ REQUIRES = [
'pip>=7.0.0', 'pip>=7.0.0',
'jinja2>=2.8', 'jinja2>=2.8',
'voluptuous==0.8.9', 'voluptuous==0.8.9',
'eventlet==0.19.0',
] ]
setup( setup(

View File

@ -1,8 +1,8 @@
"""The tests the for Locative device tracker platform.""" """The tests the for Locative device tracker platform."""
import time
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
import eventlet
import requests import requests
from homeassistant import bootstrap, const from homeassistant import bootstrap, const
@ -32,12 +32,9 @@ def setUpModule(): # pylint: disable=invalid-name
bootstrap.setup_component(hass, http.DOMAIN, { bootstrap.setup_component(hass, http.DOMAIN, {
http.DOMAIN: { http.DOMAIN: {
http.CONF_SERVER_PORT: SERVER_PORT http.CONF_SERVER_PORT: SERVER_PORT
} },
}) })
# Set up API
bootstrap.setup_component(hass, 'api')
# Set up device tracker # Set up device tracker
bootstrap.setup_component(hass, device_tracker.DOMAIN, { bootstrap.setup_component(hass, device_tracker.DOMAIN, {
device_tracker.DOMAIN: { device_tracker.DOMAIN: {
@ -46,7 +43,7 @@ def setUpModule(): # pylint: disable=invalid-name
}) })
hass.start() hass.start()
eventlet.sleep(0.05) time.sleep(0.05)
def tearDownModule(): # pylint: disable=invalid-name def tearDownModule(): # pylint: disable=invalid-name

View File

@ -1,9 +1,9 @@
"""The tests for the Alexa component.""" """The tests for the Alexa component."""
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
import unittest
import json import json
import time
import unittest
import eventlet
import requests import requests
from homeassistant import bootstrap, const from homeassistant import bootstrap, const
@ -86,8 +86,7 @@ def setUpModule(): # pylint: disable=invalid-name
}) })
hass.start() hass.start()
time.sleep(0.05)
eventlet.sleep(0.1)
def tearDownModule(): # pylint: disable=invalid-name def tearDownModule(): # pylint: disable=invalid-name

View File

@ -1,12 +1,12 @@
"""The tests for the Home Assistant API component.""" """The tests for the Home Assistant API component."""
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
# from contextlib import closing from contextlib import closing
import json import json
import tempfile import tempfile
import time
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
import eventlet
import requests import requests
from homeassistant import bootstrap, const from homeassistant import bootstrap, const
@ -48,10 +48,7 @@ def setUpModule(): # pylint: disable=invalid-name
bootstrap.setup_component(hass, 'api') bootstrap.setup_component(hass, 'api')
hass.start() hass.start()
time.sleep(0.05)
# To start HTTP
# TODO fix this
eventlet.sleep(0.05)
def tearDownModule(): # pylint: disable=invalid-name def tearDownModule(): # pylint: disable=invalid-name
@ -387,25 +384,23 @@ class TestAPI(unittest.TestCase):
headers=HA_HEADERS) headers=HA_HEADERS)
self.assertEqual(422, req.status_code) self.assertEqual(422, req.status_code)
# TODO disabled because eventlet cannot validate # Setup a real one
# a connection to itself, need a second instance req = requests.post(
# # Setup a real one _url(const.URL_API_EVENT_FORWARD),
# req = requests.post( data=json.dumps({
# _url(const.URL_API_EVENT_FORWARD), 'api_password': API_PASSWORD,
# data=json.dumps({ 'host': '127.0.0.1',
# 'api_password': API_PASSWORD, 'port': SERVER_PORT
# 'host': '127.0.0.1', }),
# 'port': SERVER_PORT headers=HA_HEADERS)
# }), self.assertEqual(200, req.status_code)
# headers=HA_HEADERS)
# self.assertEqual(200, req.status_code)
# # Delete it again.. # Delete it again..
# req = requests.delete( req = requests.delete(
# _url(const.URL_API_EVENT_FORWARD), _url(const.URL_API_EVENT_FORWARD),
# data=json.dumps({}), data=json.dumps({}),
# headers=HA_HEADERS) headers=HA_HEADERS)
# self.assertEqual(400, req.status_code) self.assertEqual(400, req.status_code)
req = requests.delete( req = requests.delete(
_url(const.URL_API_EVENT_FORWARD), _url(const.URL_API_EVENT_FORWARD),
@ -425,57 +420,58 @@ class TestAPI(unittest.TestCase):
headers=HA_HEADERS) headers=HA_HEADERS)
self.assertEqual(200, req.status_code) self.assertEqual(200, req.status_code)
# def test_stream(self): def test_stream(self):
# """Test the stream.""" """Test the stream."""
# listen_count = self._listen_count() listen_count = self._listen_count()
# with closing(requests.get(_url(const.URL_API_STREAM), timeout=3, with closing(requests.get(_url(const.URL_API_STREAM), timeout=3,
# stream=True, headers=HA_HEADERS)) as req: stream=True, headers=HA_HEADERS)) as req:
stream = req.iter_content(1)
self.assertEqual(listen_count + 1, self._listen_count())
# self.assertEqual(listen_count + 1, self._listen_count()) hass.bus.fire('test_event')
# hass.bus.fire('test_event') data = self._stream_next_event(stream)
# data = self._stream_next_event(req) self.assertEqual('test_event', data['event_type'])
# self.assertEqual('test_event', data['event_type']) def test_stream_with_restricted(self):
"""Test the stream with restrictions."""
listen_count = self._listen_count()
url = _url('{}?restrict=test_event1,test_event3'.format(
const.URL_API_STREAM))
with closing(requests.get(url, stream=True, timeout=3,
headers=HA_HEADERS)) as req:
stream = req.iter_content(1)
self.assertEqual(listen_count + 1, self._listen_count())
# def test_stream_with_restricted(self): hass.bus.fire('test_event1')
# """Test the stream with restrictions.""" data = self._stream_next_event(stream)
# listen_count = self._listen_count() self.assertEqual('test_event1', data['event_type'])
# url = _url('{}?restrict=test_event1,test_event3'.format(
# const.URL_API_STREAM))
# with closing(requests.get(url, stream=True, timeout=3,
# headers=HA_HEADERS)) as req:
# self.assertEqual(listen_count + 1, self._listen_count())
# hass.bus.fire('test_event1') hass.bus.fire('test_event2')
# data = self._stream_next_event(req) hass.bus.fire('test_event3')
# self.assertEqual('test_event1', data['event_type'])
# hass.bus.fire('test_event2') data = self._stream_next_event(stream)
# hass.bus.fire('test_event3') self.assertEqual('test_event3', data['event_type'])
# data = self._stream_next_event(req) def _stream_next_event(self, stream):
# self.assertEqual('test_event3', data['event_type']) """Read the stream for next event while ignoring ping."""
while True:
data = b''
last_new_line = False
for dat in stream:
if dat == b'\n' and last_new_line:
break
data += dat
last_new_line = dat == b'\n'
# def _stream_next_event(self, stream): conv = data.decode('utf-8').strip()[6:]
# """Read the stream for next event while ignoring ping."""
# while True:
# data = b''
# last_new_line = False
# for dat in stream.iter_content(1):
# if dat == b'\n' and last_new_line:
# break
# data += dat
# last_new_line = dat == b'\n'
# conv = data.decode('utf-8').strip()[6:] if conv != 'ping':
break
# if conv != 'ping': return json.loads(conv)
# break
# return json.loads(conv) def _listen_count(self):
"""Return number of event listeners."""
# def _listen_count(self): return sum(hass.bus.listeners.values())
# """Return number of event listeners."""
# return sum(hass.bus.listeners.values())

View File

@ -1,9 +1,9 @@
"""The tests for Home Assistant frontend.""" """The tests for Home Assistant frontend."""
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
import re import re
import time
import unittest import unittest
import eventlet
import requests import requests
import homeassistant.bootstrap as bootstrap import homeassistant.bootstrap as bootstrap
@ -42,10 +42,7 @@ def setUpModule(): # pylint: disable=invalid-name
bootstrap.setup_component(hass, 'frontend') bootstrap.setup_component(hass, 'frontend')
hass.start() hass.start()
time.sleep(0.05)
# Give eventlet time to start
# TODO fix this
eventlet.sleep(0.05)
def tearDownModule(): # pylint: disable=invalid-name def tearDownModule(): # pylint: disable=invalid-name

View File

@ -1,8 +1,8 @@
"""The tests for the Home Assistant HTTP component.""" """The tests for the Home Assistant HTTP component."""
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
import logging import logging
import time
import eventlet
import requests import requests
from homeassistant import bootstrap, const from homeassistant import bootstrap, const
@ -43,8 +43,7 @@ def setUpModule(): # pylint: disable=invalid-name
bootstrap.setup_component(hass, 'api') bootstrap.setup_component(hass, 'api')
hass.start() hass.start()
time.sleep(0.05)
eventlet.sleep(0.05)
def tearDownModule(): # pylint: disable=invalid-name def tearDownModule(): # pylint: disable=invalid-name
@ -83,7 +82,7 @@ class TestHttp:
logs = caplog.text() logs = caplog.text()
assert const.URL_API in logs # assert const.URL_API in logs
assert API_PASSWORD not in logs assert API_PASSWORD not in logs
def test_access_denied_with_wrong_password_in_url(self): def test_access_denied_with_wrong_password_in_url(self):
@ -106,5 +105,5 @@ class TestHttp:
logs = caplog.text() logs = caplog.text()
assert const.URL_API in logs # assert const.URL_API in logs
assert API_PASSWORD not in logs assert API_PASSWORD not in logs

View File

@ -1,9 +1,8 @@
"""Test Home Assistant remote methods and classes.""" """Test Home Assistant remote methods and classes."""
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
import time
import unittest import unittest
import eventlet
import homeassistant.core as ha import homeassistant.core as ha
import homeassistant.bootstrap as bootstrap import homeassistant.bootstrap as bootstrap
import homeassistant.remote as remote import homeassistant.remote as remote
@ -47,10 +46,7 @@ def setUpModule(): # pylint: disable=invalid-name
bootstrap.setup_component(hass, 'api') bootstrap.setup_component(hass, 'api')
hass.start() hass.start()
time.sleep(0.05)
# Give eventlet time to start
# TODO fix this
eventlet.sleep(0.05)
master_api = remote.API("127.0.0.1", API_PASSWORD, MASTER_PORT) master_api = remote.API("127.0.0.1", API_PASSWORD, MASTER_PORT)
@ -63,10 +59,6 @@ def setUpModule(): # pylint: disable=invalid-name
slave.start() slave.start()
# Give eventlet time to start
# TODO fix this
eventlet.sleep(0.05)
def tearDownModule(): # pylint: disable=invalid-name def tearDownModule(): # pylint: disable=invalid-name
"""Stop the Home Assistant server and slave.""" """Stop the Home Assistant server and slave."""
@ -257,7 +249,6 @@ class TestRemoteClasses(unittest.TestCase):
slave.pool.block_till_done() slave.pool.block_till_done()
# Wait till master gives updated state # Wait till master gives updated state
hass.pool.block_till_done() hass.pool.block_till_done()
eventlet.sleep(0.01)
self.assertEqual("remote.statemachine test", self.assertEqual("remote.statemachine test",
slave.states.get("remote.test").state) slave.states.get("remote.test").state)
@ -266,13 +257,11 @@ class TestRemoteClasses(unittest.TestCase):
"""Remove statemachine from master.""" """Remove statemachine from master."""
hass.states.set("remote.master_remove", "remove me!") hass.states.set("remote.master_remove", "remove me!")
hass.pool.block_till_done() hass.pool.block_till_done()
eventlet.sleep(0.01)
self.assertIn('remote.master_remove', slave.states.entity_ids()) self.assertIn('remote.master_remove', slave.states.entity_ids())
hass.states.remove("remote.master_remove") hass.states.remove("remote.master_remove")
hass.pool.block_till_done() hass.pool.block_till_done()
eventlet.sleep(0.01)
self.assertNotIn('remote.master_remove', slave.states.entity_ids()) self.assertNotIn('remote.master_remove', slave.states.entity_ids())
@ -280,14 +269,12 @@ class TestRemoteClasses(unittest.TestCase):
"""Remove statemachine from slave.""" """Remove statemachine from slave."""
hass.states.set("remote.slave_remove", "remove me!") hass.states.set("remote.slave_remove", "remove me!")
hass.pool.block_till_done() hass.pool.block_till_done()
eventlet.sleep(0.01)
self.assertIn('remote.slave_remove', slave.states.entity_ids()) self.assertIn('remote.slave_remove', slave.states.entity_ids())
self.assertTrue(slave.states.remove("remote.slave_remove")) self.assertTrue(slave.states.remove("remote.slave_remove"))
slave.pool.block_till_done() slave.pool.block_till_done()
hass.pool.block_till_done() hass.pool.block_till_done()
eventlet.sleep(0.01)
self.assertNotIn('remote.slave_remove', slave.states.entity_ids()) self.assertNotIn('remote.slave_remove', slave.states.entity_ids())
@ -306,6 +293,5 @@ class TestRemoteClasses(unittest.TestCase):
slave.pool.block_till_done() slave.pool.block_till_done()
# Wait till master gives updated event # Wait till master gives updated event
hass.pool.block_till_done() hass.pool.block_till_done()
eventlet.sleep(0.01)
self.assertEqual(1, len(test_value)) self.assertEqual(1, len(test_value))