Files
uv/crates/uv-resolver/tests/resolver.rs
T
Ibraheem Ahmed 39af09f09b Parallelize resolver (#3627)
## Summary

This PR introduces parallelism to the resolver. Specifically, we can
perform PubGrub resolution on a separate thread, while keeping all I/O
on the tokio thread. We already have the infrastructure set up for this
with the channel and `OnceMap`, which makes this change relatively
simple. The big change needed to make this possible is removing the
lifetimes on some of the types that need to be shared between the
resolver and pubgrub thread.

A related PR, https://github.com/astral-sh/uv/pull/1163, found that
adding `yield_now` calls improved throughput. With optimal scheduling we
might be able to get away with everything on the same thread here.
However, in the ideal pipeline with perfect prefetching, the resolution
and prefetching can run completely in parallel without depending on one
another. While this would be very difficult to achieve, even with our
current prefetching pattern we see a consistent performance improvement
from parallelism.

This does also require reverting a few of the changes from
https://github.com/astral-sh/uv/pull/3413, but not all of them. The
sharing is isolated to the resolver task.

## Test Plan

On smaller tasks performance is mixed with ~2% improvements/regressions
on both sides. However, on medium-large resolution tasks we see the
benefits of parallelism, with improvements anywhere from 10-50%.

```
./scripts/requirements/jupyter.in
Benchmark 1: ./target/profiling/baseline (resolve-warm)
  Time (mean ± σ):      29.2 ms ±   1.8 ms    [User: 20.3 ms, System: 29.8 ms]
  Range (min … max):    26.4 ms …  36.0 ms    91 runs
 
Benchmark 2: ./target/profiling/parallel (resolve-warm)
  Time (mean ± σ):      25.5 ms ±   1.0 ms    [User: 19.5 ms, System: 25.5 ms]
  Range (min … max):    23.6 ms …  27.8 ms    99 runs
 
Summary
  ./target/profiling/parallel (resolve-warm) ran
    1.15 ± 0.08 times faster than ./target/profiling/baseline (resolve-warm)
```
```
./scripts/requirements/boto3.in   
Benchmark 1: ./target/profiling/baseline (resolve-warm)
  Time (mean ± σ):     487.1 ms ±   6.2 ms    [User: 464.6 ms, System: 61.6 ms]
  Range (min … max):   480.0 ms … 497.3 ms    10 runs
 
Benchmark 2: ./target/profiling/parallel (resolve-warm)
  Time (mean ± σ):     430.8 ms ±   9.3 ms    [User: 529.0 ms, System: 77.2 ms]
  Range (min … max):   417.1 ms … 442.5 ms    10 runs
 
Summary
  ./target/profiling/parallel (resolve-warm) ran
    1.13 ± 0.03 times faster than ./target/profiling/baseline (resolve-warm)
```
```
./scripts/requirements/airflow.in 
Benchmark 1: ./target/profiling/baseline (resolve-warm)
  Time (mean ± σ):     478.1 ms ±  18.8 ms    [User: 482.6 ms, System: 205.0 ms]
  Range (min … max):   454.7 ms … 508.9 ms    10 runs
 
Benchmark 2: ./target/profiling/parallel (resolve-warm)
  Time (mean ± σ):     308.7 ms ±  11.7 ms    [User: 428.5 ms, System: 209.5 ms]
  Range (min … max):   287.8 ms … 323.1 ms    10 runs
 
Summary
  ./target/profiling/parallel (resolve-warm) ran
    1.55 ± 0.08 times faster than ./target/profiling/baseline (resolve-warm)
```
2024-05-17 11:47:30 -04:00

795 lines
22 KiB
Rust

#![cfg(feature = "pypi")]
//! Integration tests for the resolver. These tests rely on a live network connection, and hit
//! `PyPI` directly.
use std::path::{Path, PathBuf};
use std::str::FromStr;
use anyhow::Result;
use chrono::{DateTime, Utc};
use once_cell::sync::Lazy;
use distribution_types::{IndexLocations, Requirement, Resolution, SourceDist};
use pep508_rs::{MarkerEnvironment, MarkerEnvironmentBuilder};
use platform_tags::{Arch, Os, Platform, Tags};
use uv_cache::Cache;
use uv_client::RegistryClientBuilder;
use uv_configuration::{
BuildKind, Concurrency, Constraints, NoBinary, NoBuild, Overrides, SetupPyStrategy,
};
use uv_distribution::DistributionDatabase;
use uv_interpreter::{find_default_python, Interpreter, PythonEnvironment};
use uv_resolver::{
DisplayResolutionGraph, ExcludeNewer, Exclusions, FlatIndex, InMemoryIndex, Manifest, Options,
OptionsBuilder, PreReleaseMode, Preference, PythonRequirement, ResolutionGraph, ResolutionMode,
Resolver,
};
use uv_types::{
BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, SourceBuildTrait,
};
// Exclude any packages uploaded after this date.
static EXCLUDE_NEWER: Lazy<ExcludeNewer> = Lazy::new(|| {
ExcludeNewer::from(
DateTime::parse_from_rfc3339("2023-11-18T12:00:00Z")
.unwrap()
.with_timezone(&Utc),
)
});
struct DummyContext {
cache: Cache,
interpreter: Interpreter,
index_locations: IndexLocations,
}
impl DummyContext {
fn new(cache: Cache, interpreter: Interpreter) -> Self {
Self {
cache,
interpreter,
index_locations: IndexLocations::default(),
}
}
}
impl BuildContext for DummyContext {
type SourceDistBuilder = DummyBuilder;
fn cache(&self) -> &Cache {
&self.cache
}
fn interpreter(&self) -> &Interpreter {
&self.interpreter
}
fn build_isolation(&self) -> BuildIsolation {
BuildIsolation::Isolated
}
fn no_build(&self) -> &NoBuild {
&NoBuild::None
}
fn no_binary(&self) -> &NoBinary {
&NoBinary::None
}
fn setup_py_strategy(&self) -> SetupPyStrategy {
SetupPyStrategy::default()
}
fn index_locations(&self) -> &IndexLocations {
&self.index_locations
}
async fn resolve<'a>(&'a self, _: &'a [Requirement]) -> Result<Resolution> {
panic!("The test should not need to build source distributions")
}
async fn install<'a>(&'a self, _: &'a Resolution, _: &'a PythonEnvironment) -> Result<()> {
panic!("The test should not need to build source distributions")
}
async fn setup_build<'a>(
&'a self,
_: &'a Path,
_: Option<&'a Path>,
_: &'a str,
_: Option<&'a SourceDist>,
_: BuildKind,
) -> Result<Self::SourceDistBuilder> {
Ok(DummyBuilder)
}
}
struct DummyBuilder;
impl SourceBuildTrait for DummyBuilder {
async fn metadata(&mut self) -> Result<Option<PathBuf>> {
panic!("The test should not need to build source distributions")
}
async fn wheel<'a>(&'a self, _: &'a Path) -> Result<String> {
panic!("The test should not need to build source distributions")
}
}
async fn resolve(
manifest: Manifest,
options: Options,
markers: &'static MarkerEnvironment,
tags: &Tags,
) -> Result<ResolutionGraph> {
let cache = Cache::temp().unwrap().init().unwrap();
let real_interpreter = find_default_python(&cache).expect("Expected a python to be installed");
let client = RegistryClientBuilder::new(cache).build();
let flat_index = FlatIndex::default();
let index = InMemoryIndex::default();
let interpreter = Interpreter::artificial(real_interpreter.platform().clone(), markers.clone());
let python_requirement = PythonRequirement::from_marker_environment(&interpreter, markers);
let cache = Cache::temp().unwrap().init().unwrap();
let build_context = DummyContext::new(cache, interpreter.clone());
let hashes = HashStrategy::None;
let installed_packages = EmptyInstalledPackages;
let concurrency = Concurrency::default();
let resolver = Resolver::new(
manifest,
options,
&python_requirement,
Some(markers),
tags,
&flat_index,
&index,
&hashes,
&build_context,
installed_packages,
DistributionDatabase::new(&client, &build_context, concurrency.downloads),
)?;
Ok(resolver.resolve().await?)
}
macro_rules! assert_snapshot {
($value:expr, @$snapshot:literal) => {
let snapshot = anstream::adapter::strip_str(&format!("{}", $value)).to_string();
insta::assert_snapshot!(&snapshot, @$snapshot)
};
}
#[tokio::test]
async fn black() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black<=23.9.1").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
#[tokio::test]
async fn black_colorama() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black[colorama]<=23.9.1").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
colorama==0.4.6
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
/// Resolve Black with an invalid extra. The resolver should ignore the extra.
#[tokio::test]
async fn black_tensorboard() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black[tensorboard]<=23.9.1").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
#[tokio::test]
async fn black_python_310() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black<=23.9.1").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_310, &TAGS_310).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
tomli==2.0.1
# via black
typing-extensions==4.8.0
# via black
"###);
Ok(())
}
/// Resolve `black` with a constraint on `mypy-extensions`, to ensure that constraints are
/// respected.
#[tokio::test]
async fn black_mypy_extensions() -> Result<()> {
let manifest = Manifest::new(
vec![
Requirement::from_pep508(pep508_rs::Requirement::from_str("black<=23.9.1").unwrap())
.unwrap(),
],
Constraints::from_requirements(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("mypy-extensions<0.4.4").unwrap(),
)
.unwrap()]),
Overrides::default(),
vec![],
None,
vec![],
Exclusions::default(),
vec![],
);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
mypy-extensions==0.4.3
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
/// Resolve `black` with a constraint on `mypy-extensions[extra]`, to ensure that extras are
/// ignored when resolving constraints.
#[tokio::test]
async fn black_mypy_extensions_extra() -> Result<()> {
let manifest = Manifest::new(
vec![
Requirement::from_pep508(pep508_rs::Requirement::from_str("black<=23.9.1").unwrap())
.unwrap(),
],
Constraints::from_requirements(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("mypy-extensions[extra]<0.4.4").unwrap(),
)
.unwrap()]),
Overrides::default(),
vec![],
None,
vec![],
Exclusions::default(),
vec![],
);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
mypy-extensions==0.4.3
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
/// Resolve `black` with a redundant constraint on `flake8`, to ensure that constraints don't
/// introduce new dependencies.
#[tokio::test]
async fn black_flake8() -> Result<()> {
let manifest = Manifest::new(
vec![
Requirement::from_pep508(pep508_rs::Requirement::from_str("black<=23.9.1").unwrap())
.unwrap(),
],
Constraints::from_requirements(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("flake8<1").unwrap(),
)
.unwrap()]),
Overrides::default(),
vec![],
None,
vec![],
Exclusions::default(),
vec![],
);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
#[tokio::test]
async fn black_lowest() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black>21").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.resolution_mode(ResolutionMode::Lowest)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==22.1.0
click==8.0.0
# via black
mypy-extensions==0.4.3
# via black
pathspec==0.9.0
# via black
platformdirs==2.0.0
# via black
tomli==1.1.0
# via black
"###);
Ok(())
}
#[tokio::test]
async fn black_lowest_direct() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black>21").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.resolution_mode(ResolutionMode::LowestDirect)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==22.1.0
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
tomli==2.0.1
# via black
"###);
Ok(())
}
#[tokio::test]
async fn black_respect_preference() -> Result<()> {
let manifest = Manifest::new(
vec![Requirement::from_pep508(pep508_rs::Requirement::from_str("black<=23.9.1")?).unwrap()],
Constraints::default(),
Overrides::default(),
vec![Preference::from_requirement(
Requirement::from_pep508(pep508_rs::Requirement::from_str("black==23.9.0")?).unwrap(),
)],
None,
vec![],
Exclusions::default(),
vec![],
);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.0
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
#[tokio::test]
async fn black_ignore_preference() -> Result<()> {
let manifest = Manifest::new(
vec![Requirement::from_pep508(pep508_rs::Requirement::from_str("black<=23.9.1")?).unwrap()],
Constraints::default(),
Overrides::default(),
vec![Preference::from_requirement(
Requirement::from_pep508(pep508_rs::Requirement::from_str("black==23.9.2")?).unwrap(),
)],
None,
vec![],
Exclusions::default(),
vec![],
);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
black==23.9.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
Ok(())
}
#[tokio::test]
async fn black_disallow_prerelease() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black<=20.0").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.prerelease_mode(PreReleaseMode::Disallow)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let err = resolve(manifest, options, &MARKERS_311, &TAGS_311)
.await
.unwrap_err();
assert_snapshot!(err, @r###"
Because only black>20.0 is available and you require black<=20.0, we can conclude that the requirements are unsatisfiable.
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
Ok(())
}
#[tokio::test]
async fn black_allow_prerelease_if_necessary() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("black<=20.0").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.prerelease_mode(PreReleaseMode::IfNecessary)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let err = resolve(manifest, options, &MARKERS_311, &TAGS_311)
.await
.unwrap_err();
assert_snapshot!(err, @r###"
Because only black>20.0 is available and you require black<=20.0, we can conclude that the requirements are unsatisfiable.
hint: Pre-releases are available for black in the requested range (e.g., 19.10b0), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
Ok(())
}
#[tokio::test]
async fn pylint_disallow_prerelease() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("pylint==2.3.0").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.prerelease_mode(PreReleaseMode::Disallow)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
astroid==3.0.1
# via pylint
isort==5.12.0
# via pylint
mccabe==0.7.0
# via pylint
pylint==2.3.0
"###);
Ok(())
}
#[tokio::test]
async fn pylint_allow_prerelease() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("pylint==2.3.0").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.prerelease_mode(PreReleaseMode::Allow)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
astroid==3.0.1
# via pylint
isort==6.0.0b2
# via pylint
mccabe==0.7.0
# via pylint
pylint==2.3.0
"###);
Ok(())
}
#[tokio::test]
async fn pylint_allow_explicit_prerelease_without_marker() -> Result<()> {
let manifest = Manifest::simple(vec![
Requirement::from_pep508(pep508_rs::Requirement::from_str("pylint==2.3.0").unwrap())
.unwrap(),
Requirement::from_pep508(pep508_rs::Requirement::from_str("isort>=5.0.0").unwrap())
.unwrap(),
]);
let options = OptionsBuilder::new()
.prerelease_mode(PreReleaseMode::Explicit)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
astroid==3.0.1
# via pylint
isort==5.12.0
# via pylint
mccabe==0.7.0
# via pylint
pylint==2.3.0
"###);
Ok(())
}
#[tokio::test]
async fn pylint_allow_explicit_prerelease_with_marker() -> Result<()> {
let manifest = Manifest::simple(vec![
Requirement::from_pep508(pep508_rs::Requirement::from_str("pylint==2.3.0").unwrap())
.unwrap(),
Requirement::from_pep508(pep508_rs::Requirement::from_str("isort>=5.0.0b").unwrap())
.unwrap(),
]);
let options = OptionsBuilder::new()
.prerelease_mode(PreReleaseMode::Explicit)
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let resolution = resolve(manifest, options, &MARKERS_311, &TAGS_311).await?;
assert_snapshot!(DisplayResolutionGraph::from(&resolution), @r###"
astroid==3.0.1
# via pylint
isort==6.0.0b2
# via pylint
mccabe==0.7.0
# via pylint
pylint==2.3.0
"###);
Ok(())
}
/// Resolve `msgraph-sdk==1.0.0`, which depends on `msgraph-core>=1.0.0a2`. The resolver should
/// fail with a pre-release-centric hint.
#[tokio::test]
async fn msgraph_sdk() -> Result<()> {
let manifest = Manifest::simple(vec![Requirement::from_pep508(
pep508_rs::Requirement::from_str("msgraph-sdk==1.0.0").unwrap(),
)
.unwrap()]);
let options = OptionsBuilder::new()
.exclude_newer(Some(*EXCLUDE_NEWER))
.build();
let err = resolve(manifest, options, &MARKERS_311, &TAGS_311)
.await
.unwrap_err();
assert_snapshot!(err, @r###"
Because only msgraph-core<1.0.0a2 is available and msgraph-sdk==1.0.0 depends on msgraph-core>=1.0.0a2, we can conclude that msgraph-sdk==1.0.0 cannot be used.
And because you require msgraph-sdk==1.0.0, we can conclude that the requirements are unsatisfiable.
hint: msgraph-core was requested with a pre-release marker (e.g., msgraph-core>=1.0.0a2), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###);
Ok(())
}
static MARKERS_311: Lazy<MarkerEnvironment> = Lazy::new(|| {
MarkerEnvironment::try_from(MarkerEnvironmentBuilder {
implementation_name: "cpython",
implementation_version: "3.11.5",
os_name: "posix",
platform_machine: "arm64",
platform_python_implementation: "CPython",
platform_release: "21.6.0",
platform_system: "Darwin",
platform_version: "Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T6000",
python_full_version: "3.11.5",
python_version: "3.11",
sys_platform: "darwin",
}).unwrap()
});
static TAGS_311: Lazy<Tags> = Lazy::new(|| {
Tags::from_env(
&Platform::new(
Os::Macos {
major: 21,
minor: 6,
},
Arch::Aarch64,
),
(3, 11),
"cpython",
(3, 11),
false,
)
.unwrap()
});
static MARKERS_310: Lazy<MarkerEnvironment> = Lazy::new(|| {
MarkerEnvironment::try_from(MarkerEnvironmentBuilder {
implementation_name: "cpython",
implementation_version: "3.10.5",
os_name: "posix",
platform_machine: "arm64",
platform_python_implementation: "CPython",
platform_release: "21.6.0",
platform_system: "Darwin",
platform_version: "Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T6000",
python_full_version: "3.10.5",
python_version: "3.10",
sys_platform: "darwin",
}).unwrap()
});
static TAGS_310: Lazy<Tags> = Lazy::new(|| {
Tags::from_env(
&Platform::new(
Os::Macos {
major: 21,
minor: 6,
},
Arch::Aarch64,
),
(3, 10),
"cpython",
(3, 10),
false,
)
.unwrap()
});