Compare commits

...

3 Commits

Author SHA1 Message Date
renovate[bot]
1683317514 Update dependency pytest-cov to v6.3.0 2025-09-06 17:01:53 +00:00
renovate[bot]
e126f4e780 Update dependency pytest-asyncio to v1.1.0 (#476)
* Update dependency pytest-asyncio to v1.1.0

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-07-16 20:36:52 +02:00
Stefan Allius
7da7d6f15c Save task references (#475)
* Save a tast reference

Important: Save a reference of the created task,
to avoid a task disappearing mid-execution. The
event loop only keeps weak references to tasks.
A task that isn’t referenced elsewhere may get
garbage collected at any time, even before it’s
done. For reliable “fire-and-forget” background
tasks, gather them in a collection
2025-07-16 20:15:21 +02:00
6 changed files with 32 additions and 11 deletions

View File

@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased]
- Update dependency pytest-asyncio to v1.1.0
- save task references, to avoid a task disappearing mid-execution
- catch socket.gaierror exception and log this with info level
- Update dependency coverage to v7.9.2
- add-on: bump base-image to version 18.0.3

View File

@@ -1,7 +1,7 @@
flake8==7.3.0
pytest==8.4.1
pytest-asyncio==1.0.0
pytest-cov==6.2.1
pytest-asyncio==1.1.0
pytest-cov==6.3.0
python-dotenv==1.1.1
mock==5.2.0
coverage==7.9.2

View File

@@ -327,6 +327,7 @@ class SolarmanV5(SolarmanBase):
self.sensor_list = 0
self.mb_regs = [{'addr': 0x3000, 'len': 48},
{'addr': 0x2000, 'len': 96}]
self.background_tasks = set()
'''
Our puplic methods
@@ -339,6 +340,7 @@ class SolarmanV5(SolarmanBase):
self.inverter = None
self.switch.clear()
self.log_lvl.clear()
self.background_tasks.clear()
super().close()
def send_start_cmd(self, snr: int, host: str,
@@ -690,8 +692,10 @@ class SolarmanV5(SolarmanBase):
self.__forward_msg()
def publish_mqtt(self, key, data): # pragma: no cover
asyncio.ensure_future(
task = asyncio.ensure_future(
Proxy.mqtt.publish(key, data))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
def get_cmd_rsp_log_lvl(self) -> int:
ftype = self.ifc.rx_peek()[self.header_len]

View File

@@ -39,6 +39,7 @@ class InverterBase(InverterIfc, Proxy):
self.use_emulation = False
self.__ha_restarts = -1
self.remote = StreamPtr(None)
self.background_tasks = set()
ifc = AsyncStreamServer(reader, writer,
self.async_publ_mqtt,
self.create_remote,
@@ -73,6 +74,7 @@ class InverterBase(InverterIfc, Proxy):
if self.remote.ifc:
self.remote.ifc.close()
self.remote.ifc = None
self.background_tasks.clear()
async def disc(self, shutdown_started=False) -> None:
if self.remote.stream:
@@ -137,7 +139,10 @@ class InverterBase(InverterIfc, Proxy):
logging.info(f'[{self.remote.stream.node_id}:'
f'{self.remote.stream.conn_no}] '
f'Connected to {addr}')
asyncio.create_task(self.remote.ifc.client_loop(addr))
task = asyncio.create_task(
self.remote.ifc.client_loop(addr))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
except (ConnectionRefusedError,
TimeoutError,

View File

@@ -43,6 +43,7 @@ class ModbusTcp():
def __init__(self, loop, tim_restart=10) -> None:
self.tim_restart = tim_restart
self.background_tasks = set()
inverters = Config.get('inverters')
batteries = Config.get('batteries')
@@ -54,10 +55,13 @@ class ModbusTcp():
and 'client_mode' in inv):
client = inv['client_mode']
logger.info(f"'client_mode' for Monitoring-SN: {inv['monitor_sn']} host: {client['host']}:{client['port']}, forward: {client['forward']}") # noqa: E501
loop.create_task(self.modbus_loop(client['host'],
client['port'],
inv['monitor_sn'],
client['forward']))
task = loop.create_task(
self.modbus_loop(client['host'],
client['port'],
inv['monitor_sn'],
client['forward']))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
async def modbus_loop(self, host, port,
snr: int, forward: bool) -> None:

View File

@@ -218,6 +218,7 @@ app = Quart(__name__,
static_folder='web/static')
app.secret_key = 'JKLdks.dajlKKKdladkflKwolafallsdfl'
app.jinja_env.globals.update(url_for=url_for)
app.background_tasks = set()
server = Server(app, __name__ == "__main__")
Web(app, server.trans_path, server.rel_urls)
@@ -268,9 +269,13 @@ async def startup_app(): # pragma: no cover
for inv_class, port in [(InverterG3, 5005), (InverterG3P, 10000)]:
logging.info(f'listen on port: {port} for inverters')
loop.create_task(asyncio.start_server(lambda r, w, i=inv_class:
handle_client(r, w, i),
'0.0.0.0', port))
task = loop.create_task(
asyncio.start_server(lambda r, w, i=inv_class:
handle_client(r, w, i),
'0.0.0.0', port))
app.background_tasks.add(task)
task.add_done_callback(app.background_tasks.discard)
ProxyState.set_up(True)
@@ -294,6 +299,7 @@ async def handle_shutdown(): # pragma: no cover
await inverter.disc(True)
logging.info('Proxy disconnecting done')
app.background_tasks.clear()
await Proxy.class_close(loop)