99import tarfile
1010from pathlib import Path , PurePath
1111import tempfile
12- from colors import *
12+ from colors import yellow , red , green , cyan , color
1313from shutil import which
1414from packaging .version import parse
1515import numpy
@@ -369,7 +369,8 @@ def subst_env_for_path(path:str)->str:
369369 The possibly updated path.
370370 """
371371
372- if opts ['verbose' ] is True : return path
372+ if opts ['verbose' ] is True :
373+ return path
373374
374375 for testvar in ['TMPDIR' , 'TMP_DIR' , 'TEMP_DIR' , 'CONDA_PREFIX' , 'VIRTUAL_ENV' ]:
375376 if testvar in os .environ and re .match (os .environ [testvar ], path ) is not None :
@@ -500,7 +501,7 @@ def pip_install(pip_install_args, pkg_desc='packages'):
500501 run_cmd (cmd_list )
501502 note_ok ()
502503
503- def install_conda_pkg (pkg_name :str ):
504+ def install_conda_pkg (pkg_name :str , version : str = None ):
504505 """
505506 Shorthand for performing a 'conda install' operation for a single package.
506507
@@ -510,6 +511,8 @@ def install_conda_pkg(pkg_name:str):
510511 The name of the package to install.
511512 """
512513 note (f'Installing { pkg_name .upper ()} with conda' )
514+ if version is not None :
515+ pkg_name += f'={ version } '
513516 install_args = ['install' , '-q' , '-y' , pkg_name ]
514517 run_conda_cmd (cmd_args = install_args )
515518 note_ok ()
@@ -707,7 +710,7 @@ def install_mumps_from_src():
707710 if not allow_build ('mumps' ):
708711 return
709712
710- build_dir = git_clone ('mumps' )
713+ git_clone ('mumps' )
711714 run_cmd (['./get.Mumps' ])
712715
713716 cnf_cmd_list = get_common_solver_config_cmd ()
@@ -723,7 +726,7 @@ def install_paropt_from_src():
723726 """
724727 Git clone the PAROPT repo, build the library, and install it and the include files.
725728 """
726- build_dir = git_clone ('paropt' )
729+ git_clone ('paropt' )
727730
728731 # Use build defaults as per ParOpt instructions:
729732 Path ('Makefile.in.info' ).rename ('Makefile.in' )
@@ -763,13 +766,16 @@ def install_ipopt_from_src(config_opts:list=None):
763766 if not allow_build ('ipopt' ) or opts ['include_ipopt' ] is False :
764767 return
765768
766- build_dir = git_clone ('ipopt' )
769+ git_clone ('ipopt' )
767770 cnf_cmd_list = ['./configure' , f'--prefix={ opts ["prefix" ]} ' , '--disable-java' ]
768771
769772 # Don't accidentally use PARDISO if it wasn't selected:
770- if opts ['linear_solver' ] != 'pardiso' : cnf_cmd_list .append ('--disable-pardisomkl' )
773+ if opts ['linear_solver' ] != 'pardiso' :
774+ cnf_cmd_list .append ('--disable-pardisomkl' )
775+
776+ if config_opts is not None :
777+ cnf_cmd_list .extend (config_opts )
771778
772- if config_opts is not None : cnf_cmd_list .extend (config_opts )
773779 note ("Running configure" )
774780 run_cmd (cmd_list = cnf_cmd_list )
775781 note_ok ()
@@ -841,7 +847,7 @@ def install_hsl_from_src():
841847 if not allow_build ('hsl' ):
842848 return
843849
844- build_dir = git_clone ('hsl' )
850+ git_clone ('hsl' )
845851
846852 # Extract the HSL tar file and rename the folder to 'coinhsl'
847853 # First, determine the name of the top-level folder:
@@ -982,7 +988,7 @@ def uninstall_built_item(build_key:str):
982988
983989 try :
984990 inc_dir .rmdir ()
985- except :
991+ except Exception :
986992 pass
987993
988994 note_ok ()
@@ -1021,10 +1027,13 @@ def remove_conda_scripts():
10211027 if conda_is_active () and opts ['ignore_conda' ] is False :
10221028 note ("Removing conda activate/deactivate scripts" )
10231029 act_path = Path (sys_info ['conda_activate_dir' ]) / sys_info ['conda_env_script' ]
1024- if act_path .is_file (): act_path .unlink ()
1030+ if act_path .is_file ():
1031+ act_path .unlink ()
10251032
10261033 deact_path = Path (sys_info ['conda_deactivate_dir' ]) / sys_info ['conda_env_script' ]
1027- if deact_path .is_file (): deact_path .unlink ()
1034+ if deact_path .is_file ():
1035+ deact_path .unlink ()
1036+
10281037 note_ok ()
10291038
10301039def uninstall_built ():
@@ -1034,7 +1043,8 @@ def uninstall_built():
10341043 for build_key in ['ipopt' , 'hsl' , 'mumps' , 'metis' ]:
10351044 uninstall_built_item (build_key )
10361045
1037- if opts ['ignore_conda' ] is False : remove_conda_scripts ()
1046+ if opts ['ignore_conda' ] is False :
1047+ remove_conda_scripts ()
10381048
10391049def uninstall_conda_pkgs ():
10401050 """ Attempt to remove packages previously installed by conda. """
@@ -1105,7 +1115,7 @@ def check_compiler_sanity():
11051115 note_ok ()
11061116
11071117 if opts ['include_paropt' ]:
1108- note (f 'Testing mpicxx' )
1118+ note ('Testing mpicxx' )
11091119 run_cmd (cmd_list = ['mpicxx' , '-o' , 'hello_cxx_mpi' , 'hello.cc' ])
11101120 run_cmd (cmd_list = ['./hello_cxx_mpi' ])
11111121 note_ok ()
@@ -1232,7 +1242,8 @@ def finish_setup():
12321242
12331243 # Set an option with the parsed pyOptSparse version
12341244 pos_ver_str = build_info ['pyoptsparse' ]['branch' ]
1235- if pos_ver_str [:1 ] == 'v' : pos_ver_str = pos_ver_str [1 :] # Drop the initial v
1245+ if pos_ver_str [:1 ] == 'v' :
1246+ pos_ver_str = pos_ver_str [1 :] # Drop the initial v
12361247 opts ['pyoptsparse_version' ] = parse (pos_ver_str )
12371248
12381249 # Change snopt_dir to an absolute path
@@ -1325,22 +1336,68 @@ def post_build_success():
13251336 announce ('SUCCESS!' )
13261337 exit (0 )
13271338
1339+
1340+ def get_package_info (pkgname :str ) -> dict :
1341+ info = {
1342+ 'installed' : False ,
1343+ 'version' : None ,
1344+ 'origin' : None # 'conda-forge', 'pypi', or 'unknown'
1345+ }
1346+ try :
1347+ result = subprocess .run (['conda' , 'list' , pkgname ], capture_output = True , text = True , check = True )
1348+ lines = result .stdout .splitlines ()
1349+ for line in lines :
1350+ if line .startswith (f'{ pkgname } ' ):
1351+ parts = line .split ()
1352+ info ['installed' ] = True
1353+ info ['version' ] = parts [1 ]
1354+ info ['origin' ] = parts [- 1 ] # channel name, e.g. 'conda-forge'
1355+ break
1356+ except Exception :
1357+ pass
1358+ return info
1359+
1360+
13281361def perform_install ():
13291362 """ Initiate all the required actions in the script. """
1363+
13301364 process_command_line ()
13311365 initialize ()
13321366
13331367 if opts ['uninstall' ]:
13341368 announce ('Uninstalling pyOptSparse and related packages' )
13351369 print (f'{ yellow ("NOTE:" )} Some items may be listed even if not installed.' )
1336- if opts ['ignore_conda' ] is False : uninstall_conda_pkgs ()
1370+ if opts ['ignore_conda' ] is False :
1371+ uninstall_conda_pkgs ()
13371372 uninstall_built ()
13381373 exit (0 )
13391374
13401375 finish_setup ()
13411376
13421377 announce ('Beginning installation' )
13431378
1379+ # if using conda, we want numpy and scipy to be installed from conda-forge
1380+ if allow_install_with_conda ():
1381+ numpy_info = get_package_info ('numpy' )
1382+ if numpy_info ['installed' ] is False or numpy_info ['origin' ] != 'conda-forge' :
1383+ numpy_version = numpy_info ['version' ]
1384+ if numpy_version is not None :
1385+ print (f"{ yellow ('NOTE:' )} NumPy { numpy_version } is not installed from conda-forge, reinstalling it now." )
1386+ install_conda_pkg ('numpy' , version = numpy_version )
1387+ else :
1388+ print (f"{ yellow ('NOTE:' )} NumPy is not installed from conda-forge, installing it now." )
1389+ install_conda_pkg ('numpy' )
1390+
1391+ scipy_info = get_package_info ('scipy' )
1392+ if scipy_info ['installed' ] is False or scipy_info ['origin' ] != 'conda-forge' :
1393+ scipy_version = scipy_info ['version' ]
1394+ if scipy_version is not None :
1395+ print (f"{ yellow ('NOTE:' )} Scipy { scipy_version } is not installed from conda-forge, reinstalling it now." )
1396+ install_conda_pkg ('scipy' , version = scipy_version )
1397+ else :
1398+ print (f"{ yellow ('NOTE:' )} Scipy is not installed from conda-forge, installing it now." )
1399+ install_conda_pkg ('scipy' )
1400+
13441401 if opts ['linear_solver' ] == 'mumps' :
13451402 install_with_mumps ()
13461403 install_pyoptsparse_from_src ()
0 commit comments