You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
With bluesky/bluesky#2079 now merged, the plan stubs now have their typing improved. This means we should no longer need to rely on dodal to wrap plan stubs for strong typing, instead we should include the wrapped plan stubs from bluesky in blueapi by default and remove the duplicated wrapped plans in dodal.
This will do two things:
Reduce duplication, no longer need to maintain plan stubs in two different places.
Simplify configuration, we no longer need to specify plan stubs in services configuration, these should be included by default with blueapi.
As discussed, we first need Bluesky to have made a new release and then have blueapi use it. https://github.com/DiamondLightSource/blueapi/blob/main/src/blueapi/core/context.py#L219 is where the change will need to be done. So BlueskyContext.__post__init_ calls BlueskyContext.with_config which calls with_plan_modudle which calls register_plan. This is where plans are registered with blueapi from our plan repos and dodal wrapped plans as it loads in the module and extracts it out.
The dodal wrapped plan stubs only exist due to typing. What I am proposing is we include them directly with blueapi now and cut out the middle man as every single beamline will need the plan stubs.
The other solution is if you still want this driven by config, you instead create a module e.g
blueapi.plan_stubs.__init__.py and inside __all__ include all the default plan stubs listed above, then update the copier template to include
planSource:
blueapi.plan_stubs
Basically, the aim of this change is we don't need the wrapped plan stubs at all once the typing is up to scratch, so we eliminate the duplicate definition and have blueapi include it by default and doesn't need to include dodal at all for it.
We could go further and include plans, specifically scans, with this as well. However, they do need to wrapped to remove Callable per_step as these are not compatible with blueapi as not serialisable but there is an argument that this should go on the blueapi side instead as they are designed to work with blueapi specifically and again should be included by default. That is out of scope for now though, lets just focus on plan stubs
@oliwenmandiamond This is very helpful! Thanks for writing it out. I think I'll try with the first approach initially and then if that doesn't work, will try the second option of making a module and updating the copier template
One thing I do not know about is if blueapi can cope with plans using **kwargs as a lot of plan stubs use that in bluesky e.g allows you to specify timeout options or other bits a device might need. In dodal, there is an artificial blocker for plans and plan stubs to not use **kwargs. However, I don't think there would be anything stopping you actually doing it? The plan schema generated may not be as nice, but I don't think we should be putting artificial blockers when it is perfectly valid python and bluesky
Any untyped var-args, *args, **kwargs won't be able to be de-serialised, at best it may be possible to support *args: TypeA, **kwargs: TypeB, but without the typing information, Pydantic would never know to convert args=["stage.x", 0, 1, 4] into anything more meaningful.
Hey @DiamondJoseph, good to hear from you and thanks for responding!
So anything that is untyped has been fixed directly in bluesky with bluesky/bluesky#2083 and bluesky/bluesky#2079. Just waiting on a new release. I have tested this with blueapi #1663 (proof of concept) supporting args and kwargs, but I do have to provide additional information as the schema alone is not enough currently. So this does assume a couple of things and this should be probably be put in draft until those are decided on and merged, but in theory in the future wrapped plans in dodal will no longer be needed and blueapi can include directly bluesky plan stubs by default. More testing will be required though!
Activity
oliwenmandiamond commentedon Oct 8, 2026
@shree-iyengar-dls @ZohebShaikh @dan-fernandes
As discussed, we first need Bluesky to have made a new release and then have blueapi use it.
https://github.com/DiamondLightSource/blueapi/blob/main/src/blueapi/core/context.py#L219 is where the change will need to be done. So
BlueskyContext.__post__init_callsBlueskyContext.with_configwhich callswith_plan_modudlewhich callsregister_plan. This is where plans are registered with blueapi from our plan repos and dodal wrapped plans as it loads in the module and extracts it out.The dodal wrapped plan stubs only exist due to typing. What I am proposing is we include them directly with blueapi now and cut out the middle man as every single beamline will need the plan stubs.
then inside
__post__init_we callThe other solution is if you still want this driven by config, you instead create a module e.g
blueapi.plan_stubs.__init__.pyand inside__all__include all the default plan stubs listed above, then update the copier template to includeBasically, the aim of this change is we don't need the wrapped plan stubs at all once the typing is up to scratch, so we eliminate the duplicate definition and have blueapi include it by default and doesn't need to include dodal at all for it.
We could go further and include plans, specifically scans, with this as well. However, they do need to wrapped to remove Callable per_step as these are not compatible with blueapi as not serialisable but there is an argument that this should go on the blueapi side instead as they are designed to work with blueapi specifically and again should be included by default. That is out of scope for now though, lets just focus on plan stubs
shree-iyengar-dls commentedon Oct 8, 2026
@oliwenmandiamond This is very helpful! Thanks for writing it out. I think I'll try with the first approach initially and then if that doesn't work, will try the second option of making a module and updating the copier template
oliwenmandiamond commentedon Oct 8, 2026
One thing I do not know about is if blueapi can cope with plans using **kwargs as a lot of plan stubs use that in bluesky e.g allows you to specify timeout options or other bits a device might need. In dodal, there is an artificial blocker for plans and plan stubs to not use **kwargs. However, I don't think there would be anything stopping you actually doing it? The plan schema generated may not be as nice, but I don't think we should be putting artificial blockers when it is perfectly valid python and bluesky
DiamondJoseph commentedon Oct 19, 2026
Any untyped var-args,
*args, **kwargswon't be able to be de-serialised, at best it may be possible to support*args: TypeA, **kwargs: TypeB, but without the typing information, Pydantic would never know to convertargs=["stage.x", 0, 1, 4]into anything more meaningful.oliwenmandiamond commentedon Oct 19, 2026
Hey @DiamondJoseph, good to hear from you and thanks for responding!
So anything that is untyped has been fixed directly in bluesky with bluesky/bluesky#2083 and bluesky/bluesky#2079. Just waiting on a new release. I have tested this with blueapi #1663 (proof of concept) supporting args and kwargs, but I do have to provide additional information as the schema alone is not enough currently. So this does assume a couple of things and this should be probably be put in draft until those are decided on and merged, but in theory in the future wrapped plans in dodal will no longer be needed and blueapi can include directly bluesky plan stubs by default. More testing will be required though!