--
You received this message because you are subscribed to the Google Groups "weewx-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to weewx-developm...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/weewx-development/05551e34-35a9-47f1-846a-ea7299f1d3f9n%40googlegroups.com.
TL;DR: let a service declare run_after and run_before, and have the engine sort each service group at startup. Today every installer that needs a position rewrites the list by hand in configure().
Open question: is startup the right time to resolve this, or should it stay in the installer, so that weewx.conf remains the only source of truth?
Why configure() is not enough
An installer resolves the order once, at install time. It knows nothing about an extension installed later, and it does not notice when the neighbour is uninstalled. Every installer writes the same loop again.
The version posted above has a bug. The elif branch cannot be reached, since a service cannot be both user.mysvc.MySvc and weewx.engine.StdQC. did_modify stays False, the corner case at the end appends the service, and the list comes out unchanged.
Proposal
Two class attributes on StdService, both empty by default:
class MySvc(StdService): """Fill in a wind chill before StdQC gets to check it.""" run_after = ('weewx.engine.StdCalibrate',) run_before = ('weewx.engine.StdQC',)The names are the strings from weewx.conf, so nothing has to import the neighbour.
Semantics:
Implementation
loadServices() imports and instantiates in one pass. The classes have to be known before sorting, so it becomes two passes per group:
for service_group in all_service_groups: svcs = [svc for svc in config_dict['Engine']['Services'].get(service_group, []) if svc] # Get the classes first: the ordering constraints live on them. classes = {svc: weeutil.weeutil.get_object(svc) for svc in svcs} for svc in order_services(svcs, classes): self.service_obj.append(classes[svc](self, config_dict))order_services() is Kahn's algorithm with a stable pick, about 25 lines. Not graphlib, which needs 3.9, while make vermin targets 3.7. Among the services whose constraints are satisfied, the one listed first in weewx.conf always wins, so the sort moves only what has to move.
A prototype gives:
What the user sees
The list in weewx.conf becomes a starting order rather than a guarantee. To keep that visible, log one INFO line per move:
Service user.mysvc.MySvc moved ahead of weewx.engine.StdQC (run_before)Nothing is logged when nothing moves. A weectl service list showing the effective order would answer the original question directly.
What this does not solve
It orders services, it does not decouple them. A service that depends on a particular neighbour still has that dependency. The dependency would sit in the code that has it, where it can be checked, instead of in the line order of a configuration file.
Until then
The loop from above, with the elif fixed:
def configure(self, engine): """Insert the service right after StdQC""" svc_list = weeutil.weeutil.option_as_list( engine.config_dict['Engine']['Services']['process_service']) if 'user.mysvc.MySvc' in svc_list: svc_list.remove('user.mysvc.MySvc') try: idx = svc_list.index('weewx.engine.StdQC') + 1 except ValueError: # StdQC is not in the list. Append instead. idx = len(svc_list) svc_list.insert(idx, 'user.mysvc.MySvc') engine.config_dict['Engine']['Services']['process_service'] = svc_list return TrueThe removal is required, not cosmetic: weectl extension install has already appended the service to the end of the group by the time configure() runs.