Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move maturin metadata from Cargo.toml to pyproject.toml #1493

Merged
merged 3 commits into from
Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions guide/src/project_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,13 @@ my-rust-and-python-project
```
#### Import Rust as a submodule of your project

If the Python module created by Rust has the same name as the Python package in a mixed Rust/Python project, IDEs might get confused. You might also want to discourage end users from using the Rust functions directly by giving it a different name, say '\_my_project'. This can be done by adding `name = <package name>.<rust pymodule name>` to the `[package.metadata.maturin]` in your `Cargo.toml`. For example:
If the Python module created by Rust has the same name as the Python package in a mixed Rust/Python project, IDEs might get confused.
You might also want to discourage end users from using the Rust functions directly by giving it a different name, say '\_my_project'.
This can be done by adding `module-name = <package name>.<rust pymodule name>` to the `[tool.maturin]` in your `pyproject.toml`. For example:

```toml
[package.metadata.maturin]
name = "my_project._my_project"
[tool.maturin]
module-name = "my_project._my_project"
```

You can then import your Rust module inside your Python source as follows:
Expand Down
8 changes: 2 additions & 6 deletions src/build_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ impl BuildOptions {
bail!(
"The module name must not contain a minus `-` \
(Make sure you have set an appropriate [lib] name or \
[package.metadata.maturin] name in your Cargo.toml)"
[tool.maturin] module-name in your pyproject.toml)"
);
}

Expand Down Expand Up @@ -1497,18 +1497,14 @@ mod test {

#[test]
fn test_get_min_python_minor() {
use crate::CargoToml;

// Nothing specified
let manifest_path = "test-crates/pyo3-pure/Cargo.toml";
let cargo_toml = CargoToml::from_path(manifest_path).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_path)
.exec()
.unwrap();
let metadata21 =
Metadata21::from_cargo_toml(&cargo_toml, "test-crates/pyo3-pure", &cargo_metadata)
.unwrap();
Metadata21::from_cargo_toml("test-crates/pyo3-pure", &cargo_metadata).unwrap();
assert_eq!(get_min_python_minor(&metadata21), None);
}
}
4 changes: 1 addition & 3 deletions src/cargo_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ impl CargoToml {
"scripts",
"classifiers",
"classifier",
"data",
"maintainer",
"maintainer-email",
"requires-dist",
Expand Down Expand Up @@ -101,9 +102,6 @@ struct CargoTomlMetadata {
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "kebab-case")]
pub struct RemainingCoreMetadata {
pub name: Option<String>,
/// The directory containing the wheel data
pub data: Option<String>,
/// Cargo compile targets
pub targets: Option<Vec<CargoTarget>>,
#[serde(flatten)]
Expand Down
102 changes: 8 additions & 94 deletions src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{CargoToml, PyProjectToml};
use crate::PyProjectToml;
use anyhow::{bail, Context, Result};
use fs_err as fs;
use regex::Regex;
Expand Down Expand Up @@ -281,7 +281,6 @@ impl Metadata21 {
///
/// manifest_path must be the directory, not the file
pub fn from_cargo_toml(
cargo_toml: &CargoToml,
manifest_path: impl AsRef<Path>,
cargo_metadata: &cargo_metadata::Metadata,
) -> Result<Metadata21> {
Expand All @@ -295,8 +294,6 @@ impl Metadata21 {
None
};

let extra_metadata = cargo_toml.remaining_core_metadata();

let mut description: Option<String> = None;
let mut description_content_type: Option<String> = None;
// See https://packaging.python.org/specifications/core-metadata/#description
Expand Down Expand Up @@ -329,16 +326,7 @@ impl Metadata21 {
}
}
};
let name = extra_metadata
.name
.map(|name| {
if let Some(pos) = name.find('.') {
name.split_at(pos).0.to_string()
} else {
name.clone()
}
})
.unwrap_or_else(|| package.name.clone());
let name = package.name.clone();
let mut project_url = HashMap::new();
if let Some(repository) = package.repository.as_ref() {
project_url.insert("Source Code".to_string(), repository.clone());
Expand Down Expand Up @@ -562,16 +550,14 @@ mod test {
};

let toml_with_path = cargo_toml.replace("REPLACE_README_PATH", &readme_path);
fs::write(&manifest_path, &toml_with_path).unwrap();
fs::write(&manifest_path, toml_with_path).unwrap();

let cargo_toml_struct: CargoToml = toml::from_str(&toml_with_path).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_path)
.exec()
.unwrap();

let metadata =
Metadata21::from_cargo_toml(&cargo_toml_struct, crate_path, &cargo_metadata).unwrap();
let metadata = Metadata21::from_cargo_toml(crate_path, &cargo_metadata).unwrap();

let actual = metadata.to_file_contents().unwrap();

Expand All @@ -590,13 +576,6 @@ mod test {
"cargo_toml name and version string do not match hardcoded values, test will fail",
);

if cargo_toml_struct.remaining_core_metadata().name.is_none() {
assert_eq!(
metadata.get_dist_info_dir(),
PathBuf::from("info_project-0.1.0.dist-info"),
"Dist info dir differed from expected"
);
}
metadata
}

Expand Down Expand Up @@ -648,59 +627,6 @@ mod test {
assert_metadata_from_cargo_toml(readme, cargo_toml, expected);
}

#[test]
fn test_metadata_from_cargo_toml_name_override() {
let readme = indoc!(
r#"
Some test package
=================
"#
);

let cargo_toml = indoc!(
r#"
[package]
authors = ["konstin <[email protected]>"]
name = "info-project"
version = "0.1.0"
description = "A test project"
homepage = "https://example.org"
readme = "REPLACE_README_PATH"

[lib]
crate-type = ["cdylib"]
name = "pyo3_pure"

[package.metadata.maturin]
name = "info"
"#
);

let expected = indoc!(
r#"
Metadata-Version: 2.1
Name: info
Version: 0.1.0
Summary: A test project
Home-Page: https://example.org
Author: konstin <[email protected]>
Author-email: konstin <[email protected]>
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

Some test package
=================
"#
);

let metadata = assert_metadata_from_cargo_toml(readme, cargo_toml, expected);

assert_eq!(
metadata.get_dist_info_dir(),
PathBuf::from("info-0.1.0.dist-info"),
"Dist info dir differed from expected"
);
}

#[test]
fn test_path_to_content_type() {
for (filename, expected) in &[
Expand All @@ -726,14 +652,11 @@ mod test {
#[test]
fn test_merge_metadata_from_pyproject_toml() {
let manifest_dir = PathBuf::from("test-crates").join("pyo3-pure");
let cargo_toml_str = fs_err::read_to_string(manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml: CargoToml = toml::from_str(&cargo_toml_str).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let mut metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
let mut metadata = Metadata21::from_cargo_toml(&manifest_dir, &cargo_metadata).unwrap();
let pyproject_toml = PyProjectToml::new(manifest_dir.join("pyproject.toml")).unwrap();
metadata
.merge_pyproject_toml(&manifest_dir, &pyproject_toml)
Expand Down Expand Up @@ -777,14 +700,11 @@ mod test {
#[test]
fn test_merge_metadata_from_pyproject_toml_with_customized_python_source_dir() {
let manifest_dir = PathBuf::from("test-crates").join("pyo3-mixed-py-subdir");
let cargo_toml_str = fs_err::read_to_string(manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml: CargoToml = toml::from_str(&cargo_toml_str).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let mut metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
let mut metadata = Metadata21::from_cargo_toml(&manifest_dir, &cargo_metadata).unwrap();
let pyproject_toml = PyProjectToml::new(manifest_dir.join("pyproject.toml")).unwrap();
metadata
.merge_pyproject_toml(&manifest_dir, &pyproject_toml)
Expand All @@ -801,14 +721,11 @@ mod test {
#[test]
fn test_implicit_readme() {
let manifest_dir = PathBuf::from("test-crates").join("pyo3-mixed");
let cargo_toml_str = fs_err::read_to_string(manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml = toml::from_str(&cargo_toml_str).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
let metadata = Metadata21::from_cargo_toml(&manifest_dir, &cargo_metadata).unwrap();
assert!(metadata.description.unwrap().starts_with("# pyo3-mixed"));
assert_eq!(
metadata.description_content_type.unwrap(),
Expand All @@ -819,14 +736,11 @@ mod test {
#[test]
fn test_merge_metadata_from_pyproject_dynamic_license_test() {
let manifest_dir = PathBuf::from("test-crates").join("license-test");
let cargo_toml_str = fs_err::read_to_string(manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml: CargoToml = toml::from_str(&cargo_toml_str).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let mut metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
let mut metadata = Metadata21::from_cargo_toml(&manifest_dir, &cargo_metadata).unwrap();
let pyproject_toml = PyProjectToml::new(manifest_dir.join("pyproject.toml")).unwrap();
metadata
.merge_pyproject_toml(&manifest_dir, &pyproject_toml)
Expand Down
30 changes: 9 additions & 21 deletions src/project_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,12 @@ impl ProjectResolver {

let cargo_metadata = Self::resolve_cargo_metadata(&manifest_file, &cargo_options)?;

let mut metadata21 =
Metadata21::from_cargo_toml(&cargo_toml, manifest_dir, &cargo_metadata)
.context("Failed to parse Cargo.toml into python metadata")?;
let mut metadata21 = Metadata21::from_cargo_toml(manifest_dir, &cargo_metadata)
.context("Failed to parse Cargo.toml into python metadata")?;
if let Some(pyproject) = pyproject {
let pyproject_dir = pyproject_file.parent().unwrap();
metadata21.merge_pyproject_toml(pyproject_dir, pyproject)?;
}
let extra_metadata = cargo_toml.remaining_core_metadata();

let crate_name = &cargo_toml.package.name;

Expand All @@ -128,7 +126,7 @@ impl ProjectResolver {
.unwrap_or(crate_name)
.to_owned();

let extension_name = extra_metadata.name.as_ref().unwrap_or(&module_name);
let extension_name = pyproject.and_then(|x| x.name()).unwrap_or(&module_name);

let project_root = if pyproject_file.is_file() {
pyproject_file.parent().unwrap_or(manifest_dir)
Expand Down Expand Up @@ -168,23 +166,13 @@ impl ProjectResolver {
None => project_root.to_path_buf(),
},
};
let data = match pyproject.and_then(|x| x.data()) {
Some(data) => {
if data.is_absolute() {
Some(data.to_path_buf())
} else {
Some(project_root.join(data))
}
let data = pyproject.and_then(|x| x.data()).map(|data| {
if data.is_absolute() {
data.to_path_buf()
} else {
project_root.join(data)
}
None => extra_metadata.data.as_ref().map(|data| {
let data = Path::new(data);
if data.is_absolute() {
data.to_path_buf()
} else {
manifest_dir.join(data)
}
}),
};
});
let project_layout =
ProjectLayout::determine(project_root, extension_name, py_root, python_packages, data)?;
Ok(Self {
Expand Down
7 changes: 7 additions & 0 deletions src/pyproject_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ impl GlobPattern {
#[serde(rename_all = "kebab-case")]
pub struct ToolMaturin {
// maturin specific options
// extension module name, accepts setuptools style import name like `foo.bar`
module_name: Option<String>,
include: Option<Vec<GlobPattern>>,
exclude: Option<Vec<GlobPattern>>,
bindings: Option<String>,
Expand Down Expand Up @@ -170,6 +172,11 @@ impl PyProjectToml {
self.tool.as_ref()?.maturin.as_ref()
}

/// Returns the value of `[tool.maturin.name]` in pyproject.toml
pub fn name(&self) -> Option<&str> {
self.maturin()?.module_name.as_deref()
}

/// Returns the value of `[tool.maturin.include]` in pyproject.toml
pub fn include(&self) -> Option<&[GlobPattern]> {
self.maturin()?.include.as_ref().map(AsRef::as_ref)
Expand Down
3 changes: 0 additions & 3 deletions test-crates/pyo3-mixed-py-subdir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,3 @@ pyo3 = { version = "0.18.0", features = ["extension-module"] }
[lib]
name = "pyo3_mixed_py_subdir"
crate-type = ["cdylib"]

[package.metadata.maturin]
name = "pyo3_mixed_py_subdir._pyo3_mixed"
1 change: 1 addition & 0 deletions test-crates/pyo3-mixed-py-subdir/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ requires-python = ">=3.6"
get_42 = "pyo3_mixed_py_subdir:get_42"

[tool.maturin]
module-name = "pyo3_mixed_py_subdir._pyo3_mixed"
python-source = "python"
3 changes: 0 additions & 3 deletions test-crates/pyo3-mixed-submodule/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ description = "Implements a dummy function combining rust and python"
readme = "README.md"
edition = "2021"

[package.metadata.maturin]
name = "pyo3_mixed_submodule.rust_module.rust"

[dependencies]
pyo3 = { version = "0.18.0", features = ["extension-module"] }

Expand Down
3 changes: 3 additions & 0 deletions test-crates/pyo3-mixed-submodule/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Rust"
]

[tool.maturin]
module-name = "pyo3_mixed_submodule.rust_module.rust"