Many small improvements

This commit is contained in:
konstin 2018-08-04 19:55:15 +02:00
parent 55d0917419
commit d0ed68414a
15 changed files with 77 additions and 64 deletions

View file

@ -4,21 +4,23 @@ language: python
cache: cache:
pip: true pip: true
cargo: true directories:
- $HOME/.cargo
- $TRAVIS_BUILD_DIR/target
matrix: matrix:
include: include:
- python: "2.7" - python: "2.7"
env: FEATURES=python2 env: FEATURES=python2
- python: "3.5" - python: "3.5"
env: FEATURES=python3 env: FEATURES=python3
- python: "3.6" - python: "3.6"
env: FEATURES=python3 env: FEATURES=python3
- python: "3.7-dev" - python: "3.7-dev"
env: FEATURES=python3 env: FEATURES=python3
allow_failures: allow_failures:
- python: "3.7-dev" - python: "3.7-dev"
env: FEATURES=python3 env: FEATURES=python3
env: env:
global: global:

View file

@ -15,22 +15,22 @@ build = "build.rs"
[badges] [badges]
travis-ci = { repository = "PyO3/pyo3", branch = "master" } travis-ci = { repository = "PyO3/pyo3", branch = "master" }
appveyor = { repository = "PyO3/pyo3" } appveyor = { repository = "fafhrd91/pyo3" }
codecov = { repository = "PyO3/pyo3", branch = "master", service = "github" } codecov = { repository = "PyO3/pyo3", branch = "master", service = "github" }
[dependencies] [dependencies]
libc = "0.2" libc = "0.2.42"
spin = "0.4.6" spin = "0.4.8"
num-traits = "0.2" num-traits = "0.2.5"
pyo3cls = { path = "pyo3cls", version = "0.4.0" } pyo3cls = { path = "pyo3cls", version = "0.4.0" }
mashup = "0.1.5" mashup = "0.1.5"
[dev-dependencies] [dev-dependencies]
docmatic = "^0.1.2" docmatic = "0.1.2"
[build-dependencies] [build-dependencies]
regex = "1.0" regex = "1.0.2"
version_check = "0.1.3" version_check = "0.1.4"
[features] [features]
default = [] default = []

View file

@ -1,7 +1,7 @@
# PyO3 # PyO3
[![Build Status](https://travis-ci.org/PyO3/pyo3.svg?branch=master)](https://travis-ci.org/PyO3/pyo3) [![Build Status](https://travis-ci.org/PyO3/pyo3.svg?branch=master)](https://travis-ci.org/PyO3/pyo3)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/PyO3/pyo3?branch=master&svg=true)](https://ci.appveyor.com/project/fafhrd91/pyo3) [![Build Status](https://ci.appveyor.com/api/projects/status/github/fafhrd91/pyo3?branch=master&svg=true)](https://ci.appveyor.com/project/fafhrd91/pyo3)
[![codecov](https://codecov.io/gh/PyO3/pyo3/branch/master/graph/badge.svg)](https://codecov.io/gh/PyO3/pyo3) [![codecov](https://codecov.io/gh/PyO3/pyo3/branch/master/graph/badge.svg)](https://codecov.io/gh/PyO3/pyo3)
[![crates.io](http://meritbadge.herokuapp.com/pyo3)](https://crates.io/crates/pyo3) [![crates.io](http://meritbadge.herokuapp.com/pyo3)](https://crates.io/crates/pyo3)
[![Join the dev chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/PyO3/Lobby) [![Join the dev chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/PyO3/Lobby)

View file

@ -6,6 +6,7 @@ use std::collections::HashMap;
use std::env; use std::env;
use std::fmt; use std::fmt;
use std::process::Command; use std::process::Command;
use std::process::Stdio;
use version_check::{is_min_date, is_min_version, supports_features}; use version_check::{is_min_date, is_min_version, supports_features};
// Specifies the minimum nightly version needed to compile pyo3. // Specifies the minimum nightly version needed to compile pyo3.
@ -172,12 +173,12 @@ fn cfg_line_for_var(key: &str, val: &str) -> Option<String> {
fn run_python_script(interpreter: &str, script: &str) -> Result<String, String> { fn run_python_script(interpreter: &str, script: &str) -> Result<String, String> {
let out = Command::new(interpreter) let out = Command::new(interpreter)
.args(&["-c", script]) .args(&["-c", script])
.stderr(Stdio::inherit())
.output() .output()
.map_err(|e| format!("failed to run python interpreter:\n\n{}", e))?; .map_err(|e| format!("failed to run python interpreter:\n\n{}", e))?;
if !out.status.success() { if !out.status.success() {
let stderr = String::from_utf8(out.stderr).unwrap(); return Err(format!("python script failed"));
return Err(format!("python script failed with stderr:\n\n{}", stderr));
} }
Ok(String::from_utf8(out.stdout).unwrap()) Ok(String::from_utf8(out.stdout).unwrap())
@ -306,11 +307,16 @@ fn find_interpreter_and_get_config(
/// Extract compilation vars from the specified interpreter. /// Extract compilation vars from the specified interpreter.
fn get_config_from_interpreter(interpreter: &str) -> Result<(PythonVersion, Vec<String>), String> { fn get_config_from_interpreter(interpreter: &str) -> Result<(PythonVersion, Vec<String>), String> {
let script = "import sys; import sysconfig; print(sys.version_info[0:2]); \ let script = r#"
print(sysconfig.get_config_var('LIBDIR')); \ import sys
print(sysconfig.get_config_var('Py_ENABLE_SHARED')); \ import sysconfig
print(sysconfig.get_config_var('LDVERSION') or sysconfig.get_config_var('py_version_short')); \
print(sys.exec_prefix);"; print(sys.version_info[0:2])
print(sysconfig.get_config_var('LIBDIR'))
print(sysconfig.get_config_var('Py_ENABLE_SHARED'))
print(sysconfig.get_config_var('LDVERSION') or sysconfig.get_config_var('py_version_short'))
print(sys.exec_prefix)
"#;
let out = run_python_script(interpreter, script)?; let out = run_python_script(interpreter, script)?;
let lines: Vec<String> = out.lines().map(|line| line.to_owned()).collect(); let lines: Vec<String> = out.lines().map(|line| line.to_owned()).collect();
let interpreter_version = get_interpreter_version(&lines[0])?; let interpreter_version = get_interpreter_version(&lines[0])?;

View file

@ -38,13 +38,7 @@ fi
### Setup python linker flags ################################################## ### Setup python linker flags ##################################################
PYTHON_LIB=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))") PYTHON_LIB=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
LIBRARY_PATH="$LIBRARY_PATH:$PYTHON_LIB"
# delete any possible empty components export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PYTHON_LIB:$HOME/rust/lib"
# https://github.com/google/pulldown-cmark/issues/122#issuecomment-364948741
LIBRARY_PATH=$(echo ${LIBRARY_PATH} | sed -E -e 's/^:*//' -e 's/:*$//' -e 's/:+/:/g')
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$LIBRARY_PATH:$HOME/rust/lib"
python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"
echo ${LD_LIBRARY_PATH} echo ${LD_LIBRARY_PATH}

View file

@ -4,7 +4,7 @@ name = "word-count"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
rayon = "1.0" rayon = "1.0.2"
pyo3 = { path = "../..", features = ["extension-module"] } pyo3 = { path = "../..", features = ["extension-module"] }
[lib] [lib]

View file

@ -0,0 +1,2 @@
[build-system]
requires = ["setuptools", "wheel", "setuptools-rust"]

View file

@ -25,8 +25,7 @@ class PyTest(TestCommand):
import subprocess import subprocess
import sys import sys
errno = subprocess.call([sys.executable, "-m", "pytest", "tests"]) subprocess.check_call([sys.executable, "-m", "pytest", "tests"])
raise SystemExit(errno)
setup_requires = ["setuptools-rust>=0.10.1", "wheel"] setup_requires = ["setuptools-rust>=0.10.1", "wheel"]

View file

@ -54,3 +54,16 @@ fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> {
# fn main() {} # fn main() {}
``` ```
## Closures
Currently, there are no conversions between `Fn`s in rust and callables in python. This would definitely be possible and very useful, so contributions are welcome. In the meantime, you can do the following:
### Calling a python function in rust
You can use `ObjectProtocol::is_callable` to check if you got a callable, which is true for functions (including lambdas), methods and objects with a `__call__` method. You can call the object with `ObjectProtocol::call` with the args as first parameter and the kwargs (or `NoArgs`) as second paramter. There are also `ObjectProtocol::call0` with no args and `ObjectProtocol::call1` with only the args.
### Calling rust `Fn`s in python
If you have a static function, you can expose it with `#[pyfunction]` and use `wrap_function!` to get the corresponding `PyObject`. For dynamic functions, e.g. lambda and functions that were passed as arguments, you must put them in some kind of owned container, e.g. a box. (Long-Term a special container similar to wasm-bindgen's `Closure` should take care of that). You can than use a `#[pyclass]` struct with that container as field as a way to pass the function over the ffi-barrier. You can even make that class callable with `__call__` so it looks like a function in python code.

View file

@ -11,13 +11,13 @@ categories = ["api-bindings", "development-tools::ffi"]
license = "Apache-2.0" license = "Apache-2.0"
[dependencies] [dependencies]
log="0.4" log= "0.4.3"
quote="0.6" quote= "0.6.4"
[dependencies.proc-macro2] [dependencies.proc-macro2]
version = "0.4.9" version = "0.4.9"
features = ["nightly"] features = ["nightly"]
[dependencies.syn] [dependencies.syn]
version="0.14" version= "0.14.5"
features=["full", "parsing", "printing", "extra-traits"] features=["full", "parsing", "printing", "extra-traits"]

View file

@ -61,7 +61,7 @@ pub fn py2_init(fnname: &syn::Ident, name: &syn::Ident, doc: syn::Lit) -> TokenS
quote! { quote! {
#[no_mangle] #[no_mangle]
#[allow(non_snake_case, unused_imports)] #[allow(non_snake_case)]
pub unsafe extern "C" fn #cb_name() { pub unsafe extern "C" fn #cb_name() {
// initialize python // initialize python
::pyo3::init_once(); ::pyo3::init_once();
@ -281,7 +281,6 @@ fn function_c_wrapper(name: &syn::Ident, spec: &method::FnSpec) -> TokenStream {
let body_to_result = py_method::body_to_result(&body, spec); let body_to_result = py_method::body_to_result(&body, spec);
quote! { quote! {
#[allow(unused_variables, unused_imports)]
unsafe extern "C" fn __wrap( unsafe extern "C" fn __wrap(
_slf: *mut ::pyo3::ffi::PyObject, _slf: *mut ::pyo3::ffi::PyObject,
_args: *mut ::pyo3::ffi::PyObject, _args: *mut ::pyo3::ffi::PyObject,

View file

@ -14,10 +14,10 @@ license = "Apache-2.0"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
quote="0.6" quote= "0.6.4"
[dependencies.syn] [dependencies.syn]
version="0.14" version= "0.14.5"
features=["full", "parsing", "printing", "extra-traits"] features=["full", "parsing", "printing", "extra-traits"]
[dependencies.pyo3-derive-backend] [dependencies.pyo3-derive-backend]

View file

@ -98,21 +98,21 @@ mod tupleobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3
mod enumobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod enumobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod methodobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod methodobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod moduleobject; mod moduleobject;
mod setobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod setobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
// mod funcobject; TODO excluded by PEP-384 // mod funcobject; TODO excluded by PEP-384
// mod classobject; TODO excluded by PEP-384 // mod classobject; TODO excluded by PEP-384
mod fileobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod fileobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod pycapsule; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod pycapsule; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod sliceobject; mod sliceobject;
mod traceback; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod traceback; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
// mod cellobject; TODO excluded by PEP-384 // mod cellobject; TODO excluded by PEP-384
mod descrobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod descrobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod genobject; // TODO excluded by PEP-384 mod genobject; // TODO excluded by PEP-384
mod iterobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod iterobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod structseq; mod structseq;
mod warnings; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod warnings; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod weakrefobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod weakrefobject; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
// mod namespaceobject; TODO // mod namespaceobject; TODO
mod codecs; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod codecs; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod pyerrors; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod pyerrors; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
@ -131,7 +131,7 @@ mod import;
mod intrcheck; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod intrcheck; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
#[cfg(Py_3_6)] #[cfg(Py_3_6)]
mod osmodule; mod osmodule;
mod sysmodule; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod sysmodule; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod bltinmodule; mod bltinmodule;
mod objectabstract; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod objectabstract; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5

View file

@ -14,7 +14,6 @@ use python::{IntoPyDictPointer, IntoPyPointer, Python, ToPyPointer};
use typeob::PyTypeInfo; use typeob::PyTypeInfo;
/// Python object model helper methods /// Python object model helper methods
pub trait ObjectProtocol { pub trait ObjectProtocol {
/// Determines whether this object has the given attribute. /// Determines whether this object has the given attribute.
/// This is equivalent to the Python expression 'hasattr(self, attr_name)'. /// This is equivalent to the Python expression 'hasattr(self, attr_name)'.
@ -517,14 +516,14 @@ where
FromPyObject::extract(self.into()) FromPyObject::extract(self.into())
} }
fn get_refcnt(&self) -> isize {
unsafe { ffi::Py_REFCNT(self.as_ptr()) }
}
#[allow(non_snake_case)] // the Python keyword starts with uppercase #[allow(non_snake_case)] // the Python keyword starts with uppercase
fn None(&self) -> PyObject { fn None(&self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self.py(), ffi::Py_None()) } unsafe { PyObject::from_borrowed_ptr(self.py(), ffi::Py_None()) }
} }
fn get_refcnt(&self) -> isize {
unsafe { ffi::Py_REFCNT(self.as_ptr()) }
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -2,11 +2,6 @@
// //
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
use std;
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::c_int;
use conversion::PyTryFrom; use conversion::PyTryFrom;
use err::{PyDowncastError, PyErr, PyResult}; use err::{PyDowncastError, PyErr, PyResult};
use ffi; use ffi;
@ -14,6 +9,10 @@ use instance::{AsPyRef, Py, PyToken};
use object::PyObject; use object::PyObject;
use objects::{PyDict, PyModule, PyObjectRef, PyType}; use objects::{PyDict, PyModule, PyObjectRef, PyType};
use pythonrun::{self, GILGuard}; use pythonrun::{self, GILGuard};
use std;
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::c_int;
use typeob::{PyObjectAlloc, PyTypeInfo, PyTypeObject}; use typeob::{PyObjectAlloc, PyTypeInfo, PyTypeObject};
/// Marker type that indicates that the GIL is currently held. /// Marker type that indicates that the GIL is currently held.
@ -294,10 +293,11 @@ impl<'p> Python<'p> {
where where
T: PyTypeInfo, T: PyTypeInfo,
{ {
let p;
unsafe { unsafe {
let p = pythonrun::register_owned(self, obj.into_ptr()); p = pythonrun::register_owned(self, obj.into_ptr());
<T as PyTryFrom>::try_from(p)
} }
<T as PyTryFrom>::try_from(p)
} }
/// Register object in release pool, and do unchecked downcast to specific type. /// Register object in release pool, and do unchecked downcast to specific type.
@ -463,7 +463,6 @@ impl<'p> Python<'p> {
/// Release `ffi::PyObject` pointer. /// Release `ffi::PyObject` pointer.
/// Undefined behavior if the pointer is invalid. /// Undefined behavior if the pointer is invalid.
#[inline] #[inline]
pub fn xdecref(self, ptr: *mut ffi::PyObject) { pub fn xdecref(self, ptr: *mut ffi::PyObject) {
if !ptr.is_null() { if !ptr.is_null() {
unsafe { ffi::Py_DECREF(ptr) }; unsafe { ffi::Py_DECREF(ptr) };