@@ -145,6 +145,92 @@ def load_as_level3_asn(self, obj):
145145 update_key_value (asn , "expname" , (), mod_func = self .make_input_path )
146146 return asn
147147
148+ def prepare_output (self , init , make_copy = None , open_models = True , ** kwargs ):
149+ """
150+ Open the input data as a model, making a copy if necessary.
151+
152+ If the input data is a filename or path, it is opened
153+ and the open model is returned.
154+
155+ If it is a list of models, it is opened as a ModelContainer.
156+ In this case, or if the input is a simple datamodel or a
157+ ModelContainer, a deep copy of the model/container is returned,
158+ in order to avoid modifying the input models.
159+
160+ If the input is a ModelLibrary, it is simply returned, in order
161+ to avoid making unnecessary copies for performance-critical
162+ use cases.
163+
164+ All copies are skipped if this step has a parent (i.e. it is
165+ called as part of a pipeline).
166+
167+ Set make_copy explicitly to True or False to override the above
168+ behavior.
169+
170+ Parameters
171+ ----------
172+ init : str, list, JwstDataModel, ModelContainer, or ModelLibrary
173+ Input data to open.
174+ make_copy : bool or None
175+ If True, a copy of the input will always be made.
176+ If False, a copy will never be made. If None, a copy is
177+ conditionally made, depending on the input and whether the
178+ step is called in a standalone context.
179+ open_models : bool
180+ If True and the input is a filename or list of filenames,
181+ then datamodels.open will be called to open the input.
182+ If False, the input is returned as is.
183+ **kwargs
184+ Additional keyword arguments to pass to datamodels.open. Used
185+ only if the input is a str or list.
186+
187+ Returns
188+ -------
189+ JwstDataModel, ModelContainer, or ModelLibrary
190+ The opened datamodel(s).
191+
192+ Raises
193+ ------
194+ TypeError
195+ If make_copy=True and the input is a type that cannot be copied.
196+ """
197+ # Check whether input contains datamodels
198+ copy_needed = False
199+ if isinstance (init , list ):
200+ is_datamodel = [isinstance (m , datamodels .JwstDataModel ) for m in init ]
201+ if any (is_datamodel ):
202+ # Make the list into a ModelContainer, since it contains models
203+ init = ModelContainer (init )
204+ copy_needed = True
205+ elif isinstance (init , (datamodels .JwstDataModel , ModelContainer )):
206+ copy_needed = True
207+
208+ # Input might be a filename or path.
209+ # In that case, open it if desired.
210+ if not isinstance (init , (datamodels .JwstDataModel , ModelLibrary , ModelContainer )):
211+ if open_models :
212+ input_models = datamodels .open (init , ** kwargs )
213+ else :
214+ input_models = init
215+ else :
216+ # Use the init model directly.
217+ input_models = init
218+
219+ # Make a copy if needed
220+ if make_copy is None :
221+ make_copy = copy_needed and self .parent is None
222+ if make_copy :
223+ try :
224+ input_models = input_models .copy ()
225+ except AttributeError :
226+ # This should only happen if make_copy is explicitly set to
227+ # True and the input is a string or a ModelLibrary.
228+ raise TypeError (
229+ f"Copy is not possible for input type { type (input_models )} "
230+ ) from None
231+
232+ return input_models
233+
148234 def finalize_result (self , result , reference_files_used ):
149235 """
150236 Update the result with the software version and reference files used.
0 commit comments