fix: bare except (#1159)

Bare `except:` catches SystemExit and KeyboardInterrupt, which can:
- Mask Ctrl+C handling during long optimization runs
- Hide critical system signals
- Make debugging harder

Replacement by `except Exception:` preserves the same error handling while respecting
system-level exceptions.

Co-authored-by: Milo @KeloYuan

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
Bobby Noelte
2026-07-20 01:11:33 +02:00
committed by GitHub
parent 6933b33542
commit 8bf798daeb
21 changed files with 59 additions and 59 deletions

View File

@@ -1,6 +1,6 @@
# Akkudoktor-EOS # Akkudoktor-EOS
**Version**: `v0.3.0.dev2607171715033828` **Version**: `v0.3.0.dev2607181096091018`
<!-- pyml disable line-length --> <!-- pyml disable line-length -->
**Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period. **Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period.

View File

@@ -8,7 +8,7 @@
"name": "Apache 2.0", "name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html" "url": "https://www.apache.org/licenses/LICENSE-2.0.html"
}, },
"version": "v0.3.0.dev2607171715033828" "version": "v0.3.0.dev2607181096091018"
}, },
"paths": { "paths": {
"/v1/admin/cache/clear": { "/v1/admin/cache/clear": {

View File

@@ -33,7 +33,7 @@ def akkudoktoreos_base_branch():
# Get the current branch # Get the current branch
try: try:
current_branch = repo.active_branch.name current_branch = repo.active_branch.name
except: except Exception:
# Maybe detached branch that has no name # Maybe detached branch that has no name
return None return None

View File

@@ -146,7 +146,7 @@ class HomeAssistantAdapterCommonSettings(SettingsBaseModel):
try: try:
adapter_eos = get_adapter() adapter_eos = get_adapter()
result = adapter_eos.provider_by_id("HomeAssistant").get_homeassistant_entity_ids() result = adapter_eos.provider_by_id("HomeAssistant").get_homeassistant_entity_ids()
except: except Exception:
return [] return []
return result return result
@@ -157,7 +157,7 @@ class HomeAssistantAdapterCommonSettings(SettingsBaseModel):
try: try:
adapter_eos = get_adapter() adapter_eos = get_adapter()
result = adapter_eos.provider_by_id("HomeAssistant").get_eos_solution_entity_ids() result = adapter_eos.provider_by_id("HomeAssistant").get_eos_solution_entity_ids()
except: except Exception:
return [] return []
return result return result
@@ -170,7 +170,7 @@ class HomeAssistantAdapterCommonSettings(SettingsBaseModel):
result = adapter_eos.provider_by_id( result = adapter_eos.provider_by_id(
"HomeAssistant" "HomeAssistant"
).get_eos_device_instruction_entity_ids() ).get_eos_device_instruction_entity_ids()
except: except Exception:
return [] return []
return result return result
@@ -256,7 +256,7 @@ class HomeAssistantAdapter(AdapterProvider):
optimization_solution_keys = self.config.optimization.keys optimization_solution_keys = self.config.optimization.keys
for key in sorted(optimization_solution_keys): for key in sorted(optimization_solution_keys):
solution_entity_ids.append(self._entity_id_from_solution_key(key)) solution_entity_ids.append(self._entity_id_from_solution_key(key))
except: except Exception:
solution_entity_ids = [] solution_entity_ids = []
return solution_entity_ids return solution_entity_ids

View File

@@ -250,7 +250,7 @@ class DataRecord(DataABC, MutableMapping):
try: try:
# Let getattr do the work # Let getattr do the work
return self.__getattr__(key) return self.__getattr__(key)
except: except AttributeError:
raise KeyError(f"'{key}' not found in the record fields.") raise KeyError(f"'{key}' not found in the record fields.")
def __setitem__(self, key: str, value: Any) -> None: def __setitem__(self, key: str, value: Any) -> None:
@@ -266,7 +266,7 @@ class DataRecord(DataABC, MutableMapping):
try: try:
# Let setattr do the work # Let setattr do the work
self.__setattr__(key, value) self.__setattr__(key, value)
except: except AttributeError:
raise KeyError(f"'{key}' is not a recognized field.") raise KeyError(f"'{key}' is not a recognized field.")
def __delitem__(self, key: str) -> None: def __delitem__(self, key: str) -> None:
@@ -280,7 +280,7 @@ class DataRecord(DataABC, MutableMapping):
""" """
try: try:
self.__delattr__(key) self.__delattr__(key)
except: except AttributeError:
raise KeyError(f"'{key}' is not a recognized field.") raise KeyError(f"'{key}' is not a recognized field.")
def __iter__(self) -> Iterator[str]: def __iter__(self) -> Iterator[str]:

View File

@@ -809,7 +809,7 @@ class DatabaseRecordProtocolMixin(
self._db_load_phase = DatabaseRecordProtocolLoadPhase.NONE self._db_load_phase = DatabaseRecordProtocolLoadPhase.NONE
try: try:
del self._db_initialized del self._db_initialized
except: except Exception:
logger.debug("_db_reset_state called on uninitialized sequence") logger.debug("_db_reset_state called on uninitialized sequence")
def _db_clone_empty(self: T_DatabaseRecordProtocol) -> T_DatabaseRecordProtocol: def _db_clone_empty(self: T_DatabaseRecordProtocol) -> T_DatabaseRecordProtocol:

View File

@@ -37,7 +37,7 @@ class LoggingCommonSettings(SettingsBaseModel):
"""Computed log file path based on data output path.""" """Computed log file path based on data output path."""
try: try:
path = SettingsBaseModel.config.general.data_output_path / "eos.log" path = SettingsBaseModel.config.general.data_output_path / "eos.log"
except: except Exception:
# Config may not be fully set up # Config may not be fully set up
path = None path = None
return path return path

View File

@@ -133,8 +133,8 @@ class PydanticModelNestedValueMixin:
try: try:
self._validate_path_structure(path) self._validate_path_structure(path)
pass pass
except: except Exception as e:
raise ValueError(f"Path '{path}' is invalid") raise ValueError(f"Path '{path}' is invalid: {e}")
path = path.strip("/") path = path.strip("/")
# Use private data workaround # Use private data workaround
# Should be: # Should be:

View File

@@ -974,7 +974,7 @@ class GeneticOptimization(OptimizationBase):
if self.optimize_ev and parameters.eauto and self.simulation.ev: if self.optimize_ev and parameters.eauto and self.simulation.ev:
try: try:
penalty = self.config.optimization.genetic.penalties["ev_soc_miss"] penalty = self.config.optimization.genetic.penalties["ev_soc_miss"]
except: except Exception:
# Use default # Use default
penalty = 10 penalty = 10
logger.error( logger.error(
@@ -1005,7 +1005,7 @@ class GeneticOptimization(OptimizationBase):
individuals = self.config.optimization.genetic.individuals individuals = self.config.optimization.genetic.individuals
if individuals is None: if individuals is None:
raise raise
except: except Exception:
individuals = 300 individuals = 300
logger.error("Individuals not configured. Using {}.", individuals) logger.error("Individuals not configured. Using {}.", individuals)
@@ -1076,7 +1076,7 @@ class GeneticOptimization(OptimizationBase):
if generations is None: if generations is None:
try: try:
generations = self.config.optimization.genetic.generations generations = self.config.optimization.genetic.generations
except: except Exception:
generations = 400 generations = 400
logger.error("Generations not configured. Using {}.", generations) logger.error("Generations not configured. Using {}.", generations)

View File

@@ -282,9 +282,9 @@ class GeneticOptimizationParameters(
fill_method="linear", fill_method="linear",
) )
pvforecast_ac_power = (array * power_to_energy_per_interval_factor).to_list() pvforecast_ac_power = (array * power_to_energy_per_interval_factor).to_list()
except: except Exception as e:
logger.info( logger.info(
"No PV forecast data available - defaulting to demo data. Parameter preparation attempt {}.", "No PV forecast data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.merge_settings_from_dict( cls.config.merge_settings_from_dict(
@@ -336,9 +336,9 @@ class GeneticOptimizationParameters(
fill_method="ffill", fill_method="ffill",
) )
elecprice_marketprice_wh = array.to_list() elecprice_marketprice_wh = array.to_list()
except: except Exception as e:
logger.info( logger.info(
"No Electricity Marketprice forecast data available - defaulting to demo data. Parameter preparation attempt {}.", "No Electricity Marketprice forecast data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.elecprice.provider = "ElecPriceAkkudoktor" cls.config.elecprice.provider = "ElecPriceAkkudoktor"
@@ -353,9 +353,9 @@ class GeneticOptimizationParameters(
fill_method="ffill", fill_method="ffill",
) )
loadforecast_power_w = array.to_list() loadforecast_power_w = array.to_list()
except: except Exception as e:
logger.info( logger.info(
"No Load forecast data available - defaulting to demo data. Parameter preparation attempt {}.", "No Load forecast data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.merge_settings_from_dict( cls.config.merge_settings_from_dict(
@@ -379,9 +379,9 @@ class GeneticOptimizationParameters(
fill_method="ffill", fill_method="ffill",
) )
feed_in_tariff_wh = array.to_list() feed_in_tariff_wh = array.to_list()
except: except Exception as e:
logger.info( logger.info(
"No feed in tariff forecast data available - defaulting to demo data. Parameter preparation attempt {}.", "No feed in tariff forecast data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.merge_settings_from_dict( cls.config.merge_settings_from_dict(
@@ -407,9 +407,9 @@ class GeneticOptimizationParameters(
fill_method="ffill", fill_method="ffill",
) )
weather_temp_air = array.to_list() weather_temp_air = array.to_list()
except: except Exception as e:
logger.info( logger.info(
"No weather forecast data available - defaulting to demo data. Parameter preparation attempt {}.", "No weather forecast data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.weather.provider = "BrightSky" cls.config.weather.provider = "BrightSky"
@@ -442,9 +442,9 @@ class GeneticOptimizationParameters(
max_soc_percentage=battery_config.max_soc_percentage, max_soc_percentage=battery_config.max_soc_percentage,
charge_rates=battery_config.charge_rates, charge_rates=battery_config.charge_rates,
) )
except: except Exception as e:
logger.info( logger.info(
"No battery device data available - defaulting to demo data. Parameter preparation attempt {}.", "No battery device data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.devices.batteries = [{"device_id": "battery1", "capacity_wh": 8000}] cls.config.devices.batteries = [{"device_id": "battery1", "capacity_wh": 8000}]
@@ -472,7 +472,7 @@ class GeneticOptimizationParameters(
initial_soc_factor = 0.0 initial_soc_factor = 0.0
# genetic parameter is 0..100 as int # genetic parameter is 0..100 as int
initial_soc_percentage = int(initial_soc_factor * 100) initial_soc_percentage = int(initial_soc_factor * 100)
except: except Exception:
initial_soc_percentage = None initial_soc_percentage = None
if initial_soc_percentage is None: if initial_soc_percentage is None:
logger.info( logger.info(
@@ -514,9 +514,9 @@ class GeneticOptimizationParameters(
min_soc_percentage=electric_vehicle_config.min_soc_percentage, min_soc_percentage=electric_vehicle_config.min_soc_percentage,
max_soc_percentage=electric_vehicle_config.max_soc_percentage, max_soc_percentage=electric_vehicle_config.max_soc_percentage,
) )
except: except Exception as e:
logger.info( logger.info(
"No electric_vehicle device data available - defaulting to demo data. Parameter preparation attempt {}.", "No electric_vehicle device data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.devices.max_electric_vehicles = 1 cls.config.devices.max_electric_vehicles = 1
@@ -544,7 +544,7 @@ class GeneticOptimizationParameters(
initial_soc_factor = 0.0 initial_soc_factor = 0.0
# genetic parameter is 0..100 as int # genetic parameter is 0..100 as int
initial_soc_percentage = int(initial_soc_factor * 100) initial_soc_percentage = int(initial_soc_factor * 100)
except: except Exception:
initial_soc_percentage = None initial_soc_percentage = None
if initial_soc_percentage is None: if initial_soc_percentage is None:
logger.info( logger.info(
@@ -580,9 +580,9 @@ class GeneticOptimizationParameters(
dc_to_ac_efficiency=inverter_config.dc_to_ac_efficiency, dc_to_ac_efficiency=inverter_config.dc_to_ac_efficiency,
max_ac_charge_power_w=inverter_config.max_ac_charge_power_w, max_ac_charge_power_w=inverter_config.max_ac_charge_power_w,
) )
except: except Exception as e:
logger.info( logger.info(
"No inverter device data available - defaulting to demo data. Parameter preparation attempt {}.", "No inverter device data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.devices.inverters = [ cls.config.devices.inverters = [
@@ -635,9 +635,9 @@ class GeneticOptimizationParameters(
duration_h=home_appliance_config.duration_h, duration_h=home_appliance_config.duration_h,
time_windows=home_appliance_config.time_windows, time_windows=home_appliance_config.time_windows,
) )
except: except Exception as e:
logger.info( logger.info(
"No home appliance device data available - defaulting to demo data. Parameter preparation attempt {}.", "No home appliance device data available - defaulting to demo data. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
cls.config.devices.home_appliances = [ cls.config.devices.home_appliances = [
@@ -668,9 +668,9 @@ class GeneticOptimizationParameters(
dishwasher=home_appliance_params, dishwasher=home_appliance_params,
start_solution=start_solution, start_solution=start_solution,
) )
except: except Exception as e:
logger.info( logger.info(
"Can not prepare optimization parameters - will retry. Parameter preparation attempt {}.", "Can not prepare optimization parameters - will retry. Parameter preparation attempt {}: {e}",
attempt, attempt,
) )
oparams = None oparams = None

View File

@@ -100,7 +100,7 @@ class OptimizationCommonSettings(SettingsBaseModel):
"""The keys of the solution.""" """The keys of the solution."""
try: try:
ems_eos = get_ems() ems_eos = get_ems()
except: except Exception:
# ems might not be initialized # ems might not be initialized
return [] return []

View File

@@ -16,7 +16,7 @@ def elecprice_provider_ids() -> list[str]:
"""Valid elecprice provider ids.""" """Valid elecprice provider ids."""
try: try:
prediction_eos = get_prediction() prediction_eos = get_prediction()
except: except Exception:
# Prediction may not be initialized # Prediction may not be initialized
# Return at least provider used in example # Return at least provider used in example
return ["ElecPriceAkkudoktor"] return ["ElecPriceAkkudoktor"]

View File

@@ -13,7 +13,7 @@ def feedintariff_provider_ids() -> list[str]:
"""Valid feedintariff provider ids.""" """Valid feedintariff provider ids."""
try: try:
prediction_eos = get_prediction() prediction_eos = get_prediction()
except: except Exception:
# Prediction may not be initialized # Prediction may not be initialized
# Return at least provider used in example # Return at least provider used in example
return ["FeedInTariffFixed", "FeedInTarifImport"] return ["FeedInTariffFixed", "FeedInTarifImport"]

View File

@@ -40,7 +40,7 @@ class FeedInTariffFixed(FeedInTariffProvider):
feed_in_tariff = ( feed_in_tariff = (
self.config.feedintariff.provider_settings.FeedInTariffFixed.feed_in_tariff_kwh self.config.feedintariff.provider_settings.FeedInTariffFixed.feed_in_tariff_kwh
) )
except: except Exception:
logger.exception(error_msg) logger.exception(error_msg)
raise ValueError(error_msg) raise ValueError(error_msg)
if feed_in_tariff is None: if feed_in_tariff is None:

View File

@@ -16,7 +16,7 @@ def load_providers() -> list[str]:
"""Valid load provider ids.""" """Valid load provider ids."""
try: try:
prediction_eos = get_prediction() prediction_eos = get_prediction()
except: except Exception:
# Prediction may not be initialized # Prediction may not be initialized
# Return at least provider used in example # Return at least provider used in example
return ["LoadAkkudoktor", "LoadVrm", "LoadImport"] return ["LoadAkkudoktor", "LoadVrm", "LoadImport"]

View File

@@ -20,7 +20,7 @@ def pvforecast_provider_ids() -> list[str]:
"""Valid PV forecast providers.""" """Valid PV forecast providers."""
try: try:
prediction_eos = get_prediction() prediction_eos = get_prediction()
except: except Exception:
# Prediction may not be initialized # Prediction may not be initialized
# Return at least provider used in example # Return at least provider used in example
return [ return [

View File

@@ -14,7 +14,7 @@ def weather_provider_ids() -> list[str]:
"""Valid weather provider ids.""" """Valid weather provider ids."""
try: try:
prediction_eos = get_prediction() prediction_eos = get_prediction()
except: except Exception:
# Prediction may not be initialized # Prediction may not be initialized
# Return at least provider used in example # Return at least provider used in example
return ["WeatherImport"] return ["WeatherImport"]

View File

@@ -528,7 +528,7 @@ def Configuration(
value_json_str: str = data.get("value", "") value_json_str: str = data.get("value", "")
try: try:
value = json.loads(value_json_str) value = json.loads(value_json_str)
except: except Exception:
if value_json_str in ("None", "none", "Null", "null"): if value_json_str in ("None", "none", "Null", "null"):
value = None value = None
else: else:
@@ -602,7 +602,7 @@ def Configuration(
# find some special configuration values # find some special configuration values
try: try:
max_planes = int(config_details["pvforecast.max_planes"]["value"]) max_planes = int(config_details["pvforecast.max_planes"]["value"])
except: except Exception:
max_planes = 0 max_planes = 0
logger.debug(f"max_planes: {max_planes}") logger.debug(f"max_planes: {max_planes}")
@@ -610,7 +610,7 @@ def Configuration(
homeassistant_entity_ids = json.loads( homeassistant_entity_ids = json.loads(
config_details["adapter.homeassistant.homeassistant_entity_ids"]["value"] config_details["adapter.homeassistant.homeassistant_entity_ids"]["value"]
) )
except: except Exception:
homeassistant_entity_ids = [] homeassistant_entity_ids = []
logger.debug(f"homeassistant_entity_ids: {homeassistant_entity_ids}") logger.debug(f"homeassistant_entity_ids: {homeassistant_entity_ids}")
@@ -619,7 +619,7 @@ def Configuration(
eos_solution_entity_ids = json.loads( eos_solution_entity_ids = json.loads(
config_details["adapter.homeassistant.eos_solution_entity_ids"]["value"] config_details["adapter.homeassistant.eos_solution_entity_ids"]["value"]
) )
except: except Exception:
eos_solution_entity_ids = [] eos_solution_entity_ids = []
logger.debug(f"eos_solution_entity_ids {eos_solution_entity_ids}") logger.debug(f"eos_solution_entity_ids {eos_solution_entity_ids}")
@@ -628,14 +628,14 @@ def Configuration(
eos_device_instruction_entity_ids = json.loads( eos_device_instruction_entity_ids = json.loads(
config_details["adapter.homeassistant.eos_device_instruction_entity_ids"]["value"] config_details["adapter.homeassistant.eos_device_instruction_entity_ids"]["value"]
) )
except: except Exception:
eos_device_instruction_entity_ids = [] eos_device_instruction_entity_ids = []
logger.debug(f"eos_device_instruction_entity_ids {eos_device_instruction_entity_ids}") logger.debug(f"eos_device_instruction_entity_ids {eos_device_instruction_entity_ids}")
devices_measurement_keys = [] devices_measurement_keys = []
try: try:
devices_measurement_keys = json.loads(config_details["devices.measurement_keys"]["value"]) devices_measurement_keys = json.loads(config_details["devices.measurement_keys"]["value"])
except: except Exception:
devices_measurement_keys = [] devices_measurement_keys = []
logger.debug(f"devices_measurement_keys {devices_measurement_keys}") logger.debug(f"devices_measurement_keys {devices_measurement_keys}")
@@ -691,7 +691,7 @@ def Configuration(
# Special configuration for prediction provider setting # Special configuration for prediction provider setting
try: try:
provider_ids = json.loads(config_details[config["name"] + "s"]["value"]) provider_ids = json.loads(config_details[config["name"] + "s"]["value"])
except: except Exception:
provider_ids = [] provider_ids = []
if config["type"].startswith("Optional[list"): if config["type"].startswith("Optional[list"):
update_form_factory = make_config_update_list_form(provider_ids) update_form_factory = make_config_update_list_form(provider_ids)

View File

@@ -521,10 +521,10 @@ def fastapi_config_file_put() -> ConfigEOS:
try: try:
get_config().to_config_file() get_config().to_config_file()
return get_config() return get_config()
except: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=404, status_code=404,
detail=f"Cannot save configuration to file '{get_config().config_file_path}'.", detail=f"Cannot save configuration to file '{get_config().config_file_path}': {e}",
) )

View File

@@ -416,8 +416,8 @@ def _parse_time_string(time_str: str, default_date: pendulum.Date = None) -> pen
# Try to parse as a standard timezone name # Try to parse as a standard timezone name
try: try:
timezone_info = pendulum.timezone(timezone_str) timezone_info = pendulum.timezone(timezone_str)
except: except Exception as e:
raise ValueError(f"Unknown timezone: {timezone_str}") raise ValueError(f"Unknown timezone: {timezone_str}: {e}")
elif re.match(r"[+-]\d{2}:?\d{2}", timezone_str): elif re.match(r"[+-]\d{2}:?\d{2}", timezone_str):
# Handle offset format like +05:30, -08:00, +0530, -0800 # Handle offset format like +05:30, -08:00, +0530, -0800
clean_tz = timezone_str.replace(":", "") clean_tz = timezone_str.replace(":", "")

View File

@@ -44,7 +44,7 @@ def caplog(caplog: LogCaptureFixture):
yield caplog yield caplog
try: try:
logger.remove(handler_id) logger.remove(handler_id)
except: except Exception:
# May already be deleted # May already be deleted
pass pass
@@ -57,7 +57,7 @@ def reportlog(pytestconfig):
yield yield
try: try:
logger.remove(handler_id) logger.remove(handler_id)
except: except Exception:
# May already be deleted # May already be deleted
pass pass