Skip to content

Commit

Permalink
Add support for packaging type stubs in pure Rust project layout
Browse files Browse the repository at this point in the history
  • Loading branch information
messense committed Jun 12, 2021
1 parent 83701d7 commit 229e65f
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 11 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Add PEP 656 musllinux support in [#543](https://github.com/PyO3/maturin/pull/543)
* `--manylinux` is now called `--compatibility` and supports musllinux
* The pure rust install layout changed from just the shared library to a python module that reexports the shared library. This should have now observable consequences for users of the created wheel expect that `my_project.my_project` is now also importable (and equal to just `my_project`)
* Add support for packaging type stubs in pure Rust project layout in [#567](https://github.com/PyO3/maturin/pull/567)

## 0.10.6 - 2021-05-21

Expand Down
27 changes: 19 additions & 8 deletions src/build_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ pub enum ProjectLayout {
/// A rust crate compiled into a shared library with only some glue python for cffi
///
/// Contains the the rust extension name
PureRust(String),
PureRust {
/// Contains the canonicialized (i.e. absolute) path to the rust part of the project
rust_module: PathBuf,
/// rust extension name
extension_name: String,
},
/// A python package that is extended by a native rust module.
Mixed {
/// Contains the canonicialized (i.e. absolute) path to the python part of the project
Expand All @@ -73,18 +78,19 @@ impl ProjectLayout {
pub fn determine(project_root: impl AsRef<Path>, module_name: &str) -> Result<ProjectLayout> {
// 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_module, rust_module, extension_name) = if parts.len() > 1 {
let mut rust_module = project_root.as_ref().to_path_buf();
let mut rust_module = project_root.to_path_buf();
rust_module.extend(&parts[0..parts.len() - 1]);
(
project_root.as_ref().join(parts[0]),
project_root.join(parts[0]),
rust_module,
parts[parts.len() - 1].to_string(),
)
} else {
(
project_root.as_ref().join(module_name),
project_root.as_ref().join(module_name),
project_root.join(module_name),
project_root.join(module_name),
module_name.to_string(),
)
};
Expand All @@ -101,13 +107,18 @@ impl ProjectLayout {
extension_name,
})
} else {
Ok(ProjectLayout::PureRust(extension_name))
Ok(ProjectLayout::PureRust {
rust_module: project_root.to_path_buf(),
extension_name,
})
}
}

pub fn extension_name(&self) -> &str {
match *self {
ProjectLayout::PureRust(ref name) => name,
ProjectLayout::PureRust {
ref extension_name, ..
} => extension_name,
ProjectLayout::Mixed {
ref extension_name, ..
} => extension_name,
Expand Down Expand Up @@ -435,7 +446,7 @@ impl BuildContext {
write_python_part(&mut builder, python_module, extension_name)
.context("Failed to add the python module to the package")?;
}
ProjectLayout::PureRust(_) => {}
ProjectLayout::PureRust { .. } => {}
}

// I wouldn't know of any case where this would be the wrong (and neither do
Expand Down
27 changes: 24 additions & 3 deletions src/module_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,14 +609,23 @@ pub fn write_bindings_module(
let relative = rust_module.strip_prefix(python_module.parent().unwrap())?;
writer.add_file_with_permissions(relative.join(&so_filename), &artifact, 0o755)?;
}
ProjectLayout::PureRust(_) => {
ProjectLayout::PureRust {
ref rust_module, ..
} => {
let module = PathBuf::from(module_name);
writer.add_directory(&module)?;
// Reexport the shared library as if it were the top level module
writer.add_bytes(
&module.join("__init__.py"),
format!("from .{} import *\n", module_name).as_bytes(),
)?;
let type_stub = rust_module.join(format!("{}.pyi", module_name));
if type_stub.exists() {
writer.add_bytes(
&module.join("__init__.pyi"),
&fs_err::read(type_stub).context("Failed to read type stub file")?,
)?;
}
writer.add_file_with_permissions(&module.join(so_filename), &artifact, 0o755)?;
}
}
Expand Down Expand Up @@ -663,11 +672,23 @@ pub fn write_cffi_module(

let relative = rust_module.strip_prefix(python_module.parent().unwrap())?;
module = relative.join(extension_name);
writer.add_directory(&module)?;
}
ProjectLayout::PureRust {
ref rust_module, ..
} => {
module = PathBuf::from(module_name);
writer.add_directory(&module)?;
let type_stub = rust_module.join(format!("{}.pyi", module_name));
if type_stub.exists() {
writer.add_bytes(
&module.join("__init__.pyi"),
&fs_err::read(type_stub).context("Failed to read type stub file")?,
)?;
}
}
ProjectLayout::PureRust(_) => module = PathBuf::from(module_name),
};

writer.add_directory(&module)?;
writer.add_bytes(&module.join("__init__.py"), cffi_init_file().as_bytes())?;
writer.add_bytes(&module.join("ffi.py"), cffi_declarations.as_bytes())?;
writer.add_file_with_permissions(&module.join("native.so"), &artifact, 0o755)?;
Expand Down
6 changes: 6 additions & 0 deletions test-crates/pyo3-pure/check_installed/check_installed.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#!/usr/bin/env python3
import os

import pyo3_pure

assert pyo3_pure.DummyClass.get_42() == 42

# Check type stub
type_stub = os.path.join(os.path.dirname(pyo3_pure.__file__), "__init__.pyi")
assert os.path.exists(type_stub)

print("SUCCESS")
5 changes: 5 additions & 0 deletions test-crates/pyo3-pure/pyo3_pure.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class DummyClass:
@staticmethod
def get_42(self) -> int: ...

fourtytwo: int

0 comments on commit 229e65f

Please sign in to comment.