Skip to content

Breaking: Redesign structure to use plugin structure#165

Merged
oliwenmandiamond merged 35 commits intomainfrom
use_plugin_structure
Mar 12, 2026
Merged

Breaking: Redesign structure to use plugin structure#165
oliwenmandiamond merged 35 commits intomainfrom
use_plugin_structure

Conversation

@oliwenmandiamond
Copy link
Collaborator

@oliwenmandiamond oliwenmandiamond commented Mar 3, 2026

Fixes #164

Copy link
Collaborator

@jacob720 jacob720 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, the name plugins is a bit less clear and specific then the name models. I also think it's slightly confusing having lots of _converters.py files outside of the converters directory but maybe there's no way around this.

Also, are you against having the option of doing

from daq_config_server.models import MyModel

As we could presumably keep the other improvements to the module structure but also add models to daq_config_sever.models.__init__.py, allowing you to import them in whichever way you prefer.

import xmltodict

from daq_config_server.models.converters.beamline_parameters import (
from daq_config_server.core._base_model import ConfigModel
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid importing from private modules that are in different directories?
I think we should expose ConfigModel in daq_config_server.core.__init__.py

@olliesilvester
Copy link
Contributor

To me, the name plugins is a bit less clear and specific then the name models

I was going to comment the same thing

@oliwenmandiamond
Copy link
Collaborator Author

Yeah, I can rename back to models, no problem

Can we avoid importing from private modules that are in different directories? I think we should expose ConfigModel in daq_config_server.core.__init__.py

Yeah, could maybe even just put it at the top of daq_config_server.models

Also, are you against having the option of doing

from daq_config_server.models import MyModel

As we could presumably keep the other improvements to the module structure but also add models to daq_config_sever.models.__init__.py, allowing you to import them in whichever way you prefer.

The point is NOT to share the same namespace as other models / plugins / whatever. They should be isolated and not depend on one another / have visibility of each other.

Otherwise, what's the point of the separate model structure if we then force them into the same namespace anyway? We might as well just put everything into a single massive file

@jacob720
Copy link
Collaborator

jacob720 commented Mar 3, 2026

Personally I don't think the import structure has to exactly reflect the internal file structure.

Otherwise, what's the point of the separate model structure if we then force them into the same namespace anyway? We might as well just put everything into a single massive file

This would make it horrible for developers for work on. The internal file structure to me is a separate decision to how we want to design the public interface. I have a preference for a fairly flat import structure and a more organised file structure, but not a strong one so happy to be overuled

@oliwenmandiamond
Copy link
Collaborator Author

Otherwise, what's the point of the separate model structure if we then force them into the same namespace anyway? We might as well just put everything into a single massive file

This would make it horrible for developers for work on.

Exactly! This is the core issue. You are trying to use both structures, so you remove the advantages of one way and then give it all the disadvantages of the other. One needs to be picked! Now you are indirectly causing them all share the the same namespace which can easily cause confusion and name conflicts.

For example, to make a comparison, lets take dodal.devices.beamlines. Each beamline has folder for it's beamline specific devices. Now lets apply whats been done to the daq-config-server to it

Instead of using from dodal.devices.beamlines.iXX import MyDevice, I now do from dodal.devices.beamlines import MyDevice. What on earth is MyDevice? What beamline is it from? Not only that, but now if any other beamline also wants to name their device class the same thing as another because they are in their own namespace and they need a slightly more specific thing and is independent of every other beamline and has no visibility of others, it can name it the same thing but then causes issues in the init.py file because of the name conflicts. If it was in the same file, the name conflict would be visible to the developer straight away rather than indirectly. Not only this, you also now have to maintain the imports in two places rather one, it is a maintenance nightmare!

As I mentioned previously, it may not seem like an issue right now because only really MX are using it. But if me and many other developers want to also use this service, this will quickly get out of hand.

@olliesilvester
Copy link
Contributor

olliesilvester commented Mar 4, 2026

I think you're both actually almost on the same page here, we all agree that something like
from daq_config_server.models import MyModel
would be confusing in general. I would suggest:

  • Import different model modules into daq_config_server.models, so then for example your code using the library would do from daq_config_server.models import ixx_models
  • Also import generically useful converters, eg from daq_config_server.models import StandardLutConverter.

@oliwenmandiamond
Copy link
Collaborator Author

I think you're both actually almost on the same page here, we all agree that something like from daq_config_server.models import MyModel would be confusing in general. I would suggest:

* Import different model modules into  `daq_config_server.models`, so then for example your code using the library would do `from daq_config_server.models import ixx_models`

* Also import generically useful converters, eg `from daq_config_server.models import StandardLutConverter`.

Yes, having core / common models at the top level is perfectly fine, as long as that is where it actually lives. So it would look like this

models
     __init__.py
     _common_model.py
     _common_converter.py

     specific_thing1
          __init__.py
          _model.py
          _converter.py

     specific_thing2
          __init__.py
          _model.py
          _converter.py

imports are now

from daq_config_server.models import MyCommonModel
from daq_config_server.models.specific_thing1 import MySpecificModel
from daq_config_server.models.specific_thing2 import MySpecificModel

So common things from a quick glance would be GenericLookupTable, LookupTableBase

@oliwenmandiamond
Copy link
Collaborator Author

@olliesilvester @jacob720 I think this is ready for review now. Apologies on size of change.

The main clean up I've done is:

  • Clearly define packages app, models, converters, and testing. These are broken down further into their specific use cases so everything is well organised and makes sense navigating.
  • Models package focuses on models. Models now implement a from_contents class method. This simplifies logic and couples the loader with the model, making code easier to read and navigate. Where this isn't possible, the converter/loader function lives with the models as it is to do with loading a model.
  • Models are also reorganised, separating out ones which are generic and more specialised.
  • Tests reflect this new structure.

I will raise a separate issue so that external repositories can only import what they need, which is ConfigServer and the models package.

@olliesilvester olliesilvester changed the title Redesign structure to use plugin structure Breaking: Redesign structure to use plugin structure Mar 5, 2026
@oliwenmandiamond
Copy link
Collaborator Author

I'm also happy to go around all the external repos and apply the breaking change to them so it all rolls out smoothly

Copy link
Contributor

@olliesilvester olliesilvester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the structure is nicer I think. I haven't gone through all the tests and logic just yet

from ._converter_utils import ConverterParseError
import daq_config_server.converters._file_converter_map as file_converter_map
from daq_config_server.converters._converter_utils import ConverterParseError
from daq_config_server.models._base_model import ConfigModel


def get_converted_file_contents(file_path: Path) -> dict[str, Any]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should: expose get_converted_file_contents in converters/__init__.py, so that other files don't have to import from this private file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting something in __all__ signals that it is part of the public API and intended for external use. It is and should only be used inside this package I believe so doesn't make sense to.

Copy link
Contributor

@olliesilvester olliesilvester Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're maybe mixing up what's publicly available to those using daq-config-server as a library, and public/private parts internal to the repo

Since get_converted_file_contents is defined in the converters module but is used by the app module, we should indicate that get_converted_file_contents is okay to use in other modules. Same goes for my other comments

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could go even further and make another module that exposes the external API in a neater way. Maybe for a separate issue though

Copy link
Collaborator Author

@oliwenmandiamond oliwenmandiamond left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't updated any logic or tests, only structure (except when the tests where failing due to state persisting)

from ._converter_utils import ConverterParseError
import daq_config_server.converters._file_converter_map as file_converter_map
from daq_config_server.converters._converter_utils import ConverterParseError
from daq_config_server.models._base_model import ConfigModel


def get_converted_file_contents(file_path: Path) -> dict[str, Any]:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting something in __all__ signals that it is part of the public API and intended for external use. It is and should only be used inside this package I believe so doesn't make sense to.

Copy link
Contributor

@olliesilvester olliesilvester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'm happy with this. It'll probably become obvious if the structure is sensible as we start using all these bits a bit more

Copy link

@fajinyuan fajinyuan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please see my comments so far.

@oliwenmandiamond
Copy link
Collaborator Author

@jacob720 you good with me merging this now?

@jacob720
Copy link
Collaborator

Yeah looks good!

@oliwenmandiamond oliwenmandiamond merged commit 6df7251 into main Mar 12, 2026
15 checks passed
@oliwenmandiamond oliwenmandiamond deleted the use_plugin_structure branch March 12, 2026 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discussion on repo structure

4 participants