At the first glance here are my observations and suggestions of pymetis architecture:
- The core runtime is built around
MetisRecipe+MetisRecipeImpl, with dynamic promotion and class introspection used to assemble inputs, products, and QC metadata. Seesrc/pymetis/classes/recipes/recipe.pyandsrc/pymetis/classes/recipes/impl.py. - Domain types (
DataItemfor example) are tightly coupled to CPL and pyesorex APIs. - Recipes are heavy consumers that depend on
classes,dataitems,qc,utils, and sometimesfunctions, which indicates high coupling.
- The
classesanddataitemspackages depend on each other, which can complicate refactors and tests. - Coupling to CPL/pyesorex inside domain classes reduces testability and blocks reuse of logic outside the CPL runtime.
dataitemsdepend onclasses
src/pymetis/dataitems/gainmap.pyimports fromclasses:from pymetis.classes.dataitems import ImageDataItemfrom pymetis.classes.mixins import Detector2rgMixin, DetectorGeoMixin, DetectorIfuMixin, DetectorSpecificMixin
classesdepend ondataitems
src/pymetis/classes/inputs/common.pyimports fromdataitems:from pymetis.dataitems.gainmap import GainMapfrom pymetis.dataitems.badpixmap import BadPixMapfrom pymetis.dataitems.masterflat import MasterFlat- (and several others)
The core framework layer classes depends on concrete dataitems definitions, while dataitems depend back on core mixins and base classes in classes. This locks the two packages together and makes it harder to refactor, swap in a different backend, or data model because you can’t isolate the “core” from the “domain types”.
DataItemimports and uses CPL directly:import cplfrom cpl.core import Msg, Image, Table, PropertyList, Property- It stores CPL-specific metadata like
_frame_group,_frame_level,_frame_type. - Its
__init__signature expects acpl.core.PropertyListandHduobjects, which themselves wrap CPL objects.
This reduces testability unit tests on DataItem behavior cannot be performed without a CPL runtime available. Even simple tests of metadata formatting require CPL object creation (e.g., cpl.core.PropertyList()). Mocking CPL is possible but cumbersome because the class imports and calls CPL types directly.
- Create a new domain layer (e. g.
pymetis.core) with pure-Python models and metadata (DataItemspec,Inputspec,QCspec) that has minimal cpl.core dependency. - Keep recipe orchestration in
pymetis.recipesbut make it consume only the core interfaces + adapters. - Wherever possible keep CPL/pyesorex specifics in a separate layer that turns core objects into CPL frames, tables, and images.
- Make sure
classesanddataitemsdo not depend on each other, even by moving shared mixin and format utilities into a true core module. Example:Parametrizableand formatting helpers currently live inclassesandutils, but are used across domain types. The benefit is fewer cycles and clearer dependency direction.
- Introduced a new core/ layer (core/dataitems, core/mixins, core/format.py, core/param.py).
- Moved mixins out of classes/ into core/.
- Moved prefab implementations from classes/prefab/* to recipes/prefab/*.
- Moved input “common” into recipes.
- Rewrote imports.
Many LM/N recipe files are near-identical and differ only in band/detector mixins and a couple of strings. Examples: recipes/lm_img/metis_lm_img_calibrate.py vs recipes/n_img/metis_n_img_calibrate.py, and the recipes within recipes/lm_lss/ vs recipes/n_lss/.
Introduce a small factory/helper (e.g., pymetis/recipes/prefab/recipe_factory.py) that generates the Impl + MetisRecipe classes from band + detector + metadata.
Many recipes define an InputSet class that only mixes in band/detector traits and inherits from a prefab InputSet, e.g. class InputSet(BandNMixin, DetectorGeoMixin, MetisImgCalibrateImpl.InputSet): pass.
Create base InputSet classes per family (IMG/LSS/IFU) and per detector type in a shared module and reuse them.
Some recipes (especially recipes/metis_det_dark.py, recipes/metis_det_lingain.py, recipes/cal/metis_cal_chophome.py, recipes/ifu/metis_ifu_rsrf.py) include heavy implementation logic in the recipe file itself.
Move algorithmic code into pymetis/recipes/prefab and keep recipe modules simple.
Multiple recipes (LM/N recipe pairs for example) repeat the same stacking.method parameter block.
Define shared parameter builders and shared parameter sets per recipe family.
There are clear unused imports (e.g., BandLmMixin imported in recipes/n_img/metis_n_img_flat.py but not used). These are spread across multiple recipe files.
Eliminate unused imports and keep the recipe modules clean.