@@ -27,22 +27,42 @@ function _resolve_env_is_clean(conda_env, meta)
27
27
end
28
28
29
29
function _resolve_can_skip_1 (conda_env, load_path, meta_file)
30
- isdir (conda_env) || return false
31
- isfile (meta_file) || return false
30
+ if ! isdir (conda_env)
31
+ @debug " conda env does not exist" conda_env
32
+ return false
33
+ end
34
+ if ! isfile (meta_file)
35
+ @debug " meta file does not exist" meta_file
36
+ return false
37
+ end
32
38
meta = open (read_meta, meta_file)
33
- meta != = nothing || return false
34
- meta. version == VERSION || return false
35
- meta. load_path == load_path || return false
36
- meta. conda_env == conda_env || return false
39
+ if meta === nothing
40
+ @debug " meta file was not readable" meta_file
41
+ return false
42
+ end
43
+ if meta. version != VERSION
44
+ @debug " meta version has changed" meta. version VERSION
45
+ return false
46
+ end
47
+ if meta. load_path != load_path
48
+ @debug " load path has changed" meta. load_path load_path
49
+ return false
50
+ end
51
+ if meta. conda_env != conda_env
52
+ @debug " conda env has changed" meta. conda_env conda_env
53
+ return false
54
+ end
37
55
timestamp = max (meta. timestamp, stat (meta_file). mtime)
38
56
for env in [meta. load_path; meta. extra_path]
39
57
dir = isfile (env) ? dirname (env) : isdir (env) ? env : continue
40
58
if isdir (dir)
41
59
if stat (dir). mtime > timestamp
60
+ @debug " environment has changed" env dir timestamp
42
61
return false
43
62
else
44
63
fn = joinpath (dir, " CondaPkg.toml" )
45
64
if isfile (fn) && stat (fn). mtime > timestamp
65
+ @debug " environment has changed" env fn timestamp
46
66
return false
47
67
end
48
68
end
@@ -358,19 +378,20 @@ function _resolve_conda_remove(io, conda_env, pkgs)
358
378
nothing
359
379
end
360
380
361
- function _which_pip ()
362
- uv = which (" uv" )
363
- if uv != = nothing
364
- return ` $uv pip` , :uv
365
- end
366
- pip = which (" pip" )
367
- if pip != = nothing
368
- return ` $pip ` , :pip
381
+ function _pip_cmd (backend:: Symbol )
382
+ if backend == :uv
383
+ uv = which (" uv" )
384
+ uv === nothing && error (" uv not installed" )
385
+ return ` $uv pip`
386
+ else
387
+ @assert backend == :pip
388
+ pip = which (" pip" )
389
+ pip === nothing && error (" pip not installed" )
390
+ return ` $pip `
369
391
end
370
- error (" expecting pip (or uv) to be installed" )
371
392
end
372
393
373
- function _resolve_pip_install (io, pip_specs, load_path)
394
+ function _resolve_pip_install (io, pip_specs, load_path, backend )
374
395
args = String[]
375
396
for spec in pip_specs
376
397
if spec. binary == " only"
@@ -389,7 +410,8 @@ function _resolve_pip_install(io, pip_specs, load_path)
389
410
STATE. resolved = true
390
411
STATE. load_path = load_path
391
412
withenv () do
392
- pip, _ = _which_pip ()
413
+ @debug " pip install" get (ENV , " CONDA_PREFIX" , " " )
414
+ pip = _pip_cmd (backend)
393
415
cmd = ` $pip install $vrb $args `
394
416
_run (io, cmd, " Installing Pip packages" , flags = flags)
395
417
end
@@ -400,19 +422,20 @@ function _resolve_pip_install(io, pip_specs, load_path)
400
422
nothing
401
423
end
402
424
403
- function _resolve_pip_remove (io, pkgs, load_path)
425
+ function _resolve_pip_remove (io, pkgs, load_path, backend )
404
426
vrb = _verbosity_flags ()
405
427
flags = append! ([" -y" ], vrb)
406
428
old_load_path = STATE. load_path
407
429
try
408
430
STATE. resolved = true
409
431
STATE. load_path = load_path
410
432
withenv () do
411
- pip, kind = _which_pip ()
412
- if kind == :uv
433
+ @debug " pip uninstall" get (ENV , " CONDA_PREFIX" , " " )
434
+ pip = _pip_cmd (backend)
435
+ if backend == :uv
413
436
cmd = ` $pip uninstall $vrb $pkgs `
414
437
else
415
- cmd = ` $pip uninstall $vrb -y $pkgs `
438
+ cmd = ` $pip uninstall -y $vrb $pkgs `
416
439
end
417
440
_run (io, cmd, " Removing Pip packages" , flags = flags)
418
441
end
@@ -468,6 +491,17 @@ function offline()
468
491
getpref (Bool, " offline" , " JULIA_CONDAPKG_OFFLINE" , false )
469
492
end
470
493
494
+ function _pip_backend ()
495
+ b = getpref (String, " pip_backend" , " JULIA_CONDAPKG_PIP_BACKEND" , " uv" )
496
+ if b == " pip"
497
+ :pip
498
+ elseif b == " uv"
499
+ :uv
500
+ else
501
+ error (" pip_backend must be pip or uv, got $b " )
502
+ end
503
+ end
504
+
471
505
function resolve (;
472
506
force:: Bool = false ,
473
507
io:: IO = stderr ,
@@ -479,6 +513,7 @@ function resolve(;
479
513
# if backend is Null, assume resolved
480
514
back = backend ()
481
515
if back === :Null
516
+ @debug " using the null backend"
482
517
interactive && _log (io, " Using the Null backend, nothing to do" )
483
518
STATE. resolved = true
484
519
return
@@ -487,6 +522,7 @@ function resolve(;
487
522
# this is a very fast check which avoids touching the file system
488
523
load_path = Base. load_path ()
489
524
if ! force && STATE. resolved && STATE. load_path == load_path
525
+ @debug " already resolved (fast path)"
490
526
interactive && _log (io, " Dependencies already up to date (resolved)" )
491
527
return
492
528
end
@@ -533,6 +569,7 @@ function resolve(;
533
569
try
534
570
# skip resolving if nothing has changed since the metadata was updated
535
571
if ! force && _resolve_can_skip_1 (conda_env, load_path, meta_file)
572
+ @debug " already resolved"
536
573
STATE. resolved = true
537
574
interactive && _log (io, " Dependencies already up to date" )
538
575
return
@@ -541,11 +578,30 @@ function resolve(;
541
578
(packages, channels, pip_packages, extra_path) =
542
579
_resolve_find_dependencies (io, load_path)
543
580
# install pip if there are pip packages to install
544
- if ! isempty (pip_packages) && ! haskey (packages, " pip" )
545
- get! (Dict{String,PkgSpec}, packages, " pip" )[" <internal>" ] =
546
- PkgSpec (" pip" , version = " >=22.0.0" )
547
- if ! any (c. name in (" conda-forge" , " anaconda" ) for c in channels)
548
- push! (channels, ChannelSpec (" conda-forge" ))
581
+ pip_backend = _pip_backend ()
582
+ if ! isempty (pip_packages)
583
+ if pip_backend == :pip
584
+ if ! haskey (packages, " pip" )
585
+ if ! any (c. name in (" conda-forge" , " anaconda" ) for c in channels)
586
+ push! (channels, ChannelSpec (" conda-forge" ))
587
+ end
588
+ end
589
+ get! (Dict{String,PkgSpec}, packages, " pip" )[" <internal>" ] =
590
+ PkgSpec (" pip" , version = " >=22.0.0" )
591
+ else
592
+ @assert pip_backend == :uv
593
+ if ! haskey (packages, " uv" )
594
+ if ! any (c. name in (" conda-forge" ,) for c in channels)
595
+ push! (channels, ChannelSpec (" conda-forge" ))
596
+ end
597
+ end
598
+ get! (Dict{String,PkgSpec}, packages, " uv" )[" <internal>" ] =
599
+ PkgSpec (" uv" , version = " >=0.4" )
600
+ if ! haskey (packages, " python" )
601
+ # uv will not detect the conda environment if python is not installed
602
+ get! (Dict{String,PkgSpec}, packages, " python" )[" <internal>" ] =
603
+ PkgSpec (" python" )
604
+ end
549
605
end
550
606
end
551
607
# sort channels
@@ -599,7 +655,7 @@ function resolve(;
599
655
if ! isempty (removed_pip_pkgs) && ! shared
600
656
dry_run && return
601
657
changed = true
602
- _resolve_pip_remove (io, removed_pip_pkgs, load_path)
658
+ _resolve_pip_remove (io, removed_pip_pkgs, load_path, pip_backend )
603
659
end
604
660
if ! isempty (removed_pkgs) && ! shared
605
661
dry_run && return
@@ -620,7 +676,7 @@ function resolve(;
620
676
(! isempty (added_pip_pkgs) || ! isempty (changed_pip_pkgs) || changed)
621
677
dry_run && return
622
678
changed = true
623
- _resolve_pip_install (io, pip_specs, load_path)
679
+ _resolve_pip_install (io, pip_specs, load_path, pip_backend )
624
680
end
625
681
changed || _log (io, " Dependencies already up to date" )
626
682
else
@@ -639,7 +695,8 @@ function resolve(;
639
695
# create conda environment
640
696
_resolve_conda_install (io, conda_env, specs, channels; create = create)
641
697
# install pip packages
642
- isempty (pip_specs) || _resolve_pip_install (io, pip_specs, load_path)
698
+ isempty (pip_specs) ||
699
+ _resolve_pip_install (io, pip_specs, load_path, pip_backend)
643
700
end
644
701
# save metadata
645
702
meta = Meta (
0 commit comments