3232
3333from tabulate import tabulate
3434
35- from smartsim ._core ._cli .utils import color_bool , SMART_LOGGER_FORMAT
35+ from smartsim ._core ._cli .utils import SMART_LOGGER_FORMAT , color_bool , pip
3636from smartsim ._core ._install import builder
3737from smartsim ._core ._install .buildenv import (
3838 BuildEnv ,
39+ DbEngine ,
3940 SetupError ,
4041 Version_ ,
41- Versioner ,
42- DbEngine ,
4342 VersionConflictError ,
43+ Versioner ,
4444)
4545from smartsim ._core ._install .builder import BuildError
4646from smartsim ._core .config import CONFIG
@@ -244,16 +244,34 @@ def check_py_torch_version(versions: Versioner, device: _TDeviceStr = "cpu") ->
244244 else :
245245 raise BuildError ("Unrecognized device requested" )
246246
247- _check_packages_in_python_env (
248- {
249- "torch" : Version_ (f"{ versions .TORCH } { device_suffix } " ),
250- "torchvision" : Version_ (f"{ versions .TORCHVISION } { device_suffix } " ),
251- },
247+ torch_deps = {
248+ "torch" : Version_ (f"{ versions .TORCH } { device_suffix } " ),
249+ "torchvision" : Version_ (f"{ versions .TORCHVISION } { device_suffix } " ),
250+ }
251+ missing , conflicts = _assess_python_env (
252+ torch_deps ,
253+ package_pinning = "==" ,
252254 validate_installed_version = _create_torch_version_validator (
253255 with_suffix = device_suffix
254256 ),
255257 )
256258
259+ if len (missing ) == len (torch_deps ) and not conflicts :
260+ # All PyTorch deps are not installed and there are no conflicting
261+ # python packages. We can try to install torch deps into the current env.
262+ logger .info (
263+ "Torch version not found in python environment. "
264+ "Attempting to install via `pip`"
265+ )
266+ pip (
267+ "install" ,
268+ "-f" ,
269+ "https://download.pytorch.org/whl/torch_stable.html" ,
270+ * (f"{ package } =={ version } " for package , version in torch_deps .items ()),
271+ )
272+ elif missing or conflicts :
273+ logger .warning (_format_incompatible_python_env_message (missing , conflicts ))
274+
257275
258276def _create_torch_version_validator (
259277 with_suffix : str ,
@@ -297,20 +315,7 @@ def _check_packages_in_python_env(
297315 )
298316
299317 if missing or conflicts :
300- indent = "\n \t "
301- fmt_list : t .Callable [[str , t .List [str ]], str ] = (
302- lambda n , l : f"{ n } :{ indent } { indent .join (l )} " if l else ""
303- )
304- missing_str = fmt_list ("Missing" , missing )
305- conflict_str = fmt_list ("Conflicting" , conflicts )
306- sep = "\n " if missing_str and conflict_str else ""
307- logger .warning (
308- "Python Env Status Warning!\n "
309- "Requested Packages are Missing or Conflicting:\n \n "
310- f"{ missing_str } { sep } { conflict_str } "
311- "\n \n Consider installing packages at the requested versions via "
312- "`pip` or installing SmartSim with optional ML dependencies"
313- )
318+ logger .warning (_format_incompatible_python_env_message (missing , conflicts ))
314319
315320
316321def _assess_python_env (
@@ -334,6 +339,26 @@ def _assess_python_env(
334339 return missing , conflicts
335340
336341
342+ def _format_incompatible_python_env_message (
343+ missing : t .Iterable [str ], conflicting : t .Iterable [str ]
344+ ) -> str :
345+ indent = "\n \t "
346+ fmt_list : t .Callable [[str , t .Iterable [str ]], str ] = (
347+ lambda n , l : f"{ n } :{ indent } { indent .join (l )} " if l else ""
348+ )
349+ missing_str = fmt_list ("Missing" , missing )
350+ conflict_str = fmt_list ("Conflicting" , conflicting )
351+ sep = "\n " if missing_str and conflict_str else ""
352+ return (
353+ "Python Env Status Warning!\n "
354+ "Requested Packages are Missing or Conflicting:\n \n "
355+ f"{ missing_str } { sep } { conflict_str } \n \n "
356+ "Consider installing packages at the requested versions via `pip` or "
357+ "uninstalling them, installing SmartSim with optional ML dependencies "
358+ "(`pip install smartsim[ml]`), and running `smart clean && smart build ...`"
359+ )
360+
361+
337362def execute (args : argparse .Namespace ) -> int :
338363 verbose = args .v
339364 keydb = args .keydb
@@ -376,21 +401,22 @@ def execute(args: argparse.Namespace) -> int:
376401 print (tabulate (vers , headers = version_names , tablefmt = "github" ), "\n " )
377402
378403 try :
379- # REDIS/KeyDB
380- build_database (build_env , versions , keydb , verbose )
381-
382- # REDISAI
383- build_redis_ai (
384- build_env ,
385- versions ,
386- device ,
387- pt ,
388- tf ,
389- onnx ,
390- args .torch_dir ,
391- args .libtensorflow_dir ,
392- verbose = verbose ,
393- )
404+ if not args .only_python_packages :
405+ # REDIS/KeyDB
406+ build_database (build_env , versions , keydb , verbose )
407+
408+ # REDISAI
409+ build_redis_ai (
410+ build_env ,
411+ versions ,
412+ device ,
413+ pt ,
414+ tf ,
415+ onnx ,
416+ args .torch_dir ,
417+ args .libtensorflow_dir ,
418+ verbose = verbose ,
419+ )
394420 except (SetupError , BuildError ) as e :
395421 logger .error (str (e ))
396422 return 1
@@ -406,7 +432,7 @@ def execute(args: argparse.Namespace) -> int:
406432 check_py_tf_version (versions )
407433 if "onnxruntime" in backends :
408434 check_py_onnx_version (versions )
409- except SetupError as e :
435+ except ( SetupError , BuildError ) as e :
410436 logger .error (str (e ))
411437 return 1
412438
@@ -430,6 +456,12 @@ def configure_parser(parser: argparse.ArgumentParser) -> None:
430456 choices = ["cpu" , "gpu" ],
431457 help = "Device to build ML runtimes for" ,
432458 )
459+ parser .add_argument (
460+ "--only_python_packages" ,
461+ action = "store_true" ,
462+ default = False ,
463+ help = "Only evaluate the python packages (i.e. skip building backends)" ,
464+ )
433465 parser .add_argument (
434466 "--no_pt" ,
435467 action = "store_true" ,
0 commit comments