diff --git a/src/build_context.rs b/src/build_context.rs index 27f91076b..6fc17b97f 100644 --- a/src/build_context.rs +++ b/src/build_context.rs @@ -73,22 +73,31 @@ pub enum ProjectLayout { impl ProjectLayout { /// Checks whether a python module exists besides Cargo.toml with the right name - pub fn determine(project_root: impl AsRef, module_name: &str) -> Result { + pub fn determine( + project_root: impl AsRef, + module_name: &str, + py_src: Option>, + ) -> Result { // A dot in the module name means the extension module goes into the module folder specified by the path let parts: Vec<&str> = module_name.split('.').collect(); let project_root = project_root.as_ref(); + let python_root = if let Some(py_src) = py_src { + project_root.join(py_src.as_ref()) + } else { + project_root.to_path_buf() + }; let (python_module, rust_module, extension_name) = if parts.len() > 1 { let mut rust_module = project_root.to_path_buf(); rust_module.extend(&parts[0..parts.len() - 1]); ( - project_root.join(parts[0]), + python_root.join(parts[0]), rust_module, parts[parts.len() - 1].to_string(), ) } else { ( - project_root.join(module_name), - project_root.join(module_name), + python_root.join(module_name), + python_root.join(module_name), module_name.to_string(), ) }; diff --git a/src/build_options.rs b/src/build_options.rs index 284189ea9..e91c2daa8 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -152,7 +152,8 @@ impl BuildOptions { .filter(|name| name.contains('.')) .unwrap_or(&module_name); - let project_layout = ProjectLayout::determine(manifest_dir, extension_name)?; + let py_src = extra_metadata.python_source.clone(); + let project_layout = ProjectLayout::determine(manifest_dir, extension_name, py_src)?; let mut cargo_extra_args = split_extra_args(&self.cargo_extra_args)?; if let Some(ref target) = self.target { diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index cc6aaa4f4..ad2b45d11 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -123,6 +123,7 @@ pub struct RemainingCoreMetadata { pub project_url: Option>, pub provides_extra: Option>, pub description_content_type: Option, + pub python_source: Option, } #[cfg(test)] diff --git a/test-crates/pyo3-mixed-py-subdir/Cargo.lock b/test-crates/pyo3-mixed-py-subdir/Cargo.lock new file mode 100644 index 000000000..99ae4b18b --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/Cargo.lock @@ -0,0 +1,257 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "indoc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "libc" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" + +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" +dependencies = [ + "proc-macro-hack", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pyo3" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2fee5b9b746eccdaec45991347fd5565b3c3ea90abc99437f31fd8d6148a8d" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "parking_lot", + "paste", + "pyo3-build-config", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905f7e52b894dfabd6d65287b8a9037a9e2b15f3ae5995340edf12e18a9f488e" +dependencies = [ + "once_cell", +] + +[[package]] +name = "pyo3-macros" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33702750c42c99f6e3c062b59e5c3b6d93b2a91ee379f99028b43b92b05ebc" +dependencies = [ + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07dcdedfc1ba4e6a929dec829eb0d5520490c7bd104a7a6201a9f2e8a60e082" +dependencies = [ + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "pyo3-mixed-py-subdir" +version = "2.1.3" +dependencies = [ + "pyo3", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "syn" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/test-crates/pyo3-mixed-py-subdir/Cargo.toml b/test-crates/pyo3-mixed-py-subdir/Cargo.toml new file mode 100644 index 000000000..ba3f5a0e4 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["konstin "] +name = "pyo3-mixed-py-subdir" +version = "2.1.3" +description = "Implements a dummy function combining rust and python" +readme = "Readme.md" +edition = "2018" + +[dependencies] +pyo3 = { version = "0.14.0", features = ["extension-module"] } + +[lib] +name = "pyo3_mixed_py_subdir" +crate-type = ["cdylib"] + +[package.metadata.maturin] +python-source = "python" diff --git a/test-crates/pyo3-mixed-py-subdir/Readme.md b/test-crates/pyo3-mixed-py-subdir/Readme.md new file mode 100644 index 000000000..038375070 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/Readme.md @@ -0,0 +1,30 @@ +# pyo3-mixed + +A package for testing maturin with a mixed pyo3/python project. + +## Usage + +```bash +pip install . +``` + +```python +import pyo3_mixed +assert pyo3_mixed.get_42() == 42 +``` + +## Testing + +Install tox: + +```bash +pip install tox +``` + +Run it: + +```bash +tox +``` + +The tests are in `test_pyo3_mixed.py`, while the configuration is in tox.ini diff --git a/test-crates/pyo3-mixed-py-subdir/check_installed/check_installed.py b/test-crates/pyo3-mixed-py-subdir/check_installed/check_installed.py new file mode 100755 index 000000000..a08364703 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/check_installed/check_installed.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +import pyo3_mixed_py_subdir as pyo3_mixed + +assert pyo3_mixed.get_42() == 42 + +print("SUCCESS") diff --git a/test-crates/pyo3-mixed-py-subdir/pyproject.toml b/test-crates/pyo3-mixed-py-subdir/pyproject.toml new file mode 100644 index 000000000..9c3771ba7 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/pyproject.toml @@ -0,0 +1,14 @@ +[build-system] +requires = ["maturin>=0.11,<0.12"] +build-backend = "maturin" + +[project] +name = "pyo3-mixed-py-subdir" +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Rust" +] +requires-python = ">=3.6" + +[project.scripts] +get_42 = "pyo3_mixed_py_subdir:get_42" diff --git a/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/__init__.py b/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/__init__.py new file mode 100644 index 000000000..fd165e408 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/__init__.py @@ -0,0 +1,6 @@ +from .python_module.double import double +from .pyo3_mixed_py_subdir import get_21 + + +def get_42() -> int: + return double(get_21) diff --git a/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/python_module/__init__.py b/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/python_module/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/python_module/double.py b/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/python_module/double.py new file mode 100644 index 000000000..2eed18d52 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/python_module/double.py @@ -0,0 +1,5 @@ +from typing import Callable + + +def double(fn: Callable[[], int]) -> int: + return 2 * fn() diff --git a/test-crates/pyo3-mixed-py-subdir/src/lib.rs b/test-crates/pyo3-mixed-py-subdir/src/lib.rs new file mode 100644 index 000000000..e52a7d921 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/src/lib.rs @@ -0,0 +1,13 @@ +use pyo3::prelude::*; + +#[pyfunction] +fn get_21() -> usize { + 21 +} + +#[pymodule] +fn pyo3_mixed_py_subdir(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_wrapped(wrap_pyfunction!(get_21))?; + + Ok(()) +} diff --git a/test-crates/pyo3-mixed-py-subdir/test_pyo3_mixed.py b/test-crates/pyo3-mixed-py-subdir/test_pyo3_mixed.py new file mode 100644 index 000000000..d1f911557 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/test_pyo3_mixed.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +import pyo3_mixed_py_subdir as pyo3_mixed + + +def test_get_42(): + assert pyo3_mixed.get_42() == 42 diff --git a/test-crates/pyo3-mixed-py-subdir/tox.ini b/test-crates/pyo3-mixed-py-subdir/tox.ini new file mode 100644 index 000000000..fd19ca2b2 --- /dev/null +++ b/test-crates/pyo3-mixed-py-subdir/tox.ini @@ -0,0 +1,7 @@ +[tox] +envlist = py36,py37,py38 +isolated_build = True + +[testenv] +deps = pytest +commands = pytest \ No newline at end of file diff --git a/tests/run.rs b/tests/run.rs index c3b31549a..0ffb22f3b 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -22,6 +22,14 @@ fn develop_pyo3_mixed_submodule() { )); } +#[test] +fn develop_pyo3_mixed_py_subdir() { + handle_result(develop::test_develop( + "test-crates/pyo3-mixed-py-subdir", + None, + )); +} + #[test] fn develop_cffi_pure() { handle_result(develop::test_develop("test-crates/cffi-pure", None)); @@ -58,6 +66,14 @@ fn integration_pyo3_mixed_submodule() { )); } +#[test] +fn integration_pyo3_mixed_py_subdir() { + handle_result(integration::test_integration( + "test-crates/pyo3-mixed-py-subdir", + None, + )); +} + #[cfg(target_os = "windows")] #[test] #[ignore]