Skip to content

Commit dc72fec

Browse files
committed
duplicate service funcdefs correctly register the service; fixes #121
1 parent e5a746e commit dc72fec

File tree

4 files changed

+35
-4
lines changed

4 files changed

+35
-4
lines changed

custom_components/pyscript/eval.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,7 @@ async def do_service_call(func, ast_ctx, data):
372372

373373
return pyscript_service_handler
374374

375-
Function.hass.services.async_register(
376-
DOMAIN, self.name, pyscript_service_factory(self.name, self),
377-
)
375+
Function.service_register(DOMAIN, self.name, pyscript_service_factory(self.name, self))
378376
async_set_service_schema(Function.hass, DOMAIN, self.name, service_desc)
379377
self.trigger_service = True
380378
else:
@@ -513,7 +511,7 @@ def trigger_stop(self):
513511
trigger.stop()
514512
if self.trigger_service:
515513
self.trigger_service = False
516-
Function.hass.services.async_remove(DOMAIN, self.name)
514+
Function.service_remove(DOMAIN, self.name)
517515

518516
async def eval_decorators(self, ast_ctx):
519517
"""Evaluate the function decorators arguments."""

custom_components/pyscript/function.py

+26
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ class Function:
5959
task_waiter = None
6060
task_waiter_q = None
6161

62+
#
63+
# reference counting for service registrations; the new @service trigger
64+
# registers the service call before the old one is removed, so we only
65+
# remove the service registration when the reference count goes to zero
66+
#
67+
service_cnt = {}
68+
6269
def __init__(self):
6370
"""Warn on Function instantiation."""
6471
_LOGGER.error("Function class is not meant to be instantiated")
@@ -376,3 +383,22 @@ async def run_coro(cls, coro):
376383
def create_task(cls, coro):
377384
"""Create a new task that runs a coroutine."""
378385
return cls.hass.loop.create_task(cls.run_coro(coro))
386+
387+
@classmethod
388+
def service_register(cls, domain, service, callback):
389+
"""Register a new service callback."""
390+
key = f"{domain}.{service}"
391+
if key not in cls.service_cnt:
392+
cls.service_cnt[key] = 0
393+
cls.service_cnt[key] += 1
394+
cls.hass.services.async_register(domain, service, callback)
395+
396+
@classmethod
397+
def service_remove(cls, domain, service):
398+
"""Remove a service callback."""
399+
key = f"{domain}.{service}"
400+
if cls.service_cnt.get(key, 0) > 1:
401+
cls.service_cnt[key] -= 1
402+
return
403+
cls.service_cnt[key] = 0
404+
cls.hass.services.async_remove(domain, service)

docs/new_features.rst

+2
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,6 @@ Breaking changes since 1.1.0 include:
3838
Bug fixes since 1.1.0 include:
3939

4040
- Fixed shutdown trigger for case where it calls ``task.unique()`` (#117).
41+
- Duplicate ``@service`` function definitions (with the same name) now correctly register the service,
42+
reported by @wsw70 (#121)
4143
- Added error message for invalid ``@time_active`` argument, by @dlashua (#118).

tests/test_init.py

+5
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ async def test_service_exists(hass, caplog):
8989
def func1():
9090
pass
9191
92+
# make sure a double definition still keeps the service registered
93+
@service
94+
def func1():
95+
pass
96+
9297
def func2():
9398
pass
9499

0 commit comments

Comments
 (0)