async def async_check_config(config_dir): """Check the OPP config.""" opp = core.OpenPeerPower() opp.config.config_dir = config_dir components = await async_check_op_config_file(opp) await opp.async_stop(force=True) return components
async def test_track_task_functions(loop): """Test function to start/stop track task and initial state.""" opp = ha.OpenPeerPower() try: assert opp._track_task opp.async_stop_track_tasks() assert not opp._track_task opp.async_track_tasks() assert opp._track_task finally: await opp.async_stop()
async def test_opp_start_starts_the_timer(loop): """Test when opp.starts, it starts the timer.""" opp = ha.OpenPeerPower() try: with patch("openpeerpower.core._async_create_timer") as mock_timer: await opp.async_start() assert opp.state == ha.CoreState.running assert not opp._track_task assert len(mock_timer.mock_calls) == 1 assert mock_timer.mock_calls[0][1][0] is opp finally: await opp.async_stop() assert opp.state == ha.CoreState.stopped
async def test_start_taking_too_long(loop, caplog): """Test when async_start takes too long.""" opp = ha.OpenPeerPower() caplog.set_level(logging.WARNING) try: with patch.object( opp, "async_block_till_done", side_effect=asyncio.TimeoutError), patch( "openpeerpower.core._async_create_timer") as mock_timer: await opp.async_start() assert opp.state == ha.CoreState.running assert len(mock_timer.mock_calls) == 1 assert mock_timer.mock_calls[0][1][0] is opp assert "Something is blocking Open Peer Power" in caplog.text finally: await opp.async_stop() assert opp.state == ha.CoreState.stopped
def run(args): """Handle benchmark commandline script.""" # Disable logging logging.getLogger("openpeerpower.core").setLevel(logging.CRITICAL) parser = argparse.ArgumentParser(description=("Run a Open Peer Power benchmark.")) parser.add_argument("name", choices=BENCHMARKS) parser.add_argument("--script", choices=["benchmark"]) args = parser.parse_args() bench = BENCHMARKS[args.name] print("Using event loop:", asyncio.get_event_loop_policy().__module__) with suppress(KeyboardInterrupt): while True: loop = asyncio.new_event_loop() opp = core.OpenPeerPower(loop) opp.async_stop_track_tasks() runtime = loop.run_until_complete(bench(opp)) print(f"Benchmark {bench.__name__} done in {runtime}s") loop.run_until_complete(opp.async_stop()) loop.close()
async def run_benchmark(bench): """Run a benchmark.""" opp = core.OpenPeerPower() runtime = await bench(opp) print(f"Benchmark {bench.__name__} done in {runtime}s") await opp.async_stop()
async def async_setup_opp( *, config_dir: str, verbose: bool, log_rotate_days: int, log_file: str, log_no_color: bool, skip_pip: bool, safe_mode: bool, ) -> Optional[core.OpenPeerPower]: """Set up Open Peer Power.""" opp = core.OpenPeerPower() opp.config.config_dir = config_dir async_enable_logging(opp, verbose, log_rotate_days, log_file, log_no_color) opp.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning( "Skipping pip installation of required modules. This may cause issues" ) if not await conf_util.async_ensure_config_exists(opp): _LOGGER.error("Error getting configuration path") return None _LOGGER.info("Config directory: %s", config_dir) config_dict = None basic_setup_success = False if not safe_mode: await opp.async_add_executor_job(conf_util.process_op_config_upgrade, opp) try: config_dict = await conf_util.async_opp_config_yaml(opp) except OpenPeerPowerError as err: _LOGGER.error( "Failed to parse configuration.yaml: %s. Activating safe mode", err, ) else: if not is_virtual_env(): await async_mount_local_lib_path(config_dir) basic_setup_success = ( await async_from_config_dict(config_dict, opp) is not None ) finally: clear_secret_cache() if config_dict is None: safe_mode = True elif not basic_setup_success: _LOGGER.warning("Unable to set up core integrations. Activating safe mode") safe_mode = True elif ( "frontend" in opp.data.get(DATA_SETUP, {}) and "frontend" not in opp.config.components ): _LOGGER.warning("Detected that frontend did not load. Activating safe mode") # Ask integrations to shut down. It's messy but we can't # do a clean stop without knowing what is broken opp.async_track_tasks() opp.bus.async_fire(EVENT_OPENPEERPOWER_STOP, {}) with contextlib.suppress(asyncio.TimeoutError): async with timeout(10): await opp.async_block_till_done() safe_mode = True opp = core.OpenPeerPower() opp.config.config_dir = config_dir if safe_mode: _LOGGER.info("Starting in safe mode") opp.config.safe_mode = True http_conf = (await http.async_get_last_config(opp)) or {} await async_from_config_dict( {"safe_mode": {}, "http": http_conf}, opp, ) return opp
async def async_test_open_peer_power(loop, load_registries=True): """Return a Open Peer Power object pointing at test config dir.""" opp = ha.OpenPeerPower() store = auth_store.AuthStore(opp) opp.auth = auth.AuthManager(opp, store, {}, {}) ensure_auth_manager_loaded(opp.auth) INSTANCES.append(opp) orig_async_add_job = opp.async_add_job orig_async_add_executor_job = opp.async_add_executor_job orig_async_create_task = opp.async_create_task def async_add_job(target, *args): """Add job.""" check_target = target while isinstance(check_target, ft.partial): check_target = check_target.func if isinstance(check_target, Mock) and not isinstance(target, AsyncMock): fut = asyncio.Future() fut.set_result(target(*args)) return fut return orig_async_add_job(target, *args) def async_add_executor_job(target, *args): """Add executor job.""" check_target = target while isinstance(check_target, ft.partial): check_target = check_target.func if isinstance(check_target, Mock): fut = asyncio.Future() fut.set_result(target(*args)) return fut return orig_async_add_executor_job(target, *args) def async_create_task(coroutine): """Create task.""" if isinstance(coroutine, Mock) and not isinstance(coroutine, AsyncMock): fut = asyncio.Future() fut.set_result(None) return fut return orig_async_create_task(coroutine) async def async_wait_for_task_count(self, max_remaining_tasks: int = 0) -> None: """Block until at most max_remaining_tasks remain. Based on OpenPeerPower.async_block_till_done """ # To flush out any call_soon_threadsafe await asyncio.sleep(0) start_time: float | None = None while len(self._pending_tasks) > max_remaining_tasks: pending: Collection[Awaitable[Any]] = [ task for task in self._pending_tasks if not task.done() ] self._pending_tasks.clear() if len(pending) > max_remaining_tasks: remaining_pending = await self._await_count_and_log_pending( pending, max_remaining_tasks=max_remaining_tasks) self._pending_tasks.extend(remaining_pending) if start_time is None: # Avoid calling monotonic() until we know # we may need to start logging blocked tasks. start_time = 0 elif start_time == 0: # If we have waited twice then we set the start # time start_time = monotonic() elif monotonic() - start_time > BLOCK_LOG_TIMEOUT: # We have waited at least three loops and new tasks # continue to block. At this point we start # logging all waiting tasks. for task in pending: _LOGGER.debug("Waiting for task: %s", task) else: self._pending_tasks.extend(pending) await asyncio.sleep(0) async def _await_count_and_log_pending( self, pending: Collection[Awaitable[Any]], max_remaining_tasks: int = 0) -> Collection[Awaitable[Any]]: """Block at most max_remaining_tasks remain and log tasks that take a long time. Based on OpenPeerPower._await_and_log_pending """ wait_time = 0 return_when = asyncio.ALL_COMPLETED if max_remaining_tasks: return_when = asyncio.FIRST_COMPLETED while len(pending) > max_remaining_tasks: _, pending = await asyncio.wait(pending, timeout=BLOCK_LOG_TIMEOUT, return_when=return_when) if not pending or max_remaining_tasks: return pending wait_time += BLOCK_LOG_TIMEOUT for task in pending: _LOGGER.debug("Waited %s seconds for task: %s", wait_time, task) return [] opp.async_add_job = async_add_job opp.async_add_executor_job = async_add_executor_job opp.async_create_task = async_create_task opp.async_wait_for_task_count = types.MethodType(async_wait_for_task_count, opp) opp._await_count_and_log_pending = types.MethodType( _await_count_and_log_pending, opp) opp.data[loader.DATA_CUSTOM_COMPONENTS] = {} opp.config.location_name = "test home" opp.config.config_dir = get_test_config_dir() opp.config.latitude = 32.87336 opp.config.longitude = -117.22743 opp.config.elevation = 0 opp.config.time_zone = "US/Pacific" opp.config.units = METRIC_SYSTEM opp.config.media_dirs = {"local": get_test_config_dir("media")} opp.config.skip_pip = True opp.config_entries = config_entries.ConfigEntries(opp, {}) opp.config_entries._entries = {} opp.config_entries._store._async_ensure_stop_listener = lambda: None # Load the registries if load_registries: await asyncio.gather( device_registry.async_load(opp), entity_registry.async_load(opp), area_registry.async_load(opp), ) await opp.async_block_till_done() opp.state = ha.CoreState.running # Mock async_start orig_start = opp.async_start async def mock_async_start(): """Start the mocking.""" # We only mock time during tests and we want to track tasks with patch("openpeerpower.core._async_create_timer"), patch.object( opp, "async_stop_track_tasks"): await orig_start() opp.async_start = mock_async_start @ha.callback def clear_instance(event): """Clear global instance.""" INSTANCES.remove(opp) opp.bus.async_listen_once(EVENT_OPENPEERPOWER_CLOSE, clear_instance) return opp
def check(config_dir, secrets=False): """Perform a check by mocking opp load functions.""" logging.getLogger("openpeerpower.loader").setLevel(logging.CRITICAL) res: Dict[str, Any] = { "yaml_files": OrderedDict(), # yaml_files loaded "secrets": OrderedDict(), # secret cache and secrets loaded "except": OrderedDict(), # exceptions raised (with config) #'components' is a OpenPeerPowerConfig # noqa: E265 "secret_cache": None, } # pylint: disable=possibly-unused-variable def mock_load(filename): """Mock opp.util.load_yaml to save config file names.""" res["yaml_files"][filename] = True return MOCKS["load"][1](filename) # pylint: disable=possibly-unused-variable def mock_secrets(ldr, node): """Mock _get_secrets.""" try: val = MOCKS["secrets"][1](ldr, node) except OpenPeerPowerError: val = None res["secrets"][node.value] = val return val # Patches to skip functions for sil in SILENCE: PATCHES[sil] = patch(sil) # Patches with local mock functions for key, val in MOCKS.items(): if not secrets and key == "secrets": continue # The * in the key is removed to find the mock_function (side_effect) # This allows us to use one side_effect to patch multiple locations mock_function = locals()["mock_" + key.replace("*", "")] PATCHES[key] = patch(val[0], side_effect=mock_function) # Start all patches for pat in PATCHES.values(): pat.start() if secrets: # Ensure !secrets point to the patched function yaml_loader.yaml.SafeLoader.add_constructor("!secret", yaml_loader.secret_yaml) try: opp = core.OpenPeerPower() opp.config.config_dir = config_dir res["components"] = opp.loop.run_until_complete( async_check_op_config_file(opp)) res["secret_cache"] = OrderedDict(yaml_loader.__SECRET_CACHE) for err in res["components"].errors: domain = err.domain or ERROR_STR res["except"].setdefault(domain, []).append(err.message) if err.config: res["except"].setdefault(domain, []).append(err.config) except Exception as err: # pylint: disable=broad-except _LOGGER.exception("BURB") print(color("red", "Fatal error while loading config:"), str(err)) res["except"].setdefault(ERROR_STR, []).append(str(err)) finally: # Stop all patches for pat in PATCHES.values(): pat.stop() if secrets: # Ensure !secrets point to the original function yaml_loader.yaml.SafeLoader.add_constructor( "!secret", yaml_loader.secret_yaml) bootstrap.clear_secret_cache() return res
async def async_setup_opp( runtime_config: RuntimeConfig, ) -> core.OpenPeerPower | None: """Set up Open Peer Power.""" opp = core.OpenPeerPower() opp.config.config_dir = runtime_config.config_dir async_enable_logging( opp, runtime_config.verbose, runtime_config.log_rotate_days, runtime_config.log_file, runtime_config.log_no_color, ) opp.config.skip_pip = runtime_config.skip_pip if runtime_config.skip_pip: _LOGGER.warning( "Skipping pip installation of required modules. This may cause issues" ) if not await conf_util.async_ensure_config_exists(opp): _LOGGER.error("Error getting configuration path") return None _LOGGER.info("Config directory: %s", runtime_config.config_dir) config_dict = None basic_setup_success = False safe_mode = runtime_config.safe_mode if not safe_mode: await opp.async_add_executor_job(conf_util.process_op_config_upgrade, opp) try: config_dict = await conf_util.async_opp_config_yaml(opp) except OpenPeerPowerError as err: _LOGGER.error( "Failed to parse configuration.yaml: %s. Activating safe mode", err, ) else: if not is_virtual_env(): await async_mount_local_lib_path(runtime_config.config_dir) basic_setup_success = (await async_from_config_dict(config_dict, opp) is not None) if config_dict is None: safe_mode = True elif not basic_setup_success: _LOGGER.warning( "Unable to set up core integrations. Activating safe mode") safe_mode = True elif ("frontend" in opp.data.get(DATA_SETUP, {}) and "frontend" not in opp.config.components): _LOGGER.warning( "Detected that frontend did not load. Activating safe mode") # Ask integrations to shut down. It's messy but we can't # do a clean stop without knowing what is broken with contextlib.suppress(asyncio.TimeoutError): async with opp.timeout.async_timeout(10): await opp.async_stop() safe_mode = True old_config = opp.config opp = core.OpenPeerPower() opp.config.skip_pip = old_config.skip_pip opp.config.internal_url = old_config.internal_url opp.config.external_url = old_config.external_url opp.config.config_dir = old_config.config_dir if safe_mode: _LOGGER.info("Starting in safe mode") opp.config.safe_mode = True http_conf = (await http.async_get_last_config(opp)) or {} await async_from_config_dict( { "safe_mode": {}, "http": http_conf }, opp, ) if runtime_config.open_ui: opp.add_job(open_opp_ui, opp) return opp