From 5b0d9cd96e82a19b0dfc3404ad0923d0109d6ca5 Mon Sep 17 00:00:00 2001 From: messense Date: Thu, 16 Feb 2023 23:50:12 +0800 Subject: [PATCH 1/3] Remove `package.metadata.maturin.data` from `Cargo.toml` Use `tool.maturin.data` of `pyproject.toml` instead. --- src/cargo_toml.rs | 3 +-- src/project_layout.rs | 22 ++++++---------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index 59a1de34c..49a4165df 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -64,6 +64,7 @@ impl CargoToml { "scripts", "classifiers", "classifier", + "data", "maintainer", "maintainer-email", "requires-dist", @@ -102,8 +103,6 @@ struct CargoTomlMetadata { #[serde(rename_all = "kebab-case")] pub struct RemainingCoreMetadata { pub name: Option, - /// The directory containing the wheel data - pub data: Option, /// Cargo compile targets pub targets: Option>, #[serde(flatten)] diff --git a/src/project_layout.rs b/src/project_layout.rs index 61956eec2..9679a709b 100644 --- a/src/project_layout.rs +++ b/src/project_layout.rs @@ -168,23 +168,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 { From 0b5311e698ef5ef2be6f4cb3cb18ac8f0b928516 Mon Sep 17 00:00:00 2001 From: messense Date: Sat, 18 Feb 2023 17:07:07 +0800 Subject: [PATCH 2/3] Replace `package.metadata.maturin.name` with `tool.maturin.module-name` in `pyproject.toml` --- guide/src/project_layout.md | 8 +- src/build_options.rs | 8 +- src/cargo_toml.rs | 1 - src/metadata.rs | 102 ++---------------- src/project_layout.rs | 8 +- src/pyproject_toml.rs | 7 ++ test-crates/pyo3-mixed-py-subdir/Cargo.toml | 3 - .../pyo3-mixed-py-subdir/pyproject.toml | 1 + test-crates/pyo3-mixed-submodule/Cargo.toml | 3 - .../pyo3-mixed-submodule/pyproject.toml | 3 + 10 files changed, 29 insertions(+), 115 deletions(-) diff --git a/guide/src/project_layout.md b/guide/src/project_layout.md index e6969c838..b3071f959 100644 --- a/guide/src/project_layout.md +++ b/guide/src/project_layout.md @@ -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 = .` 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 = .` 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: diff --git a/src/build_options.rs b/src/build_options.rs index ddd339260..22edf3ee7 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -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)" ); } @@ -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); } } diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index 49a4165df..b31e9a936 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -102,7 +102,6 @@ struct CargoTomlMetadata { #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(rename_all = "kebab-case")] pub struct RemainingCoreMetadata { - pub name: Option, /// Cargo compile targets pub targets: Option>, #[serde(flatten)] diff --git a/src/metadata.rs b/src/metadata.rs index 954e9b678..e96d3137d 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,4 +1,4 @@ -use crate::{CargoToml, PyProjectToml}; +use crate::PyProjectToml; use anyhow::{bail, Context, Result}; use fs_err as fs; use regex::Regex; @@ -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, cargo_metadata: &cargo_metadata::Metadata, ) -> Result { @@ -295,8 +294,6 @@ impl Metadata21 { None }; - let extra_metadata = cargo_toml.remaining_core_metadata(); - let mut description: Option = None; let mut description_content_type: Option = None; // See https://packaging.python.org/specifications/core-metadata/#description @@ -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()); @@ -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(); @@ -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 } @@ -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 "] - 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 - Author-email: konstin - 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 &[ @@ -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) @@ -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) @@ -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(), @@ -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) diff --git a/src/project_layout.rs b/src/project_layout.rs index 9679a709b..4300b7d04 100644 --- a/src/project_layout.rs +++ b/src/project_layout.rs @@ -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; @@ -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) diff --git a/src/pyproject_toml.rs b/src/pyproject_toml.rs index d3e0ac8e5..f4ad3ad24 100644 --- a/src/pyproject_toml.rs +++ b/src/pyproject_toml.rs @@ -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, include: Option>, exclude: Option>, bindings: Option, @@ -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) diff --git a/test-crates/pyo3-mixed-py-subdir/Cargo.toml b/test-crates/pyo3-mixed-py-subdir/Cargo.toml index 4620a49ba..e6fe21af5 100644 --- a/test-crates/pyo3-mixed-py-subdir/Cargo.toml +++ b/test-crates/pyo3-mixed-py-subdir/Cargo.toml @@ -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" diff --git a/test-crates/pyo3-mixed-py-subdir/pyproject.toml b/test-crates/pyo3-mixed-py-subdir/pyproject.toml index d4dec533b..0f11e634b 100644 --- a/test-crates/pyo3-mixed-py-subdir/pyproject.toml +++ b/test-crates/pyo3-mixed-py-subdir/pyproject.toml @@ -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" diff --git a/test-crates/pyo3-mixed-submodule/Cargo.toml b/test-crates/pyo3-mixed-submodule/Cargo.toml index a7bb42a48..5bf924c67 100644 --- a/test-crates/pyo3-mixed-submodule/Cargo.toml +++ b/test-crates/pyo3-mixed-submodule/Cargo.toml @@ -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"] } diff --git a/test-crates/pyo3-mixed-submodule/pyproject.toml b/test-crates/pyo3-mixed-submodule/pyproject.toml index 14719144a..be3882d7b 100644 --- a/test-crates/pyo3-mixed-submodule/pyproject.toml +++ b/test-crates/pyo3-mixed-submodule/pyproject.toml @@ -8,3 +8,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Rust" ] + +[tool.maturin] +module-name = "pyo3_mixed_submodule.rust_module.rust" From c1f3991c72fb5cf66df5da850471b6020c8b874d Mon Sep 17 00:00:00 2001 From: messense Date: Sat, 25 Mar 2023 20:30:49 +0800 Subject: [PATCH 3/3] Replace `package.metadata.maturin.targets` with `tool.maturin.targets` in `pyproject.toml` --- src/build_options.rs | 8 ++++---- src/cargo_toml.rs | 19 ------------------ src/pyproject_toml.rs | 27 ++++++++++++++++++++++++++ test-crates/hello-world/Cargo.toml | 7 ------- test-crates/hello-world/pyproject.toml | 7 +++++++ 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/build_options.rs b/src/build_options.rs index 22edf3ee7..a90a40497 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -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 { @@ -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> { let root_pkg = cargo_metadata.root_package().unwrap(); let resolved_features = cargo_metadata diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index b31e9a936..737d95f66 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -102,25 +102,10 @@ struct CargoTomlMetadata { #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(rename_all = "kebab-case")] pub struct RemainingCoreMetadata { - /// Cargo compile targets - pub targets: Option>, #[serde(flatten)] pub other: HashMap, } -/// 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, - // TODO: Add bindings option - // Bridge model, which kind of bindings to use - // pub bindings: Option, -} - #[cfg(test)] mod test { use super::*; @@ -158,10 +143,6 @@ mod test { let cargo_toml: Result = 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] diff --git a/src/pyproject_toml.rs b/src/pyproject_toml.rs index f4ad3ad24..191f236a3 100644 --- a/src/pyproject_toml.rs +++ b/src/pyproject_toml.rs @@ -81,6 +81,19 @@ impl GlobPattern { } } +/// 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, + // TODO: Add bindings option + // Bridge model, which kind of bindings to use + // pub bindings: Option, +} + /// The `[tool.maturin]` section of a pyproject.toml #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] @@ -103,6 +116,8 @@ pub struct ToolMaturin { python_packages: Option>, /// Path to the wheel directory, defaults to `.data` data: Option, + /// Cargo compile targets + pub targets: Option>, // Some customizable cargo options /// Build artifacts with the specified Cargo profile pub profile: Option, @@ -228,6 +243,11 @@ impl PyProjectToml { self.maturin().and_then(|maturin| maturin.data.as_deref()) } + /// Returns the value of `[tool.maturin.targets]` in pyproject.toml + pub fn targets(&self) -> Option> { + self.maturin().and_then(|maturin| maturin.targets.clone()) + } + /// Returns the value of `[tool.maturin.manifest-path]` in pyproject.toml pub fn manifest_path(&self) -> Option<&Path> { self.maturin()?.manifest_path.as_deref() @@ -310,6 +330,11 @@ mod tests { no-default-features = true locked = true rustc-args = ["-Z", "unstable-options"] + + [[tool.maturin.targets]] + name = "pyo3_pure" + kind = "lib" + bindings = "pyo3" "#, ) .unwrap(); @@ -334,6 +359,8 @@ mod tests { maturin.python_packages, Some(vec!["foo".to_string(), "bar".to_string()]) ); + let targets = maturin.targets.as_ref().unwrap(); + assert_eq!("pyo3_pure", targets[0].name); } #[test] diff --git a/test-crates/hello-world/Cargo.toml b/test-crates/hello-world/Cargo.toml index c11820ea6..a5b03b869 100644 --- a/test-crates/hello-world/Cargo.toml +++ b/test-crates/hello-world/Cargo.toml @@ -8,10 +8,3 @@ readme = "../../README.md" default-run = "hello-world" [dependencies] - -[[package.metadata.maturin.targets]] -name = "hello-world" -bindings = "bin" - -[[package.metadata.maturin.targets]] -name = "foo" diff --git a/test-crates/hello-world/pyproject.toml b/test-crates/hello-world/pyproject.toml index ce8aada25..cd8509ab2 100644 --- a/test-crates/hello-world/pyproject.toml +++ b/test-crates/hello-world/pyproject.toml @@ -4,3 +4,10 @@ build-backend = "maturin" [tool.maturin] bindings = "bin" + +[[tool.maturin.targets]] +name = "hello-world" +bindings = "bin" + +[[tool.maturin.targets]] +name = "foo"