2626
2727from __future__ import annotations
2828
29+ import collections
2930import copy
3031import itertools
3132import os
3839from smartsim .entity .application import Application
3940from smartsim .entity .files import EntityFiles
4041from smartsim .launchable .job import Job
42+ from smartsim .settings .launch_settings import LaunchSettings
4143
4244if t .TYPE_CHECKING :
4345 from smartsim .settings .launch_settings import LaunchSettings
@@ -137,7 +139,7 @@ def __init__(
137139 copy .deepcopy (exe_arg_parameters ) if exe_arg_parameters else {}
138140 )
139141 """The parameters and values to be used when configuring entities"""
140- self ._files = copy .deepcopy (files ) if files else None
142+ self ._files = copy .deepcopy (files ) if files else EntityFiles ()
141143 """The files to be copied, symlinked, and/or configured prior to execution"""
142144 self ._file_parameters = (
143145 copy .deepcopy (file_parameters ) if file_parameters else {}
@@ -163,7 +165,11 @@ def exe(self, value: str | os.PathLike[str]) -> None:
163165 """Set the executable.
164166
165167 :param value: the executable
168+ :raises TypeError: if the exe argument is not str or PathLike str
166169 """
170+ if not isinstance (value , (str , os .PathLike )):
171+ raise TypeError ("exe argument was not of type str or PathLike str" )
172+
167173 self ._exe = os .fspath (value )
168174
169175 @property
@@ -179,7 +185,15 @@ def exe_args(self, value: t.Sequence[str]) -> None:
179185 """Set the executable arguments.
180186
181187 :param value: the executable arguments
188+ :raises TypeError: if exe_args is not sequence of str
182189 """
190+
191+ if not (
192+ isinstance (value , collections .abc .Sequence )
193+ and (all (isinstance (x , str ) for x in value ))
194+ ):
195+ raise TypeError ("exe_args argument was not of type sequence of str" )
196+
183197 self ._exe_args = list (value )
184198
185199 @property
@@ -197,11 +211,36 @@ def exe_arg_parameters(
197211 """Set the executable argument parameters.
198212
199213 :param value: the executable argument parameters
214+ :raises TypeError: if exe_arg_parameters is not mapping
215+ of str and sequences of sequences of strings
200216 """
217+
218+ if not (
219+ isinstance (value , collections .abc .Mapping )
220+ and (
221+ all (
222+ isinstance (key , str )
223+ and isinstance (val , collections .abc .Sequence )
224+ and all (
225+ isinstance (subval , collections .abc .Sequence ) for subval in val
226+ )
227+ and all (
228+ isinstance (item , str )
229+ for item in itertools .chain .from_iterable (val )
230+ )
231+ for key , val in value .items ()
232+ )
233+ )
234+ ):
235+ raise TypeError (
236+ "exe_arg_parameters argument was not of type "
237+ "mapping of str and sequences of sequences of strings"
238+ )
239+
201240 self ._exe_arg_parameters = copy .deepcopy (value )
202241
203242 @property
204- def files (self ) -> t . Union [ EntityFiles , None ] :
243+ def files (self ) -> EntityFiles :
205244 """Return attached EntityFiles object.
206245
207246 :return: the EntityFiles object of files to be copied, symlinked,
@@ -210,12 +249,16 @@ def files(self) -> t.Union[EntityFiles, None]:
210249 return self ._files
211250
212251 @files .setter
213- def files (self , value : t . Optional [ EntityFiles ] ) -> None :
252+ def files (self , value : EntityFiles ) -> None :
214253 """Set the EntityFiles object.
215254
216255 :param value: the EntityFiles object of files to be copied, symlinked,
217256 and/or configured prior to execution
257+ :raises TypeError: if files is not of type EntityFiles
218258 """
259+
260+ if not isinstance (value , EntityFiles ):
261+ raise TypeError ("files argument was not of type EntityFiles" )
219262 self ._files = copy .deepcopy (value )
220263
221264 @property
@@ -231,7 +274,26 @@ def file_parameters(self, value: t.Mapping[str, t.Sequence[str]]) -> None:
231274 """Set the file parameters.
232275
233276 :param value: the file parameters
277+ :raises TypeError: if file_parameters is not a mapping of str and
278+ sequence of str
234279 """
280+
281+ if not (
282+ isinstance (value , t .Mapping )
283+ and (
284+ all (
285+ isinstance (key , str )
286+ and isinstance (val , collections .abc .Sequence )
287+ and all (isinstance (subval , str ) for subval in val )
288+ for key , val in value .items ()
289+ )
290+ )
291+ ):
292+ raise TypeError (
293+ "file_parameters argument was not of type mapping of str "
294+ "and sequence of str"
295+ )
296+
235297 self ._file_parameters = dict (value )
236298
237299 @property
@@ -249,7 +311,15 @@ def permutation_strategy(
249311 """Set the permutation strategy
250312
251313 :param value: the permutation strategy
314+ :raises TypeError: if permutation_strategy is not str or
315+ PermutationStrategyType
252316 """
317+
318+ if not (callable (value ) or isinstance (value , str )):
319+ raise TypeError (
320+ "permutation_strategy argument was not of "
321+ "type str or PermutationStrategyType"
322+ )
253323 self ._permutation_strategy = value
254324
255325 @property
@@ -265,7 +335,11 @@ def max_permutations(self, value: int) -> None:
265335 """Set the maximum permutations
266336
267337 :param value: the max permutations
338+ :raises TypeError: max_permutations argument was not of type int
268339 """
340+ if not isinstance (value , int ):
341+ raise TypeError ("max_permutations argument was not of type int" )
342+
269343 self ._max_permutations = value
270344
271345 @property
@@ -281,7 +355,13 @@ def replicas(self, value: int) -> None:
281355 """Set the number of replicas.
282356
283357 :return: the number of replicas
358+ :raises TypeError: replicas argument was not of type int
284359 """
360+ if not isinstance (value , int ):
361+ raise TypeError ("replicas argument was not of type int" )
362+ if value <= 0 :
363+ raise ValueError ("Number of replicas must be a positive integer" )
364+
285365 self ._replicas = value
286366
287367 def _create_applications (self ) -> tuple [Application , ...]:
@@ -342,7 +422,11 @@ def build_jobs(self, settings: LaunchSettings) -> tuple[Job, ...]:
342422
343423 :param settings: LaunchSettings to apply to each Job
344424 :return: Sequence of Jobs with the provided LaunchSettings
425+ :raises TypeError: if the ids argument is not type LaunchSettings
426+ :raises ValueError: if the LaunchSettings provided are empty
345427 """
428+ if not isinstance (settings , LaunchSettings ):
429+ raise TypeError ("ids argument was not of type LaunchSettings" )
346430 apps = self ._create_applications ()
347431 if not apps :
348432 raise ValueError ("There are no members as part of this ensemble" )
0 commit comments