mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-30 22:11:12 +00:00
Accept setup.py
and setup.cfg
files in compile (#2634)
## Summary This costs us ~nothing now that we support using PEP 517 hooks for `pyproject.toml`. ## Test Plan `cargo test`
This commit is contained in:
parent
71428f7d74
commit
ae35a215c6
5 changed files with 154 additions and 13 deletions
|
@ -94,8 +94,9 @@ uv pip install "flask[dotenv]" # Install Flask with "dotenv" extra.
|
||||||
To generate a set of locked dependencies:
|
To generate a set of locked dependencies:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
uv pip compile pyproject.toml -o requirements.txt # Read a pyproject.toml file.
|
|
||||||
uv pip compile requirements.in -o requirements.txt # Read a requirements.in file.
|
uv pip compile requirements.in -o requirements.txt # Read a requirements.in file.
|
||||||
|
uv pip compile pyproject.toml -o requirements.txt # Read a pyproject.toml file.
|
||||||
|
uv pip compile setup.py -o requirements.txt # Read a setup.py file.
|
||||||
echo flask | uv pip compile - -o requirements.txt # Read from stdin.
|
echo flask | uv pip compile - -o requirements.txt # Read from stdin.
|
||||||
uv pip freeze | uv pip compile - -o requirements.txt # Lock the current environment.
|
uv pip freeze | uv pip compile - -o requirements.txt # Lock the current environment.
|
||||||
```
|
```
|
||||||
|
|
|
@ -18,6 +18,10 @@ pub enum RequirementsSource {
|
||||||
RequirementsTxt(PathBuf),
|
RequirementsTxt(PathBuf),
|
||||||
/// Dependencies were provided via a `pyproject.toml` file (e.g., `pip-compile pyproject.toml`).
|
/// Dependencies were provided via a `pyproject.toml` file (e.g., `pip-compile pyproject.toml`).
|
||||||
PyprojectToml(PathBuf),
|
PyprojectToml(PathBuf),
|
||||||
|
/// Dependencies were provided via a `setup.py` file (e.g., `pip-compile setup.py`).
|
||||||
|
SetupPy(PathBuf),
|
||||||
|
/// Dependencies were provided via a `setup.cfg` file (e.g., `pip-compile setup.cfg`).
|
||||||
|
SetupCfg(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequirementsSource {
|
impl RequirementsSource {
|
||||||
|
@ -26,6 +30,10 @@ impl RequirementsSource {
|
||||||
pub fn from_requirements_file(path: PathBuf) -> Self {
|
pub fn from_requirements_file(path: PathBuf) -> Self {
|
||||||
if path.ends_with("pyproject.toml") {
|
if path.ends_with("pyproject.toml") {
|
||||||
Self::PyprojectToml(path)
|
Self::PyprojectToml(path)
|
||||||
|
} else if path.ends_with("setup.py") {
|
||||||
|
Self::SetupPy(path)
|
||||||
|
} else if path.ends_with("setup.cfg") {
|
||||||
|
Self::SetupCfg(path)
|
||||||
} else {
|
} else {
|
||||||
Self::RequirementsTxt(path)
|
Self::RequirementsTxt(path)
|
||||||
}
|
}
|
||||||
|
@ -74,16 +82,27 @@ impl RequirementsSource {
|
||||||
|
|
||||||
Self::Package(name)
|
Self::Package(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the source allows extras to be specified.
|
||||||
|
pub fn allows_extras(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::PyprojectToml(_) | Self::SetupPy(_) | Self::SetupCfg(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for RequirementsSource {
|
impl std::fmt::Display for RequirementsSource {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Package(package) => write!(f, "{package}"),
|
||||||
Self::Editable(path) => write!(f, "-e {path}"),
|
Self::Editable(path) => write!(f, "-e {path}"),
|
||||||
Self::RequirementsTxt(path) | Self::PyprojectToml(path) => {
|
Self::RequirementsTxt(path)
|
||||||
|
| Self::PyprojectToml(path)
|
||||||
|
| Self::SetupPy(path)
|
||||||
|
| Self::SetupCfg(path) => {
|
||||||
write!(f, "{}", path.simplified_display())
|
write!(f, "{}", path.simplified_display())
|
||||||
}
|
}
|
||||||
Self::Package(package) => write!(f, "{package}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,6 +177,28 @@ impl RequirementsSpecification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RequirementsSource::SetupPy(path) | RequirementsSource::SetupCfg(path) => {
|
||||||
|
let path = fs_err::canonicalize(path)?;
|
||||||
|
let source_tree = path.parent().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"The file `{}` appears to be a `setup.py` or `setup.cfg` file, which must be in a directory",
|
||||||
|
path.user_display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Self {
|
||||||
|
project: None,
|
||||||
|
requirements: vec![],
|
||||||
|
constraints: vec![],
|
||||||
|
overrides: vec![],
|
||||||
|
editables: vec![],
|
||||||
|
source_trees: vec![source_tree.to_path_buf()],
|
||||||
|
extras: FxHashSet::default(),
|
||||||
|
index_url: None,
|
||||||
|
extra_index_urls: vec![],
|
||||||
|
no_index: false,
|
||||||
|
find_links: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,14 +80,11 @@ pub(crate) async fn pip_compile(
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
// If the user requests `extras` but does not provide a pyproject toml source
|
// If the user requests `extras` but does not provide a valid source (e.g., a `pyproject.toml`),
|
||||||
if !matches!(extras, ExtrasSpecification::None)
|
// return an error.
|
||||||
&& !requirements
|
if !extras.is_empty() && !requirements.iter().any(RequirementsSource::allows_extras) {
|
||||||
.iter()
|
|
||||||
.any(|source| matches!(source, RequirementsSource::PyprojectToml(_)))
|
|
||||||
{
|
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"Requesting extras requires a pyproject.toml input file."
|
"Requesting extras requires a `pyproject.toml`, `setup.cfg`, or `setup.py` file."
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -510,6 +510,108 @@ setup(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compile a `setup.cfg` file.
|
||||||
|
#[test]
|
||||||
|
fn compile_setup_cfg() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let setup_cfg = context.temp_dir.child("setup.cfg");
|
||||||
|
setup_cfg.write_str(
|
||||||
|
r#"[options]
|
||||||
|
packages = find:
|
||||||
|
install_requires=
|
||||||
|
anyio
|
||||||
|
|
||||||
|
[options.extras_require]
|
||||||
|
dev =
|
||||||
|
iniconfig; python_version >= "3.7"
|
||||||
|
mypy; python_version <= "3.8"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let setup_py = context.temp_dir.child("setup.py");
|
||||||
|
setup_py.write_str(
|
||||||
|
r#"# setup.py
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="dummypkg",
|
||||||
|
description="A dummy package",
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.compile()
|
||||||
|
.arg("setup.cfg")
|
||||||
|
.arg("--extra")
|
||||||
|
.arg("dev"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z setup.cfg --extra dev
|
||||||
|
anyio==4.3.0
|
||||||
|
idna==3.6
|
||||||
|
# via anyio
|
||||||
|
iniconfig==2.0.0
|
||||||
|
sniffio==1.3.1
|
||||||
|
# via anyio
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 4 packages in [TIME]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile a `setup.py` file.
|
||||||
|
#[test]
|
||||||
|
fn compile_setup_py() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let setup_py = context.temp_dir.child("setup.py");
|
||||||
|
setup_py.write_str(
|
||||||
|
r#"# setup.py
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="dummypkg",
|
||||||
|
description="A dummy package",
|
||||||
|
install_requires=["anyio"],
|
||||||
|
extras_require={
|
||||||
|
"dev": ["iniconfig; python_version >= '3.7'", "mypy; python_version <= '3.8'"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.compile()
|
||||||
|
.arg("setup.py")
|
||||||
|
.arg("--extra")
|
||||||
|
.arg("dev"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z setup.py --extra dev
|
||||||
|
anyio==4.3.0
|
||||||
|
idna==3.6
|
||||||
|
# via anyio
|
||||||
|
iniconfig==2.0.0
|
||||||
|
sniffio==1.3.1
|
||||||
|
# via anyio
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 4 packages in [TIME]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Request multiple extras that do not exist as a dependency group in a `pyproject.toml` file.
|
/// Request multiple extras that do not exist as a dependency group in a `pyproject.toml` file.
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_pyproject_toml_extras_missing() -> Result<()> {
|
fn compile_pyproject_toml_extras_missing() -> Result<()> {
|
||||||
|
@ -564,7 +666,7 @@ fn compile_requirements_file_extra() -> Result<()> {
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
error: Requesting extras requires a pyproject.toml input file.
|
error: Requesting extras requires a `pyproject.toml`, `setup.cfg`, or `setup.py` file.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue