diff --git a/Cargo.toml b/Cargo.toml index f2f37654..0c3d22a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,16 @@ default = ["python3-sys"] # Enable additional features that require nightly rust nightly = [] +# Use this feature when building an extension module. +# It tells the linker to keep the python symbols unresolved, +# so that the module can also be used with statically linked python interpreters. +extension-module = [ "python3-sys/extension-module" ] + +# Unfortunately we can't use the forward the same feature to either python27-sys +# or python3-sys. (honestly, we should probably merge both crates into 'python-sys') +extension-module-2-7 = [ "python27-sys/extension-module" ] + + # Optional features to support explicitly specifying python minor version. # If you don't care which minor version, just specify python3-sys as a # feature. @@ -55,3 +65,6 @@ python-3-4 = ["python3-sys/python-3-4"] #pep-384 = ["python3-sys/pep-384"] +[workspace] +members = ["python27-sys", "python3-sys", "extensions/hello"] + diff --git a/Makefile b/Makefile index a0143345..11f3b24e 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ doc: build cargo doc --no-deps $(CARGO_FLAGS) extensions: build - make -C extensions/ PY=$(PY) + make -C extensions/tests PY=$(PY) clean: rm -r target diff --git a/extensions/hello/Cargo.toml b/extensions/hello/Cargo.toml new file mode 100644 index 00000000..ff0e1040 --- /dev/null +++ b/extensions/hello/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "hello" +version = "0.1.0" +authors = ["Daniel Grunwald "] + +[lib] +## Python extension modules should be compiled as 'cdylib' +crate-type = ["cdylib"] + +[dependencies.cpython] +path = "../.." +features = ["extension-module"] +# The 'extension-module' feature allows using the resulting binary module +# with statically linked python interpreters. + +## By default, cpython will use whichever Python 3.x interpreter is found in PATH. +## To target Python 2.7, use: +#default-features=false +#features = ["python27-sys", "extension-module-2-7"] + + diff --git a/extensions/hello/src/hello.rs b/extensions/hello/src/hello.rs new file mode 100644 index 00000000..d1b52488 --- /dev/null +++ b/extensions/hello/src/hello.rs @@ -0,0 +1,38 @@ +#[macro_use] extern crate cpython; + +use cpython::{PyObject, PyResult, Python, PyTuple, PyDict}; + +// Our module is named 'hello', and can be imported using `import hello`. +// This requires that the output binary file is named `hello.so` (or Windows: `hello.pyd`). +// As the output name cannot be configured in cargo (https://github.com/rust-lang/cargo/issues/1970), +// you'll have to rename the output file. +py_module_initializer!(hello, inithello, PyInit_hello, |py, m| { + m.add(py, "__doc__", "Module documentation string")?; + m.add(py, "func", py_fn!(py, func(a: &str, b: i32)))?; + m.add(py, "run", py_fn!(py, run(*args, **kwargs)))?; + Ok(()) +}); + +// The py_fn!()-macro can translate between Python and Rust values, +// so you can use `&str`, `i32` or `String` in the signature of a function +// callable from Python. +// The first argument of type `Python<'p>` is used to indicate that your +// function may assume that the current thread holds the global interpreter lock. +// Most functions in the `cpython` crate require that you pass this argument. +fn func(_: Python, a: &str, b: i32) -> PyResult { + Ok(format!("func({}, {})", a, b)) +} + +fn run(py: Python, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult { + println!("Rust says: Hello Python!"); + for arg in args.iter(py) { + println!("Rust got {}", arg); + } + if let Some(kwargs) = kwargs { + for (key, val) in kwargs.items(py) { + println!("{} = {}", key, val); + } + } + Ok(py.None()) +} + diff --git a/extensions/.gitignore b/extensions/tests/.gitignore similarity index 100% rename from extensions/.gitignore rename to extensions/tests/.gitignore diff --git a/extensions/Makefile b/extensions/tests/Makefile similarity index 89% rename from extensions/Makefile rename to extensions/tests/Makefile index 1b8cd933..722dca90 100644 --- a/extensions/Makefile +++ b/extensions/tests/Makefile @@ -1,4 +1,4 @@ -TARGETDIR=../target/debug +TARGETDIR=../../target/debug rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) .PHONY: all clean @@ -27,10 +27,10 @@ clean: stamps: mkdir stamps -stamps/rust-cpython-$(PY): $(call rwildcard,../src,*.rs) Makefile | stamps +stamps/rust-cpython-$(PY): $(call rwildcard,../../src,*.rs) Makefile | stamps -rm stamps/rust-cpython-* - cd .. && make build PY=$(PY) - touch $@ + cd ../.. && make build PY=$(PY) + touch "$@" %.so: %.rs stamps/rust-cpython-$(PY) rustc $< -g -L $(TARGETDIR) -L $(TARGETDIR)/deps -o $@ diff --git a/extensions/btree.rs b/extensions/tests/btree.rs similarity index 100% rename from extensions/btree.rs rename to extensions/tests/btree.rs diff --git a/extensions/custom_class.rs b/extensions/tests/custom_class.rs similarity index 100% rename from extensions/custom_class.rs rename to extensions/tests/custom_class.rs diff --git a/extensions/hello.rs b/extensions/tests/hello.rs similarity index 100% rename from extensions/hello.rs rename to extensions/tests/hello.rs diff --git a/extensions/test_btree.py b/extensions/tests/test_btree.py similarity index 100% rename from extensions/test_btree.py rename to extensions/tests/test_btree.py diff --git a/python27-sys/Cargo.toml b/python27-sys/Cargo.toml index 3bc03119..cea9b719 100644 --- a/python27-sys/Cargo.toml +++ b/python27-sys/Cargo.toml @@ -18,6 +18,7 @@ exclude = [ ".gitignore", ".travis.yml", ] +workspace = ".." [dependencies] libc = "0.2" @@ -36,3 +37,9 @@ regex = "0.1" # where supporting multiple 3.x's is more important. default = ["python-2-7"] python-2-7 = [] + +# Use this feature when building an extension module. +# It tells the linker to keep the python symbols unresolved, +# so that the module can also be used with statically linked python interpreters. +extension-module = [ ] + diff --git a/python27-sys/build.rs b/python27-sys/build.rs index cb364b18..c496cbce 100644 --- a/python27-sys/build.rs +++ b/python27-sys/build.rs @@ -297,13 +297,14 @@ fn configure_from_path(expected_version: &PythonVersion) -> Result Result Result>> import example -/// >>> example.run() +/// >>> import hello +/// >>> hello.run() /// Rust says: Hello Python! /// ``` ///