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 all 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
16 changes: 6 additions & 10 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 @@ -664,9 +664,9 @@ impl BuildOptions {
.clone()
.unwrap_or_else(|| cargo_metadata.target_directory.clone().into_std_path_buf());

let remaining_core_metadata = cargo_toml.remaining_core_metadata();
let config_targets = remaining_core_metadata.targets.as_deref();
let cargo_targets = filter_cargo_targets(&cargo_metadata, bridge, config_targets)?;
let config_targets = pyproject.and_then(|x| x.targets());
let cargo_targets =
filter_cargo_targets(&cargo_metadata, bridge, config_targets.as_deref())?;

let crate_name = cargo_toml.package.name;
Ok(BuildContext {
Expand Down Expand Up @@ -747,7 +747,7 @@ fn validate_bridge_type(
fn filter_cargo_targets(
cargo_metadata: &Metadata,
bridge: BridgeModel,
config_targets: Option<&[crate::cargo_toml::CargoTarget]>,
config_targets: Option<&[crate::pyproject_toml::CargoTarget]>,
) -> Result<Vec<CompileTarget>> {
let root_pkg = cargo_metadata.root_package().unwrap();
let resolved_features = cargo_metadata
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);
}
}
23 changes: 1 addition & 22 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,28 +102,10 @@ 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)]
pub other: HashMap<String, toml::Value>,
}

/// Cargo compile target
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "kebab-case")]
pub struct CargoTarget {
/// Name as given in the `Cargo.toml` or generated from the file name
pub name: String,
/// Kind of target ("bin", "lib")
pub kind: Option<String>,
// TODO: Add bindings option
// Bridge model, which kind of bindings to use
// pub bindings: Option<String>,
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -160,10 +143,6 @@ mod test {

let cargo_toml: Result<CargoToml, _> = toml::from_str(cargo_toml);
assert!(cargo_toml.is_ok());

let maturin = cargo_toml.unwrap().remaining_core_metadata();
let targets = maturin.targets.unwrap();
assert_eq!("pyo3_pure", targets[0].name);
}

#[test]
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
Loading