mirror of
https://github.com/home-assistant/core.git
synced 2025-07-10 14:57:09 +00:00
Core support for hass.io calls & Bugfix check_config (#11571)
* Initial overwrites * Add check_config function. * Update hassio.py * Address comments * add hassio support * add more tests * revert core changes * Address check_config * Address comment with api_bool * Bugfix check_config * Update core.py * Update test_core.py * Update config.py * Update hassio.py * Update config.py * Update test_config.py
This commit is contained in:
parent
033e06868e
commit
f2cc00cc64
@ -17,13 +17,16 @@ from aiohttp.hdrs import CONTENT_TYPE
|
|||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.core import callback, DOMAIN as HASS_DOMAIN
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONTENT_TYPE_TEXT_PLAIN, SERVER_PORT, CONF_TIME_ZONE)
|
CONTENT_TYPE_TEXT_PLAIN, SERVER_PORT, CONF_TIME_ZONE,
|
||||||
|
SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART)
|
||||||
|
from homeassistant.components import SERVICE_CHECK_CONFIG
|
||||||
from homeassistant.components.http import (
|
from homeassistant.components.http import (
|
||||||
HomeAssistantView, KEY_AUTHENTICATED, CONF_API_PASSWORD, CONF_SERVER_PORT,
|
HomeAssistantView, KEY_AUTHENTICATED, CONF_API_PASSWORD, CONF_SERVER_PORT,
|
||||||
CONF_SERVER_HOST, CONF_SSL_CERTIFICATE)
|
CONF_SERVER_HOST, CONF_SSL_CERTIFICATE)
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -34,7 +37,7 @@ DEPENDENCIES = ['http']
|
|||||||
X_HASSIO = 'X-HASSIO-KEY'
|
X_HASSIO = 'X-HASSIO-KEY'
|
||||||
|
|
||||||
DATA_HOMEASSISTANT_VERSION = 'hassio_hass_version'
|
DATA_HOMEASSISTANT_VERSION = 'hassio_hass_version'
|
||||||
HASSIO_UPDATE_INTERVAL = timedelta(hours=1)
|
HASSIO_UPDATE_INTERVAL = timedelta(minutes=55)
|
||||||
|
|
||||||
SERVICE_ADDON_START = 'addon_start'
|
SERVICE_ADDON_START = 'addon_start'
|
||||||
SERVICE_ADDON_STOP = 'addon_stop'
|
SERVICE_ADDON_STOP = 'addon_stop'
|
||||||
@ -120,12 +123,40 @@ MAP_SERVICE_API = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def get_homeassistant_version(hass):
|
def get_homeassistant_version(hass):
|
||||||
"""Return last available HomeAssistant version."""
|
"""Return latest available HomeAssistant version.
|
||||||
|
|
||||||
|
Async friendly.
|
||||||
|
"""
|
||||||
return hass.data.get(DATA_HOMEASSISTANT_VERSION)
|
return hass.data.get(DATA_HOMEASSISTANT_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@bind_hass
|
||||||
|
def is_hassio(hass):
|
||||||
|
"""Return True if hass.io is loaded.
|
||||||
|
|
||||||
|
Async friendly.
|
||||||
|
"""
|
||||||
|
return DOMAIN in hass.config.components
|
||||||
|
|
||||||
|
|
||||||
|
@bind_hass
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_check_config(hass):
|
||||||
|
"""Check config over Hass.io API."""
|
||||||
|
result = yield from hass.data[DOMAIN].send_command(
|
||||||
|
'/homeassistant/check', timeout=300)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
return "Hass.io config check API error"
|
||||||
|
elif result['result'] == "error":
|
||||||
|
return result['message']
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_setup(hass, config):
|
def async_setup(hass, config):
|
||||||
"""Set up the HASSio component."""
|
"""Set up the HASSio component."""
|
||||||
@ -136,7 +167,7 @@ def async_setup(hass, config):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
websession = hass.helpers.aiohttp_client.async_get_clientsession()
|
websession = hass.helpers.aiohttp_client.async_get_clientsession()
|
||||||
hassio = HassIO(hass.loop, websession, host)
|
hass.data[DOMAIN] = hassio = HassIO(hass.loop, websession, host)
|
||||||
|
|
||||||
if not (yield from hassio.is_connected()):
|
if not (yield from hassio.is_connected()):
|
||||||
_LOGGER.error("Not connected with HassIO!")
|
_LOGGER.error("Not connected with HassIO!")
|
||||||
@ -170,11 +201,14 @@ def async_setup(hass, config):
|
|||||||
payload = data
|
payload = data
|
||||||
|
|
||||||
# Call API
|
# Call API
|
||||||
yield from hassio.send_command(
|
ret = yield from hassio.send_command(
|
||||||
api_command.format(addon=addon, snapshot=snapshot),
|
api_command.format(addon=addon, snapshot=snapshot),
|
||||||
payload=payload, timeout=MAP_SERVICE_API[service.service][2]
|
payload=payload, timeout=MAP_SERVICE_API[service.service][2]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not ret or ret['result'] != "ok":
|
||||||
|
_LOGGER.error("Error on Hass.io API: %s", ret['message'])
|
||||||
|
|
||||||
for service, settings in MAP_SERVICE_API.items():
|
for service, settings in MAP_SERVICE_API.items():
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN, service, async_service_handler, schema=settings[1])
|
DOMAIN, service, async_service_handler, schema=settings[1])
|
||||||
@ -193,9 +227,44 @@ def async_setup(hass, config):
|
|||||||
# Fetch last version
|
# Fetch last version
|
||||||
yield from update_homeassistant_version(None)
|
yield from update_homeassistant_version(None)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_handle_core_service(call):
|
||||||
|
"""Service handler for handling core services."""
|
||||||
|
if call.service == SERVICE_HOMEASSISTANT_STOP:
|
||||||
|
yield from hassio.send_command('/homeassistant/stop')
|
||||||
|
return
|
||||||
|
|
||||||
|
error = yield from async_check_config(hass)
|
||||||
|
if error:
|
||||||
|
_LOGGER.error(error)
|
||||||
|
hass.components.persistent_notification.async_create(
|
||||||
|
"Config error. See dev-info panel for details.",
|
||||||
|
"Config validating", "{0}.check_config".format(HASS_DOMAIN))
|
||||||
|
return
|
||||||
|
|
||||||
|
if call.service == SERVICE_HOMEASSISTANT_RESTART:
|
||||||
|
yield from hassio.send_command('/homeassistant/restart')
|
||||||
|
|
||||||
|
# Mock core services
|
||||||
|
for service in (SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART,
|
||||||
|
SERVICE_CHECK_CONFIG):
|
||||||
|
hass.services.async_register(
|
||||||
|
HASS_DOMAIN, service, async_handle_core_service)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _api_bool(funct):
|
||||||
|
"""API wrapper to return Boolean."""
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _wrapper(*argv, **kwargs):
|
||||||
|
"""Wrapper function."""
|
||||||
|
data = yield from funct(*argv, **kwargs)
|
||||||
|
return data and data['result'] == "ok"
|
||||||
|
|
||||||
|
return _wrapper
|
||||||
|
|
||||||
|
|
||||||
class HassIO(object):
|
class HassIO(object):
|
||||||
"""Small API wrapper for HassIO."""
|
"""Small API wrapper for HassIO."""
|
||||||
|
|
||||||
@ -205,6 +274,7 @@ class HassIO(object):
|
|||||||
self.websession = websession
|
self.websession = websession
|
||||||
self._ip = ip
|
self._ip = ip
|
||||||
|
|
||||||
|
@_api_bool
|
||||||
def is_connected(self):
|
def is_connected(self):
|
||||||
"""Return True if it connected to HassIO supervisor.
|
"""Return True if it connected to HassIO supervisor.
|
||||||
|
|
||||||
@ -219,6 +289,7 @@ class HassIO(object):
|
|||||||
"""
|
"""
|
||||||
return self.send_command("/homeassistant/info", method="get")
|
return self.send_command("/homeassistant/info", method="get")
|
||||||
|
|
||||||
|
@_api_bool
|
||||||
def update_hass_api(self, http_config):
|
def update_hass_api(self, http_config):
|
||||||
"""Update Home-Assistant API data on HassIO.
|
"""Update Home-Assistant API data on HassIO.
|
||||||
|
|
||||||
@ -238,6 +309,7 @@ class HassIO(object):
|
|||||||
|
|
||||||
return self.send_command("/homeassistant/options", payload=options)
|
return self.send_command("/homeassistant/options", payload=options)
|
||||||
|
|
||||||
|
@_api_bool
|
||||||
def update_hass_timezone(self, core_config):
|
def update_hass_timezone(self, core_config):
|
||||||
"""Update Home-Assistant timezone data on HassIO.
|
"""Update Home-Assistant timezone data on HassIO.
|
||||||
|
|
||||||
@ -261,7 +333,7 @@ class HassIO(object):
|
|||||||
X_HASSIO: os.environ.get('HASSIO_TOKEN')
|
X_HASSIO: os.environ.get('HASSIO_TOKEN')
|
||||||
})
|
})
|
||||||
|
|
||||||
if request.status != 200:
|
if request.status not in (200, 400):
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"%s return code %d.", command, request.status)
|
"%s return code %d.", command, request.status)
|
||||||
return None
|
return None
|
||||||
|
@ -97,9 +97,15 @@ def async_setup(hass, config):
|
|||||||
|
|
||||||
newest, releasenotes = result
|
newest, releasenotes = result
|
||||||
|
|
||||||
|
# Skip on dev
|
||||||
if newest is None or 'dev' in current_version:
|
if newest is None or 'dev' in current_version:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Load data from supervisor on hass.io
|
||||||
|
if hass.components.hassio.is_hassio():
|
||||||
|
newest = hass.components.hassio.get_homeassistant_version()
|
||||||
|
|
||||||
|
# Validate version
|
||||||
if StrictVersion(newest) > StrictVersion(current_version):
|
if StrictVersion(newest) > StrictVersion(current_version):
|
||||||
_LOGGER.info("The latest available version is %s", newest)
|
_LOGGER.info("The latest available version is %s", newest)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
@ -131,6 +137,7 @@ def get_system_info(hass, include_components):
|
|||||||
'timezone': dt_util.DEFAULT_TIME_ZONE.zone,
|
'timezone': dt_util.DEFAULT_TIME_ZONE.zone,
|
||||||
'version': current_version,
|
'version': current_version,
|
||||||
'virtualenv': os.environ.get('VIRTUAL_ENV') is not None,
|
'virtualenv': os.environ.get('VIRTUAL_ENV') is not None,
|
||||||
|
'hassio': hass.components.hassio.is_hassio(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if include_components:
|
if include_components:
|
||||||
|
@ -33,6 +33,8 @@ from homeassistant.helpers import config_per_platform, extract_domain_configs
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DATA_PERSISTENT_ERRORS = 'bootstrap_persistent_errors'
|
DATA_PERSISTENT_ERRORS = 'bootstrap_persistent_errors'
|
||||||
|
RE_YAML_ERROR = re.compile(r"homeassistant\.util\.yaml")
|
||||||
|
RE_ASCII = re.compile(r"\033\[[^m]*m")
|
||||||
HA_COMPONENT_URL = '[{}](https://home-assistant.io/components/{}/)'
|
HA_COMPONENT_URL = '[{}](https://home-assistant.io/components/{}/)'
|
||||||
YAML_CONFIG_FILE = 'configuration.yaml'
|
YAML_CONFIG_FILE = 'configuration.yaml'
|
||||||
VERSION_FILE = '.HA_VERSION'
|
VERSION_FILE = '.HA_VERSION'
|
||||||
@ -655,15 +657,19 @@ def async_check_ha_config_file(hass):
|
|||||||
proc = yield from asyncio.create_subprocess_exec(
|
proc = yield from asyncio.create_subprocess_exec(
|
||||||
sys.executable, '-m', 'homeassistant', '--script',
|
sys.executable, '-m', 'homeassistant', '--script',
|
||||||
'check_config', '--config', hass.config.config_dir,
|
'check_config', '--config', hass.config.config_dir,
|
||||||
stdout=asyncio.subprocess.PIPE, loop=hass.loop)
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.STDOUT, loop=hass.loop)
|
||||||
|
|
||||||
# Wait for the subprocess exit
|
# Wait for the subprocess exit
|
||||||
stdout_data, dummy = yield from proc.communicate()
|
log, _ = yield from proc.communicate()
|
||||||
result = yield from proc.wait()
|
exit_code = yield from proc.wait()
|
||||||
|
|
||||||
if not result:
|
# Convert to ASCII
|
||||||
return None
|
log = RE_ASCII.sub('', log.decode())
|
||||||
|
|
||||||
return re.sub(r'\033\[[^m]*m', '', str(stdout_data, 'utf-8'))
|
if exit_code != 0 or RE_YAML_ERROR.search(log):
|
||||||
|
return log
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -7,6 +7,7 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
from homeassistant.components.hassio import async_check_config
|
||||||
|
|
||||||
from tests.common import mock_coro
|
from tests.common import mock_coro
|
||||||
|
|
||||||
@ -60,6 +61,8 @@ def test_fail_setup_cannot_connect(hass):
|
|||||||
result = yield from async_setup_component(hass, 'hassio', {})
|
result = yield from async_setup_component(hass, 'hassio', {})
|
||||||
assert not result
|
assert not result
|
||||||
|
|
||||||
|
assert not hass.components.hassio.is_hassio()
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_setup_api_ping(hass, aioclient_mock):
|
def test_setup_api_ping(hass, aioclient_mock):
|
||||||
@ -75,7 +78,8 @@ def test_setup_api_ping(hass, aioclient_mock):
|
|||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 2
|
assert aioclient_mock.call_count == 2
|
||||||
assert hass.data['hassio_hass_version'] == "10.0"
|
assert hass.components.hassio.get_homeassistant_version() == "10.0"
|
||||||
|
assert hass.components.hassio.is_hassio()
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
@ -215,6 +219,7 @@ def test_service_register(hassio_env, hass):
|
|||||||
assert hass.services.has_service('hassio', 'addon_stdin')
|
assert hass.services.has_service('hassio', 'addon_stdin')
|
||||||
assert hass.services.has_service('hassio', 'host_shutdown')
|
assert hass.services.has_service('hassio', 'host_shutdown')
|
||||||
assert hass.services.has_service('hassio', 'host_reboot')
|
assert hass.services.has_service('hassio', 'host_reboot')
|
||||||
|
assert hass.services.has_service('hassio', 'host_reboot')
|
||||||
assert hass.services.has_service('hassio', 'snapshot_full')
|
assert hass.services.has_service('hassio', 'snapshot_full')
|
||||||
assert hass.services.has_service('hassio', 'snapshot_partial')
|
assert hass.services.has_service('hassio', 'snapshot_partial')
|
||||||
assert hass.services.has_service('hassio', 'restore_full')
|
assert hass.services.has_service('hassio', 'restore_full')
|
||||||
@ -294,6 +299,57 @@ def test_service_calls(hassio_env, hass, aioclient_mock):
|
|||||||
'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False}
|
'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False}
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_service_calls_core(hassio_env, hass, aioclient_mock):
|
||||||
|
"""Call core service and check the API calls behind that."""
|
||||||
|
assert (yield from async_setup_component(hass, 'hassio', {}))
|
||||||
|
|
||||||
|
aioclient_mock.post(
|
||||||
|
"http://127.0.0.1/homeassistant/restart", json={'result': 'ok'})
|
||||||
|
aioclient_mock.post(
|
||||||
|
"http://127.0.0.1/homeassistant/stop", json={'result': 'ok'})
|
||||||
|
aioclient_mock.post(
|
||||||
|
"http://127.0.0.1/homeassistant/check", json={'result': 'ok'})
|
||||||
|
|
||||||
|
yield from hass.services.async_call('homeassistant', 'stop')
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert aioclient_mock.call_count == 1
|
||||||
|
|
||||||
|
yield from hass.services.async_call('homeassistant', 'check_config')
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert aioclient_mock.call_count == 2
|
||||||
|
|
||||||
|
yield from hass.services.async_call('homeassistant', 'restart')
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert aioclient_mock.call_count == 4
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_check_config_ok(hassio_env, hass, aioclient_mock):
|
||||||
|
"""Check Config that is okay."""
|
||||||
|
assert (yield from async_setup_component(hass, 'hassio', {}))
|
||||||
|
|
||||||
|
aioclient_mock.post(
|
||||||
|
"http://127.0.0.1/homeassistant/check", json={'result': 'ok'})
|
||||||
|
|
||||||
|
assert (yield from async_check_config(hass)) is None
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_check_config_fail(hassio_env, hass, aioclient_mock):
|
||||||
|
"""Check Config that is wrong."""
|
||||||
|
assert (yield from async_setup_component(hass, 'hassio', {}))
|
||||||
|
|
||||||
|
aioclient_mock.post(
|
||||||
|
"http://127.0.0.1/homeassistant/check", json={
|
||||||
|
'result': 'error', 'message': "Error"})
|
||||||
|
|
||||||
|
assert (yield from async_check_config(hass)) == "Error"
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_forward_request(hassio_client):
|
def test_forward_request(hassio_client):
|
||||||
"""Test fetching normal path."""
|
"""Test fetching normal path."""
|
||||||
|
@ -8,7 +8,7 @@ import pytest
|
|||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.components import updater
|
from homeassistant.components import updater
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from tests.common import async_fire_time_changed, mock_coro
|
from tests.common import async_fire_time_changed, mock_coro, mock_component
|
||||||
|
|
||||||
NEW_VERSION = '10000.0'
|
NEW_VERSION = '10000.0'
|
||||||
MOCK_VERSION = '10.0'
|
MOCK_VERSION = '10.0'
|
||||||
@ -174,3 +174,24 @@ def test_error_fetching_new_version_invalid_response(hass, aioclient_mock):
|
|||||||
Mock(return_value=mock_coro({'fake': 'bla'}))):
|
Mock(return_value=mock_coro({'fake': 'bla'}))):
|
||||||
res = yield from updater.get_newest_version(hass, MOCK_HUUID, False)
|
res = yield from updater.get_newest_version(hass, MOCK_HUUID, False)
|
||||||
assert res is None
|
assert res is None
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_new_version_shows_entity_after_hour_hassio(
|
||||||
|
hass, mock_get_uuid, mock_get_newest_version):
|
||||||
|
"""Test if new entity is created if new version is available / hass.io."""
|
||||||
|
mock_get_uuid.return_value = MOCK_HUUID
|
||||||
|
mock_get_newest_version.return_value = mock_coro((NEW_VERSION, ''))
|
||||||
|
mock_component(hass, 'hassio')
|
||||||
|
hass.data['hassio_hass_version'] = "999.0"
|
||||||
|
|
||||||
|
res = yield from async_setup_component(
|
||||||
|
hass, updater.DOMAIN, {updater.DOMAIN: {}})
|
||||||
|
assert res, 'Updater failed to setup'
|
||||||
|
|
||||||
|
with patch('homeassistant.components.updater.current_version',
|
||||||
|
MOCK_VERSION):
|
||||||
|
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(hours=1))
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.is_state(updater.ENTITY_ID, "999.0")
|
||||||
|
@ -531,7 +531,7 @@ class TestConfig(unittest.TestCase):
|
|||||||
"""Check that restart propagates to stop."""
|
"""Check that restart propagates to stop."""
|
||||||
process_mock = mock.MagicMock()
|
process_mock = mock.MagicMock()
|
||||||
attrs = {
|
attrs = {
|
||||||
'communicate.return_value': mock_coro(('output', 'error')),
|
'communicate.return_value': mock_coro((b'output', None)),
|
||||||
'wait.return_value': mock_coro(0)}
|
'wait.return_value': mock_coro(0)}
|
||||||
process_mock.configure_mock(**attrs)
|
process_mock.configure_mock(**attrs)
|
||||||
mock_create.return_value = mock_coro(process_mock)
|
mock_create.return_value = mock_coro(process_mock)
|
||||||
@ -546,7 +546,7 @@ class TestConfig(unittest.TestCase):
|
|||||||
process_mock = mock.MagicMock()
|
process_mock = mock.MagicMock()
|
||||||
attrs = {
|
attrs = {
|
||||||
'communicate.return_value':
|
'communicate.return_value':
|
||||||
mock_coro(('\033[34mhello'.encode('utf-8'), 'error')),
|
mock_coro(('\033[34mhello'.encode('utf-8'), None)),
|
||||||
'wait.return_value': mock_coro(1)}
|
'wait.return_value': mock_coro(1)}
|
||||||
process_mock.configure_mock(**attrs)
|
process_mock.configure_mock(**attrs)
|
||||||
mock_create.return_value = mock_coro(process_mock)
|
mock_create.return_value = mock_coro(process_mock)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user