From b033446982dd0d922ef6ca21336925a1f270102d Mon Sep 17 00:00:00 2001 From: Peter Holloway Date: Tue, 14 Apr 2026 15:11:55 +0100 Subject: [PATCH 1/2] Include module version and location in logging --- src/blueapi/core/context.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/blueapi/core/context.py b/src/blueapi/core/context.py index 3e86b402c..83740290d 100644 --- a/src/blueapi/core/context.py +++ b/src/blueapi/core/context.py @@ -2,7 +2,7 @@ import sys from collections.abc import Callable from dataclasses import InitVar, dataclass, field, fields, is_dataclass -from importlib import import_module +from importlib import import_module, metadata from inspect import Parameter, isclass, signature from types import ModuleType, NoneType, UnionType from typing import Any, Generic, TypeVar, Union, get_args, get_origin, get_type_hints @@ -217,15 +217,25 @@ def find_device(self, addr: str | list[str]) -> Device | None: def with_config(self, config: EnvironmentConfig) -> None: if config.metadata is not None: self.run_engine.md |= config.metadata.model_dump() + package_map = metadata.packages_distributions() for source in config.sources: mod = import_module(source.module) + if root_pkg := package_map.get(source.module.split(".")[0]): + version = metadata.version(root_pkg[0]) + loc = mod.__spec__ and mod.__spec__.origin + summary = f"{root_pkg[0]}[version: {version}, location: {loc}]" + else: + summary = "" + match source: case PlanSource(): - LOGGER.info("Including plans from %s", source.module) + LOGGER.info("Including plans from %s (%s)", source.module, summary) self.with_plan_module(mod) case DeviceSource(): - LOGGER.info("Including devices from %s", source.module) + LOGGER.info( + "Including devices from %s (%s)", source.module, summary + ) LOGGER.warning( "'devices' environment kind is deprecated - please convert " "configuration to use deviceManager" @@ -233,7 +243,9 @@ def with_config(self, config: EnvironmentConfig) -> None: self.with_device_module(mod) case DodalSource(mock=mock): LOGGER.info( - "Including devices from 'dodal' source %s", source.module + "Including devices from 'dodal' source %s (%s)", + source.module, + summary, ) LOGGER.warning( "'dodal' environment kind is deprecated - please convert " @@ -242,9 +254,10 @@ def with_config(self, config: EnvironmentConfig) -> None: self.with_dodal_module(mod, mock=mock) case DeviceManagerSource(mock=mock, name=name): LOGGER.info( - "Including devices from 'deviceManager' source %s:%s", + "Including devices from 'deviceManager' source %s:%s (%s)", source.module, name, + summary, ) manager = getattr(mod, name) if not isinstance(manager, DeviceManager): From cda0734396d19d177e9360e2c91e4dd90f195420 Mon Sep 17 00:00:00 2001 From: Peter Holloway Date: Wed, 15 Apr 2026 11:02:34 +0100 Subject: [PATCH 2/2] Only report package versions once --- src/blueapi/core/context.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/blueapi/core/context.py b/src/blueapi/core/context.py index 83740290d..60df7a9d7 100644 --- a/src/blueapi/core/context.py +++ b/src/blueapi/core/context.py @@ -217,25 +217,23 @@ def find_device(self, addr: str | list[str]) -> Device | None: def with_config(self, config: EnvironmentConfig) -> None: if config.metadata is not None: self.run_engine.md |= config.metadata.model_dump() + package_map = metadata.packages_distributions() + packages = {src.module.split(".")[0] for src in config.sources} + for pkg in packages: + if root_pkg := package_map.get(pkg): + version = metadata.version(root_pkg[0]) + LOGGER.info("Using package %s[%s]", root_pkg[0], version) + for source in config.sources: mod = import_module(source.module) - if root_pkg := package_map.get(source.module.split(".")[0]): - version = metadata.version(root_pkg[0]) - loc = mod.__spec__ and mod.__spec__.origin - summary = f"{root_pkg[0]}[version: {version}, location: {loc}]" - else: - summary = "" - match source: case PlanSource(): - LOGGER.info("Including plans from %s (%s)", source.module, summary) + LOGGER.info("Including plans from %s", source.module) self.with_plan_module(mod) case DeviceSource(): - LOGGER.info( - "Including devices from %s (%s)", source.module, summary - ) + LOGGER.info("Including devices from %s", source.module) LOGGER.warning( "'devices' environment kind is deprecated - please convert " "configuration to use deviceManager" @@ -243,9 +241,7 @@ def with_config(self, config: EnvironmentConfig) -> None: self.with_device_module(mod) case DodalSource(mock=mock): LOGGER.info( - "Including devices from 'dodal' source %s (%s)", - source.module, - summary, + "Including devices from 'dodal' source %s", source.module ) LOGGER.warning( "'dodal' environment kind is deprecated - please convert " @@ -254,10 +250,9 @@ def with_config(self, config: EnvironmentConfig) -> None: self.with_dodal_module(mod, mock=mock) case DeviceManagerSource(mock=mock, name=name): LOGGER.info( - "Including devices from 'deviceManager' source %s:%s (%s)", + "Including devices from 'deviceManager' source %s:%s", source.module, name, - summary, ) manager = getattr(mod, name) if not isinstance(manager, DeviceManager):