Skip to content

Commit

Permalink
ux: add python as a minor pinning as its to important for beginner us…
Browse files Browse the repository at this point in the history
…ers to not get wrong
  • Loading branch information
ruben-arts committed Oct 18, 2024
1 parent 1493343 commit 0b81670
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 15 deletions.
37 changes: 22 additions & 15 deletions src/cli/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,28 @@ use std::{
str::FromStr,
};

use super::has_specs::HasSpecs;
use crate::environment::LockFileUsage;
use crate::{
cli::cli_config::{DependencyConfig, PrefixUpdateConfig, ProjectConfig},
environment::verify_prefix_location_unchanged,
load_lock_file,
lock_file::{filter_lock_file, LockFileDerivedData, UpdateContext},
project::{grouped_environment::GroupedEnvironment, DependencyType, Project},
};
use clap::Parser;
use indexmap::IndexMap;
use itertools::Itertools;
use pep440_rs::VersionSpecifiers;
use pep508_rs::{Requirement, VersionOrUrl::VersionSpecifier};
use pixi_config::PinningStrategy;
use pixi_manifest::{
pypi::PyPiPackageName, DependencyOverwriteBehavior, FeatureName, FeaturesExt, HasFeaturesIter,
SpecType,
};
use rattler_conda_types::{MatchSpec, PackageName, Platform, Version};
use rattler_lock::{LockFile, Package};

use super::has_specs::HasSpecs;
use crate::environment::LockFileUsage;
use crate::{
cli::cli_config::{DependencyConfig, PrefixUpdateConfig, ProjectConfig},
environment::verify_prefix_location_unchanged,
load_lock_file,
lock_file::{filter_lock_file, LockFileDerivedData, UpdateContext},
project::{grouped_environment::GroupedEnvironment, DependencyType, Project},
};

/// Adds dependencies to the project
///
/// The dependencies should be defined as MatchSpec for conda package, or a PyPI
Expand Down Expand Up @@ -380,18 +380,25 @@ fn update_conda_specs_from_lock_file(
.flatten()
.collect_vec();

let pinning_strategy = project.config().pinning_strategy.unwrap_or_default();
let mut pinning_strategy = project.config().pinning_strategy;
let channel_config = project.channel_config();
for (name, (spec_type, spec)) in conda_specs_to_add_constraints_for {
let version_constraint = pinning_strategy.determine_version_constraint(
conda_records.iter().filter_map(|record| {
// Edge case: python is a special case where we want to pin the minor version by default.
// This is done to avoid early user confusion when the minor version changes and environments magically start breaking.
// This move a `>=3.13, <4` to a `>=3.13, <3.14` constraint.
if name.as_normalized() == "python" && pinning_strategy.is_none() {
pinning_strategy = Some(PinningStrategy::Minor);
}

let version_constraint = pinning_strategy
.unwrap_or_default()
.determine_version_constraint(conda_records.iter().filter_map(|record| {
if record.package_record.name == name {
Some(record.package_record.version.version())
} else {
None
}
}),
);
}));

if let Some(version_constraint) = version_constraint {
implicit_constraints
Expand Down
63 changes: 63 additions & 0 deletions tests/add_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,66 @@ async fn add_unconstrainted_dependency() {
bar = "*"
"###);
}

#[tokio::test]
async fn pinning_dependency() {
// Create a channel with a single package
let mut package_database = PackageDatabase::default();
package_database.add_package(Package::build("foobar", "1").finish());
package_database.add_package(Package::build("python", "3.13").finish());

let local_channel = package_database.into_channel().await.unwrap();

// Initialize a new pixi project using the above channel
let pixi = PixiControl::new().unwrap();
pixi.init().with_channel(local_channel.url()).await.unwrap();

// Add the `packages` to the project
pixi.add("foobar").await.unwrap();
pixi.add("python").await.unwrap();

let project = pixi.project().unwrap();

// Get the specs for the `python` package
let python_spec = project
.manifest()
.default_feature()
.dependencies(None, None)
.unwrap_or_default()
.get("python")
.cloned()
.unwrap()
.to_toml_value()
.to_string();
// Testing to see if edge cases are handled correctly
// Python shouldn't be automatically pinned to a major version.
assert_eq!(python_spec, r#"">=3.13,<3.14""#);

// Get the specs for the `foobar` package
let foobar_spec = project
.manifest()
.default_feature()
.dependencies(None, None)
.unwrap_or_default()
.get("foobar")
.cloned()
.unwrap()
.to_toml_value()
.to_string();
assert_eq!(foobar_spec, r#"">=1,<2""#);

// Add the `python` package with a specific version
pixi.add("python==3.13").await.unwrap();
let project = pixi.project().unwrap();
let python_spec = project
.manifest()
.default_feature()
.dependencies(None, None)
.unwrap_or_default()
.get("python")
.cloned()
.unwrap()
.to_toml_value()
.to_string();
assert_eq!(python_spec, r#""==3.13""#);
}

0 comments on commit 0b81670

Please sign in to comment.