From fe931a594f9facaa5562a55ee1c01cce62036c0c Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 10 Jul 2018 00:13:02 +0200 Subject: [PATCH] #[pymodinit] now uses the function name as default module name --- CHANGELOG.md | 3 ++- README.md | 4 +-- examples/word-count-cls/Cargo.toml | 2 +- examples/word-count-cls/setup.py | 2 +- examples/word-count-cls/src/lib.rs | 4 +-- .../word-count-cls/word_count_cls/__init__.py | 2 +- examples/word-count/setup.py | 2 +- examples/word-count/src/lib.rs | 4 +-- examples/word-count/word_count/__init__.py | 2 +- guide/src/function.md | 8 +++--- guide/src/module.md | 4 +-- guide/src/overview.md | 4 +-- guide/src/parallelism.md | 4 +-- pyo3-derive-backend/src/module.rs | 2 +- pyo3cls/src/lib.rs | 16 +++++++++--- src/lib.rs | 16 ++++-------- src/pythonrun.rs | 2 +- tests/test_module.rs | 25 ++++++++++++++++--- 18 files changed, 64 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc3ddc40..a3cbcfcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ * Upgraded to syn 0.14 which means much better error messages :tada: * 128 bit integer support by [kngwyu](https://github.com/kngwyu) ([#137](https://github.com/PyO3/pyo3/pull/173)) -* Added `py` prefixes to the proc macros and moved them into the root module. You should just use the plain proc macros, i.e. `#[pyclass]`, `#[pymethods]`, `#[pyproto]`, `#[pyfunction]` and `#[pymodinit]`. This is important because `proc_macro_path_invoc` isn't going to be stabilized soon. +* Added `py` prefixes to the proc macros and moved them into the root module. You should just use the plain proc macros, i.e. `#[pyclass]`, `#[pymethods]`, `#[pyproto]`, `#[pyfunction]` and `#[pymodinit]`. This is important because `proc_macro_path_invoc` isn't going to be stabilized soon. +* `#[pymodinit]` uses the function name as module name, unless the name is overrriden with `#[pymodinit(name)]` * The guide is now properly versioned. * A few internal macros became part of the public api ([#155](https://github.com/PyO3/pyo3/pull/155), [#186](https://github.com/PyO3/pyo3/pull/186)) * Always clone in getters. This allows using the get-annotation on all Clone-Types diff --git a/README.md b/README.md index aac13fe8..707ad1f5 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,8 @@ use pyo3::pymodinit; // Add bindings to the generated python module // N.B: names: "librust2py" must be the name of the `.so` or `.pyd` file /// This module is implemented in Rust. -#[pymodinit(rust2py)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn rust2py(py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m, "sum_as_string")] // ``#[pyfn()]` converts the arguments from Python objects to Rust values diff --git a/examples/word-count-cls/Cargo.toml b/examples/word-count-cls/Cargo.toml index 94171fe7..287563fe 100644 --- a/examples/word-count-cls/Cargo.toml +++ b/examples/word-count-cls/Cargo.toml @@ -10,5 +10,5 @@ rayon = "0.8" path = "../../" [lib] -name = "word_count" +name = "word_count_cls" crate-type = ["cdylib"] diff --git a/examples/word-count-cls/setup.py b/examples/word-count-cls/setup.py index c5a20629..4ad4bad1 100644 --- a/examples/word-count-cls/setup.py +++ b/examples/word-count-cls/setup.py @@ -44,7 +44,7 @@ setup( 'Operating System :: MacOS :: MacOS X', ], packages=['word_count_cls'], - rust_extensions=[RustExtension('word_count_cls._word_count', 'Cargo.toml')], + rust_extensions=[RustExtension('word_count_cls.word_count_cls', 'Cargo.toml')], install_requires=install_requires, tests_require=tests_require, setup_requires=setup_requires, diff --git a/examples/word-count-cls/src/lib.rs b/examples/word-count-cls/src/lib.rs index f7771538..637811cd 100644 --- a/examples/word-count-cls/src/lib.rs +++ b/examples/word-count-cls/src/lib.rs @@ -79,8 +79,8 @@ fn wc_parallel(lines: &str, search: &str) -> i32 { lines.par_lines().map(|line| wc_line(line, search)).sum() } -#[pymodinit(_word_count)] -fn init_mod(_py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn word_count(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; Ok(()) diff --git a/examples/word-count-cls/word_count_cls/__init__.py b/examples/word-count-cls/word_count_cls/__init__.py index e9abc928..ed9beb8f 100644 --- a/examples/word-count-cls/word_count_cls/__init__.py +++ b/examples/word-count-cls/word_count_cls/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -from ._word_count import WordCounter +from .word_count_cls import WordCounter __all__ = ['WordCounter', 'search_py'] diff --git a/examples/word-count/setup.py b/examples/word-count/setup.py index e1476fb3..13f9bf27 100644 --- a/examples/word-count/setup.py +++ b/examples/word-count/setup.py @@ -44,7 +44,7 @@ setup( 'Operating System :: MacOS :: MacOS X', ], packages=['word_count'], - rust_extensions=[RustExtension('word_count._word_count', 'Cargo.toml')], + rust_extensions=[RustExtension('word_count.word_count', 'Cargo.toml')], install_requires=install_requires, tests_require=tests_require, setup_requires=setup_requires, diff --git a/examples/word-count/src/lib.rs b/examples/word-count/src/lib.rs index 14e5b209..523082f7 100644 --- a/examples/word-count/src/lib.rs +++ b/examples/word-count/src/lib.rs @@ -50,8 +50,8 @@ fn wc_parallel(lines: &str, search: &str) -> i32 { lines.par_lines().map(|line| wc_line(line, search)).sum() } -#[pymodinit(_word_count)] -fn init_mod(_py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn word_count(_py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m, "search")] fn search(py: Python, path: String, search: String) -> PyResult { let mut file = File::open(path)?; diff --git a/examples/word-count/word_count/__init__.py b/examples/word-count/word_count/__init__.py index 3b1ae9af..ce843be6 100644 --- a/examples/word-count/word_count/__init__.py +++ b/examples/word-count/word_count/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -from ._word_count import search, search_sequential +from .word_count import search, search_sequential __all__ = ['search', 'search_sequential', 'search_py'] diff --git a/guide/src/function.md b/guide/src/function.md index ff4c5a7c..e86a1115 100644 --- a/guide/src/function.md +++ b/guide/src/function.md @@ -12,8 +12,8 @@ extern crate pyo3; use pyo3::prelude::*; use pyo3::pymodinit; -#[pymodinit(rust2py)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn rust2py(py: Python, m: &PyModule) -> PyResult<()> { // Note that the `#[pyfn()]` annotation automatically converts the arguments from // Python objects to Rust values; and the Rust return value back into a Python object. @@ -47,8 +47,8 @@ fn double(x: usize) -> usize { x * 2 } -#[pymodinit(module_with_functions)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_function!(double)).unwrap(); Ok(()) diff --git a/guide/src/module.md b/guide/src/module.md index 2bf4cbed..f30f0a81 100644 --- a/guide/src/module.md +++ b/guide/src/module.md @@ -13,8 +13,8 @@ use pyo3::pymodinit; // add bindings to the generated python module // N.B: names: "librust2py" must be the name of the `.so` or `.pyd` file /// This module is implemented in Rust. -#[pymodinit(rust2py)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn rust2py(py: Python, m: &PyModule) -> PyResult<()> { // pyo3 aware function. All of our python interface could be declared in a separate module. // Note that the `#[pyfn()]` annotation automatically converts the arguments from diff --git a/guide/src/overview.md b/guide/src/overview.md index 0b47166b..70f20b50 100644 --- a/guide/src/overview.md +++ b/guide/src/overview.md @@ -69,8 +69,8 @@ use pyo3::pymodinit; // Add bindings to the generated python module // N.B: names: "librust2py" must be the name of the `.so` or `.pyd` file /// This module is implemented in Rust. -#[pymodinit(rust2py)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn rust2py(py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m, "sum_as_string")] // ``#[pyfn()]` converts the arguments from Python objects to Rust values diff --git a/guide/src/parallelism.md b/guide/src/parallelism.md index 86a4f8d3..30348546 100644 --- a/guide/src/parallelism.md +++ b/guide/src/parallelism.md @@ -28,8 +28,8 @@ Then in the Python bridge, we have a function `search` exposed to Python runtime `Python::allow_threads` method to enable true parallelism: ```rust,ignore -#[pymodinit(_word_count)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn word_count(py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m, "search")] fn search(py: Python, path: String, search: String) -> PyResult { diff --git a/pyo3-derive-backend/src/module.rs b/pyo3-derive-backend/src/module.rs index aefde6bc..1a373d7e 100644 --- a/pyo3-derive-backend/src/module.rs +++ b/pyo3-derive-backend/src/module.rs @@ -92,7 +92,7 @@ pub fn py2_init(fnname: &syn::Ident, name: &syn::Ident, doc: syn::Lit) -> TokenS } } -/// Finds and takes care of the #[pyfn(...)] in #[pymodinit(...)] +/// Finds and takes care of the #[pyfn(...)] in #[pymodinit] pub fn process_functions_in_module(func: &mut syn::ItemFn) { let mut stmts: Vec = Vec::new(); diff --git a/pyo3cls/src/lib.rs b/pyo3cls/src/lib.rs index 1f7116da..875eac89 100644 --- a/pyo3cls/src/lib.rs +++ b/pyo3cls/src/lib.rs @@ -26,8 +26,12 @@ pub fn mod2init( // Parse the token stream into a syntax tree let mut ast: syn::ItemFn = syn::parse(input).expect("#[pymodinit] must be used on a function"); - // Extract the mod name - let modname: syn::Ident = syn::parse(attr).expect("could not parse module name"); + let modname: syn::Ident; + if attr.is_empty() { + modname = ast.ident.clone(); + } else { + modname = syn::parse(attr).expect("could not parse module name"); + } // Process the functions within the module module::process_functions_in_module(&mut ast); @@ -49,8 +53,12 @@ pub fn mod3init( // Parse the token stream into a syntax tree let mut ast: syn::ItemFn = syn::parse(input).expect("#[pymodinit] must be used on a `fn` block"); - // Extract the mod name - let modname: syn::Ident = syn::parse(attr).expect("could not parse module name"); + let modname: syn::Ident; + if attr.is_empty() { + modname = ast.ident.clone(); + } else { + modname = syn::parse(attr).expect("could not parse module name"); + } // Process the functions within the module module::process_functions_in_module(&mut ast); diff --git a/src/lib.rs b/src/lib.rs index 1bbe8612..514c94f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,15 +53,9 @@ //! # Python extension //! //! To allow Python to load the rust code as a Python extension -//! module, you need provide initialization function and annotate it with `#[pymodinit(name)]`. -//! `pymodinit` expands to an `extern "C"` function. -//! -//! Macro syntax: `#[pymodinit(name)]` -//! -//! 1. `name`: The module name as a Rust identifier -//! 2. Decorate init function `Fn(Python, &PyModule) -> PyResult<()>`. -//! This function will be called when the module is imported, and is responsible -//! for adding the module's members. +//! module, you need an initialization function with `Fn(Python, &PyModule) -> PyResult<()>` +//! that is annotates with `#[pymodinit]`. By default the function name will become the module name, +//! but you can override that with `#[pymodinit(name)]`. //! //! To creates a Python callable object that invokes a Rust function, specify rust //! function and decorate it with `#[pyfn()]` attribute. `pyfn()` accepts three parameters. @@ -84,8 +78,8 @@ //! // Add bindings to the generated python module //! // N.B: names: "librust2py" must be the name of the `.so` or `.pyd` file //! /// This module is implemented in Rust. -//! #[pymodinit(rust2py)] -//! fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +//! #[pymodinit] +//! fn rust2py(py: Python, m: &PyModule) -> PyResult<()> { //! //! #[pyfn(m, "sum_as_string")] //! // ``#[pyfn()]` converts the arguments from Python objects to Rust values diff --git a/src/pythonrun.rs b/src/pythonrun.rs index 9d375006..1965b899 100644 --- a/src/pythonrun.rs +++ b/src/pythonrun.rs @@ -27,7 +27,7 @@ static START_PYO3: sync::Once = sync::ONCE_INIT; /// thread (the thread which originally initialized Python) also initializes /// threading. /// -/// When writing an extension module, the `#[pymodinit(..)]` macro +/// When writing an extension module, the `#[pymodinit]` macro /// will ensure that Python threading is initialized. /// pub fn prepare_freethreaded_python() { diff --git a/tests/test_module.rs b/tests/test_module.rs index 99ae7c27..547fd103 100644 --- a/tests/test_module.rs +++ b/tests/test_module.rs @@ -19,8 +19,8 @@ fn double(x: usize) -> usize { } /// This module is implemented in Rust. -#[pymodinit(module_with_functions)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pymodinit] +fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m, "sum_as_string")] fn sum_as_string_py(_py: Python, a: i64, b: i64) -> PyResult { let out = sum_as_string(a, b); @@ -64,6 +64,25 @@ fn test_module_with_functions() { run("assert module_with_functions.also_double(3) == 6"); } +#[pymodinit(other_name)] +fn some_name(_: Python, _: &PyModule) -> PyResult<()> { + Ok(()) +} + +#[test] +#[cfg(Py_3)] +fn test_module_renaming() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let d = PyDict::new(py); + d.set_item("different_name", unsafe { + PyObject::from_owned_ptr(py, PyInit_other_name()) + }).unwrap(); + + py.run("assert different_name.__name__ == 'other_name'", None, Some(d)).unwrap(); +} + #[test] #[cfg(Py_3)] fn test_module_from_code() { @@ -89,4 +108,4 @@ fn test_module_from_code() { .expect("The value should be able to be converted to an i32"); assert_eq!(ret_value, 3); -} +} \ No newline at end of file