66
66
get_conda_root ,
67
67
parse_explicit_url_conda ,
68
68
parse_explicit_url_pip ,
69
+ plural_marker ,
69
70
)
70
71
71
72
from .env_descr import (
@@ -114,7 +115,9 @@ def _modified_logger(*args: Any, **kwargs: Any):
114
115
self ._use_conda_lock_to_resolve = (
115
116
CONDA_PREFERRED_RESOLVER == "conda-lock"
116
117
) # type: bool
117
- self ._find_conda_binary ()
118
+ self ._found_binaries = False # We delay resolving binaries until we need them
119
+ # because in the remote case, in conda_environment.py
120
+ # we create this object but don't use it.
118
121
119
122
# Figure out what environments we know about locally
120
123
self ._local_root = LocalStorage .get_datastore_root_from_config (
@@ -136,6 +139,8 @@ def _modified_logger(*args: Any, **kwargs: Any):
136
139
self ._storage = None
137
140
138
141
def binary (self , binary : str ) -> Optional [str ]:
142
+ if not self ._found_binaries :
143
+ self ._find_conda_binary ()
139
144
if self ._bins :
140
145
return self ._bins .get (binary )
141
146
return None
@@ -153,6 +158,9 @@ def resolve(
153
158
# is a "lighter" conda.
154
159
raise CondaException ("Cannot resolve environments in a remote environment" )
155
160
161
+ if not self ._found_binaries :
162
+ self ._find_conda_binary ()
163
+
156
164
have_pip_deps = any ([d .category == "pip" for d in deps ])
157
165
if have_pip_deps and not self ._use_conda_lock_to_resolve :
158
166
raise CondaException (
@@ -189,6 +197,9 @@ def add_to_resolved_env(
189
197
# is a "lighter" conda.
190
198
raise CondaException ("Cannot resolve environments in a remote environment" )
191
199
200
+ if not self ._found_binaries :
201
+ self ._find_conda_binary ()
202
+
192
203
have_pip_deps = any (
193
204
chain (
194
205
[p .TYPE == "pip" for p in cur_env .packages ],
@@ -252,6 +263,10 @@ def create_for_step(
252
263
env : ResolvedEnvironment ,
253
264
do_symlink : bool = False ,
254
265
):
266
+
267
+ if not self ._found_binaries :
268
+ self ._find_conda_binary ()
269
+
255
270
try :
256
271
# I am not 100% sure the lock is required but since the environments share
257
272
# a common package cache, we will keep it for now
@@ -263,6 +278,10 @@ def create_for_step(
263
278
def create_for_name (
264
279
self , name : str , env : ResolvedEnvironment , do_symlink : bool = False
265
280
):
281
+
282
+ if not self ._found_binaries :
283
+ self ._find_conda_binary ()
284
+
266
285
with CondaLock (self ._env_lock_file (name )):
267
286
self ._create (env , name )
268
287
if do_symlink :
@@ -272,6 +291,9 @@ def create_for_name(
272
291
273
292
def remove_for_step (self , step_name : str , env_id : EnvID ):
274
293
# Remove the conda environment
294
+ if not self ._found_binaries :
295
+ self ._find_conda_binary ()
296
+
275
297
try :
276
298
env_name = self ._env_directory_from_envid (env_id )
277
299
return self .remove_for_name (env_name )
@@ -280,11 +302,15 @@ def remove_for_step(self, step_name: str, env_id: EnvID):
280
302
raise CondaStepException (e , [step_name ])
281
303
282
304
def remove_for_name (self , name : str ):
305
+ if not self ._found_binaries :
306
+ self ._find_conda_binary ()
283
307
with CondaLock (self ._env_lock_file (name )):
284
308
self ._remove (name )
285
309
286
310
def python (self , env_desc : Union [EnvID , str ]) -> Optional [str ]:
287
311
# Get Python interpreter for the conda environment
312
+ if not self ._found_binaries :
313
+ self ._find_conda_binary ()
288
314
env_path = None
289
315
if isinstance (env_desc , EnvID ):
290
316
env_path = self .created_environment (env_desc )
@@ -305,6 +331,9 @@ def python(self, env_desc: Union[EnvID, str]) -> Optional[str]:
305
331
def created_environment (
306
332
self , env_desc : Union [EnvID , str ]
307
333
) -> Optional [Tuple [EnvID , str ]]:
334
+ if not self ._found_binaries :
335
+ self ._find_conda_binary ()
336
+
308
337
if isinstance (env_desc , EnvID ):
309
338
prefix = "metaflow_%s_%s" % (env_desc .req_id , env_desc .full_id )
310
339
else :
@@ -320,6 +349,8 @@ def created_environments(
320
349
# List all existing metaflow environments; this can include environments that
321
350
# were created with the `environment` command and therefore have a different
322
351
# name
352
+ if not self ._found_binaries :
353
+ self ._find_conda_binary ()
323
354
prefix = "metaflow_%s_" % req_id if req_id else ""
324
355
return {
325
356
k : v
@@ -400,6 +431,8 @@ def cache_environments(
400
431
raise CondaException (
401
432
"Cannot cache environments since no datastore configured"
402
433
)
434
+ if not self ._found_binaries :
435
+ self ._find_conda_binary ()
403
436
404
437
cache_paths_to_check = [] # type: List[Tuple[str, str, str]]
405
438
my_arch_id = arch_id ()
@@ -633,12 +666,19 @@ def _cache_pkg(pkg: PackageSpecification, pkg_fmt: str, local_path: str) -> str:
633
666
if upload_files :
634
667
start = time .time ()
635
668
self ._echo (
636
- " Caching %d items to %s ..."
637
- % (len (upload_files ), self ._datastore_type ),
669
+ " Caching %d item%s to %s ..."
670
+ % (
671
+ len (upload_files ),
672
+ plural_marker (len (upload_files )),
673
+ self ._datastore_type ,
674
+ ),
638
675
nl = False ,
639
676
)
640
677
self ._upload_to_ds (upload_files )
641
- self ._echo (" done in %d seconds." % int (time .time () - start ))
678
+ delta_time = int (time .time () - start )
679
+ self ._echo (
680
+ " done in %d second%s." % (delta_time , plural_marker (delta_time ))
681
+ )
642
682
643
683
# If this is successful, we cache the environments. We do this *after*
644
684
# in case some packages fail to upload so we don't write corrupt
@@ -656,12 +696,19 @@ def _cache_pkg(pkg: PackageSpecification, pkg_fmt: str, local_path: str) -> str:
656
696
)
657
697
start = time .time ()
658
698
self ._echo (
659
- " Caching %d environments to %s ..."
660
- % (len (upload_files ), self ._datastore_type ),
699
+ " Caching %d environment%s to %s ..."
700
+ % (
701
+ len (upload_files ),
702
+ plural_marker (len (upload_files )),
703
+ self ._datastore_type ,
704
+ ),
661
705
nl = False ,
662
706
)
663
707
self ._upload_to_ds (upload_files )
664
- self ._echo (" done in %d seconds." % int (time .time () - start ))
708
+ delta_time = int (time .time () - start )
709
+ self ._echo (
710
+ " done in %d second%s." % (delta_time , plural_marker (delta_time ))
711
+ )
665
712
else :
666
713
self ._echo (" All items already cached in %s." % self ._datastore_type )
667
714
@@ -691,6 +738,9 @@ def lazy_fetch_packages(
691
738
# transmutation also happens if the requested format is not found (only for
692
739
# conda packages))
693
740
741
+ if not self ._found_binaries :
742
+ self ._find_conda_binary ()
743
+
694
744
if require_conda_format is None :
695
745
require_conda_format = []
696
746
use_package_dirs = True
@@ -961,8 +1011,12 @@ def _micromamba_transmute(src_file: str, dst_file: str, dst_format: str):
961
1011
if do_download :
962
1012
start = time .time ()
963
1013
self ._echo (
964
- " Downloading %d(web) + %d(cache) packages ..."
965
- % (len (web_downloads ), len (cache_downloads )),
1014
+ " Downloading %d(web) + %d(cache) package%s ..."
1015
+ % (
1016
+ len (web_downloads ),
1017
+ len (cache_downloads ),
1018
+ plural_marker (len (web_downloads ) + len (cache_downloads )),
1019
+ ),
966
1020
nl = False ,
967
1021
)
968
1022
@@ -1041,12 +1095,18 @@ def _micromamba_transmute(src_file: str, dst_file: str, dst_format: str):
1041
1095
)
1042
1096
1043
1097
if do_download :
1098
+ delta_time = int (time .time () - start )
1044
1099
self ._echo (
1045
- " done in %d seconds." % int (time .time () - start ), timestamp = False
1100
+ " done in %d second%s." % (delta_time , plural_marker (delta_time )),
1101
+ timestamp = False ,
1046
1102
)
1047
1103
if not pending_errors and transmutes :
1048
1104
start = time .time ()
1049
- self ._echo (" Transmuting %d packages ..." % len (transmutes ), nl = False )
1105
+ self ._echo (
1106
+ " Transmuting %d package%s ..."
1107
+ % (len (transmutes ), plural_marker (len (transmutes ))),
1108
+ nl = False ,
1109
+ )
1050
1110
with ThreadPoolExecutor (max_workers = os .cpu_count ()) as executor :
1051
1111
transmut_results = [
1052
1112
executor .submit (_transmute , entry ) for entry in transmutes
@@ -1064,8 +1124,10 @@ def _micromamba_transmute(src_file: str, dst_file: str, dst_format: str):
1064
1124
1065
1125
if new_url not in known_urls :
1066
1126
url_adds .append (new_url )
1127
+ delta_time = int (time .time () - start )
1067
1128
self ._echo (
1068
- " done in %d seconds." % int (time .time () - start ), timestamp = False
1129
+ " done in %d second%s." % (delta_time , plural_marker (delta_time )),
1130
+ timestamp = False ,
1069
1131
)
1070
1132
if url_adds :
1071
1133
# Update the urls file in the packages directory so that Conda knows that the
@@ -1387,6 +1449,7 @@ def _find_conda_binary(self):
1387
1449
err = self ._validate_conda_installation ()
1388
1450
if err :
1389
1451
raise err
1452
+ self ._found_binaries = True
1390
1453
1391
1454
def _ensure_local_conda (self ):
1392
1455
if CONDA_LOCAL_PATH is not None :
@@ -1458,7 +1521,11 @@ def _install_local_conda(self):
1458
1521
raise InvalidEnvironmentException (
1459
1522
msg = "Could not extract environment: %s" % str (e )
1460
1523
)
1461
- self ._echo (" done in %d seconds." % int (time .time () - start ), timestamp = False )
1524
+ delta_time = int (time .time () - start )
1525
+ self ._echo (
1526
+ " done in %d second%s." % (delta_time , plural_marker (delta_time )),
1527
+ timestamp = False ,
1528
+ )
1462
1529
1463
1530
def _ensure_remote_conda (self ):
1464
1531
if CONDA_REMOTE_INSTALLER is not None :
@@ -1467,14 +1534,17 @@ def _ensure_remote_conda(self):
1467
1534
# If we don't have a REMOTE_INSTALLER, we check if we need to install one
1468
1535
args = [
1469
1536
"/bin/bash" ,
1470
- "-c"
1471
- "if ! type %s >/dev/null 2>&1; "
1472
- "then wget --no-check-certificate "
1473
- "https://micro.mamba.pm/install.sh -O micromamba.sh >/dev/null 2>&1; "
1474
- "./micromamba.sh >/dev/null 2>&1; echo $HOME/.local/bin/micromamba; "
1475
- "else which %s; fi" % ("micromamba" , "micromamba" ),
1537
+ "-c" ,
1538
+ "if ! type micromamba >/dev/null 2>&1; then "
1539
+ "mkdir -p ~/.local/bin >/dev/null 2>&1; "
1540
+ "curl -Ls https://micro.mamba.pm/api/micromamba/%s/latest | "
1541
+ "tar -xvj -C ~/.local/bin/ --strip-components=1 bin/micromamba >/dev/null 2>&1; "
1542
+ "echo $HOME/.local/bin/micromamba; "
1543
+ "else which micromamba; fi" % arch_id (),
1476
1544
]
1477
- self ._bins = {"micromamba" : subprocess .check_output (args ).decode ("utf-8" )}
1545
+ self ._bins = {
1546
+ "conda" : subprocess .check_output (args ).decode ("utf-8" ).strip ()
1547
+ }
1478
1548
1479
1549
def _install_remote_conda (self ):
1480
1550
from metaflow .plugins import DATASTORES
@@ -1541,7 +1611,8 @@ def _validate_conda_installation(self) -> Optional[Exception]:
1541
1611
to_remove .append (k )
1542
1612
else :
1543
1613
return InvalidEnvironmentException (
1544
- "Required binary '%s' found. Install using `%s install -n base %s`"
1614
+ "Required binary '%s' not found. "
1615
+ "Install using `%s install -n base %s`"
1545
1616
% (k , self ._dependency_solver , k )
1546
1617
)
1547
1618
if to_remove :
@@ -1847,7 +1918,11 @@ def _create(self, env: ResolvedEnvironment, env_name: str) -> None:
1847
1918
) as f :
1848
1919
json .dump (env .env_id , f )
1849
1920
1850
- self ._echo (" done in %s seconds." % int (time .time () - start ), timestamp = False )
1921
+ delta_time = int (time .time () - start )
1922
+ self ._echo (
1923
+ " done in %s second%s." % (delta_time , plural_marker (delta_time )),
1924
+ timestamp = False ,
1925
+ )
1851
1926
1852
1927
def _remove (self , env_name : str ):
1853
1928
# TODO: Verify that this is a proper metaflow environment to remove
0 commit comments