This commit is contained in:
konstin 2018-08-11 17:35:03 +02:00
parent 7ebbbe41db
commit b12b65cfae
17 changed files with 158 additions and 134 deletions

View File

@ -1,12 +1,8 @@
sudo: required
dist: trusty
language: python
cache:
pip: true
directories:
- $HOME/.cargo
- $TRAVIS_BUILD_DIR/target
cargo: true
matrix:
include:

View File

@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
* PyTryFrom's error is always to `PyDowncastError`
### Removed
* The pyobject_downcast macro
## [0.4.0] - 2018-07-30
### Removed

View File

@ -1,7 +1,7 @@
# 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/fafhrd91/pyo3?branch=master&svg=true)](https://ci.appveyor.com/project/fafhrd91/pyo3)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/PyO3/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)
[![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)
@ -64,7 +64,7 @@ fn rust_py(py: Python, m: &PyModule) -> PyResult<()> {
}
```
On windows and linux, you can build normally with `cargo build --release`. On Mac Os, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
On windows and linux, you can build normally with `cargo build --release`. On Mac Os, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
```toml
[target.x86_64-apple-darwin]
@ -117,6 +117,7 @@ fn main() -> PyResult<()> {
* [hyperjson](https://github.com/mre/hyperjson) _A hyper-fast Python module for reading/writing JSON data using Rust's serde-json_
* [rust-numpy](https://github.com/rust-numpy/rust-numpy) _Rust binding of NumPy C-API_
* [pyo3-built](https://github.com/PyO3/pyo3-built) _Simple macro to expose metadata obtained with the [`built`](https://crates.io/crates/built) crate as a [`PyDict`](https://pyo3.github.io/pyo3/pyo3/struct.PyDict.html)_
* [point-process](https://github.com/ManifoldFR/point-process-rust/tree/master/pylib) _High level API for pointprocesses as a Python library_
## License

View File

@ -200,8 +200,16 @@ fn get_rustc_link_lib(
#[cfg(target_os = "macos")]
fn get_macos_linkmodel() -> Result<String, String> {
let script = "import sysconfig;\
print('framework' if sysconfig.get_config_var('PYTHONFRAMEWORK') else ('shared' if sysconfig.get_config_var('Py_ENABLE_SHARED') else 'static'));";
let script = r#"
import sysconfig
if sysconfig.get_config_var("PYTHONFRAMEWORK"):
print("framework")
elif sysconfig.get_config_var("Py_ENABLE_SHARED"):
print("shared")
else:
print("static")
"#;
let out = run_python_script("python", script).unwrap();
Ok(out.trim_right().to_owned())
}
@ -520,6 +528,10 @@ fn main() {
}
);
if env::var_os("TARGET") == Some("x86_64-apple-darwin".into()) {
// TODO: Find out how we can set -undefined dynamic_lookup here (if this is possible)
}
let env_vars = ["LD_LIBRARY_PATH", "PATH", "PYTHON_SYS_EXECUTABLE"];
for var in env_vars.iter() {

View File

@ -1,23 +1,86 @@
# PyO3
[Rust](http://www.rust-lang.org/) bindings for the [Python](https://www.python.org/) interpreter. This includes running and interacting with python code from a rust binaries as well as writing native python modules.
[Rust](http://www.rust-lang.org/) bindings for [Python](https://www.python.org/). This includes running and interacting with python code from a rust binaries as well as writing native python modules.
## Usage
Pyo3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.27.0-nightly 2018-05-01.
Pyo3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.29.0-nightly 2018-07-16.
### From a rust binary
You can either write a native python module in rust or use python from a rust binary.
To use `pyo3`, add this to your `Cargo.toml`:
### Using rust from python
Pyo3 can be used to generate a native python module.
**`Cargo.toml`:**
```toml
[package]
name = "rust-py"
version = "0.1.0"
[lib]
name = "rust_py"
crate-type = ["cdylib"]
[dependencies.pyo3]
version = "0.3"
features = ["extension-module"]
```
**`src/lib.rs`**
```rust
#![feature(use_extern_macros, specialization)]
#[macro_use]
extern crate pyo3;
use pyo3::prelude::*;
#[pyfunction]
/// Formats the sum of two numbers as string
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
/// This module is a python moudle implemented in Rust.
#[pymodinit]
fn rust_py(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_function!(sum_as_string))?;
Ok(())
}
```
On windows and linux, you can build normally with `cargo build --release`. On Mac Os, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
```toml
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
```
Also on macOS, you will need to rename the output from \*.dylib to \*.so. On Windows, you will need to rename the output from \*.dll to \*.pyd.
[`setuptools-rust`](https://github.com/PyO3/setuptools-rust) can be used to generate a python package and includes the commands above by default. See [examples/word-count](examples/https://github.com/PyO3/pyo3/tree/master/examples/word-count) and the associated setup.py.
### Using python from rust
Add `pyo3` this to your `Cargo.toml`:
```toml
[dependencies]
pyo3 = "0.2"
pyo3 = "0.3"
```
Example program displaying the value of `sys.version`:
```rust
#![feature(use_extern_macros, specialization)]
extern crate pyo3;
use pyo3::prelude::*;
@ -37,57 +100,10 @@ fn main() -> PyResult<()> {
}
```
### As native module
## Examples and tooling
Pyo3 can be used to write native python module. The example will generate a python-compatible library.
For MacOS, "-C link-arg=-undefined -C link-arg=dynamic_lookup" is required to build the library.
`setuptools-rust` includes this by default. See [examples/word-count](examples/word-count) and the associated setup.py. Also on macOS, you will need to rename the output from \*.dylib to \*.so. On Windows, you will need to rename the output from \*.dll to \*.pyd.
**`Cargo.toml`:**
```toml
[lib]
name = "rust2py"
crate-type = ["cdylib"]
[dependencies.pyo3]
version = "0.2"
features = ["extension-module"]
```
**`src/lib.rs`**
```rust
#![feature(use_extern_macros, specialization)]
extern crate pyo3;
use pyo3::prelude::*;
// 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]
fn rust2py(py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "sum_as_string")]
// ``#[pyfn()]` converts the arguments from Python objects to Rust values
// and the Rust return value back into a Python object.
fn sum_as_string_py(a:i64, b:i64) -> PyResult<String> {
let out = sum_as_string(a, b);
Ok(out)
}
Ok(())
}
// The logic can be implemented as a normal rust function
fn sum_as_string(a:i64, b:i64) -> String {
format!("{}", a + b).to_string()
}
```
For `setup.py` integration, see [setuptools-rust](https://github.com/PyO3/setuptools-rust)
* [examples/word-count](https://github.com/PyO3/pyo3/tree/master/examples/word-count) _Counting the occurences of a word in a text file_
* [hyperjson](https://github.com/mre/hyperjson) _A hyper-fast Python module for reading/writing JSON data using Rust's serde-json_
* [rust-numpy](https://github.com/rust-numpy/rust-numpy) _Rust binding of NumPy C-API_
* [pyo3-built](https://github.com/PyO3/pyo3-built) _Simple macro to expose metadata obtained with the [`built`](https://crates.io/crates/built) crate as a [`PyDict`](https://pyo3.github.io/pyo3/pyo3/struct.PyDict.html)_
* [point-process](https://github.com/ManifoldFR/point-process-rust/tree/master/pylib) _High level API for pointprocesses as a Python library_

View File

@ -136,14 +136,14 @@ fn impl_class(
}
impl std::fmt::Debug for #cls {
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
use pyo3::ObjectProtocol;
use ::pyo3::ObjectProtocol;
let s = try!(self.repr().map_err(|_| std::fmt::Error));
f.write_str(&s.to_string_lossy())
}
}
impl std::fmt::Display for #cls {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
use pyo3::ObjectProtocol;
use ::pyo3::ObjectProtocol;
let s = try!(self.str().map_err(|_| std::fmt::Error));
f.write_str(&s.to_string_lossy())
}

View File

@ -318,7 +318,7 @@ macro_rules! py_func_set {
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> $crate::c_int
) -> $crate::libc::c_int
where
T: for<'p> $trait<'p>,
{
@ -368,7 +368,7 @@ macro_rules! py_func_del {
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> $crate::c_int
) -> $crate::libc::c_int
where
T: for<'p> $trait<'p>,
{
@ -414,7 +414,7 @@ macro_rules! py_func_set_del {
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> $crate::c_int
) -> $crate::libc::c_int
where
T: for<'p> $trait<'p> + for<'p> $trait2<'p>,
{

View File

@ -4,6 +4,7 @@ use std;
use std::ffi::CString;
use ffi;
use libc::c_int;
static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
@ -42,7 +43,7 @@ pub enum PyMethodType {
pub struct PyMethodDef {
pub ml_name: &'static str,
pub ml_meth: PyMethodType,
pub ml_flags: ::c_int,
pub ml_flags: c_int,
pub ml_doc: &'static str,
}

View File

@ -155,18 +155,18 @@ where
/// Extract reference to instance from `PyObject`
impl<'a, T> FromPyObject<'a> for &'a T
where
T: PyTypeInfo,
T: PyTryFrom,
{
#[inline]
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a T> {
Ok(<T as PyTryFrom>::try_from(ob)?)
Ok(T::try_from(ob)?)
}
}
/// Extract mutable reference to instance from `PyObject`
impl<'a, T> FromPyObject<'a> for &'a mut T
where
T: PyTypeInfo,
T: PyTryFrom,
{
#[inline]
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a mut T> {
@ -212,20 +212,17 @@ pub trait PyTryInto<T>: Sized {
/// Trait implemented by Python object types that allow a checked downcast.
/// This trait is similar to `std::convert::TryFrom`
pub trait PyTryFrom: Sized {
/// The type returned in the event of a conversion error.
type Error;
/// Cast from a concrete Python object type to PyObject.
fn try_from(value: &PyObjectRef) -> Result<&Self, Self::Error>;
fn try_from(value: &PyObjectRef) -> Result<&Self, PyDowncastError>;
/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_exact(value: &PyObjectRef) -> Result<&Self, Self::Error>;
fn try_from_exact(value: &PyObjectRef) -> Result<&Self, PyDowncastError>;
/// Cast from a concrete Python object type to PyObject.
fn try_from_mut(value: &PyObjectRef) -> Result<&mut Self, Self::Error>;
fn try_from_mut(value: &PyObjectRef) -> Result<&mut Self, PyDowncastError>;
/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut Self, Self::Error>;
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut Self, PyDowncastError>;
}
// TryFrom implies TryInto
@ -233,18 +230,18 @@ impl<U> PyTryInto<U> for PyObjectRef
where
U: PyTryFrom,
{
type Error = U::Error;
type Error = PyDowncastError;
fn try_into(&self) -> Result<&U, U::Error> {
fn try_into(&self) -> Result<&U, PyDowncastError> {
U::try_from(self)
}
fn try_into_exact(&self) -> Result<&U, U::Error> {
fn try_into_exact(&self) -> Result<&U, PyDowncastError> {
U::try_from_exact(self)
}
fn try_into_mut(&self) -> Result<&mut U, U::Error> {
fn try_into_mut(&self) -> Result<&mut U, PyDowncastError> {
U::try_from_mut(self)
}
fn try_into_mut_exact(&self) -> Result<&mut U, U::Error> {
fn try_into_mut_exact(&self) -> Result<&mut U, PyDowncastError> {
U::try_from_mut_exact(self)
}
}
@ -253,9 +250,7 @@ impl<T> PyTryFrom for T
where
T: PyTypeInfo,
{
type Error = PyDowncastError;
fn try_from(value: &PyObjectRef) -> Result<&T, Self::Error> {
fn try_from(value: &PyObjectRef) -> Result<&T, PyDowncastError> {
unsafe {
if T::is_instance(value.as_ptr()) {
let ptr = if T::OFFSET == 0 {
@ -270,7 +265,7 @@ where
}
}
fn try_from_exact(value: &PyObjectRef) -> Result<&T, Self::Error> {
fn try_from_exact(value: &PyObjectRef) -> Result<&T, PyDowncastError> {
unsafe {
if T::is_exact_instance(value.as_ptr()) {
let ptr = if T::OFFSET == 0 {
@ -285,7 +280,7 @@ where
}
}
fn try_from_mut(value: &PyObjectRef) -> Result<&mut T, Self::Error> {
fn try_from_mut(value: &PyObjectRef) -> Result<&mut T, PyDowncastError> {
unsafe {
if T::is_instance(value.as_ptr()) {
let ptr = if T::OFFSET == 0 {
@ -300,7 +295,7 @@ where
}
}
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut T, Self::Error> {
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut T, PyDowncastError> {
unsafe {
if T::is_exact_instance(value.as_ptr()) {
let ptr = if T::OFFSET == 0 {

View File

@ -6,6 +6,7 @@ use std;
use err::PyResult;
use ffi;
use python::Python;
use std::os::raw::c_void;
use typeob::{PyObjectAlloc, PyTypeInfo};
/// Implementing this trait for custom class adds free allocation list to class.
@ -92,13 +93,13 @@ where
if let Some(obj) = <T as PyObjectWithFreeList>::get_free_list().insert(obj) {
match (*T::type_object()).tp_free {
Some(free) => free(obj as *mut ::c_void),
Some(free) => free(obj as *mut c_void),
None => {
let ty = ffi::Py_TYPE(obj);
if ffi::PyType_IS_GC(ty) != 0 {
ffi::PyObject_GC_Del(obj as *mut ::c_void);
ffi::PyObject_GC_Del(obj as *mut c_void);
} else {
ffi::PyObject_Free(obj as *mut ::c_void);
ffi::PyObject_Free(obj as *mut c_void);
}
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
@ -117,13 +118,13 @@ where
if let Some(obj) = <T as PyObjectWithFreeList>::get_free_list().insert(obj) {
match (*T::type_object()).tp_free {
Some(free) => free(obj as *mut ::c_void),
Some(free) => free(obj as *mut c_void),
None => {
let ty = ffi::Py_TYPE(obj);
if ffi::PyType_IS_GC(ty) != 0 {
ffi::PyObject_GC_Del(obj as *mut ::c_void);
ffi::PyObject_GC_Del(obj as *mut c_void);
} else {
ffi::PyObject_Free(obj as *mut ::c_void);
ffi::PyObject_Free(obj as *mut c_void);
}
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,

View File

@ -120,7 +120,9 @@
//!
//! [`setuptools-rust`](https://github.com/PyO3/setuptools-rust) can be used to generate a python package and includes the commands above by default. See [examples/word-count](examples/word-count) and the associated setup.py.
extern crate libc;
// We need those types in the macro exports
#[doc(hidden)]
pub extern crate libc;
extern crate pyo3cls;
extern crate spin;
// We need that reexport for wrap_function
@ -214,7 +216,3 @@ pub mod prelude;
pub mod python;
mod pythonrun;
pub mod typeob;
// re-export for simplicity
#[doc(hidden)]
pub use std::os::raw::*;

View File

@ -14,6 +14,7 @@ use pythonrun;
/// Safe wrapper around unsafe `*mut ffi::PyObject` pointer.
#[derive(Debug)]
#[repr(transparent)]
pub struct PyObject(*mut ffi::PyObject);
// `PyObject` is thread-safe, any python related operations require a Python<'p> token.
@ -136,9 +137,9 @@ impl PyObject {
}
/// Casts the PyObject to a concrete Python object type.
pub fn cast_as<D>(&self, py: Python) -> Result<&D, <D as PyTryFrom>::Error>
pub fn cast_as<D>(&self, py: Python) -> Result<&D, PyDowncastError>
where
D: PyTryFrom<Error = PyDowncastError>,
D: PyTryFrom,
{
D::try_from(self.as_ref(py))
}

View File

@ -177,9 +177,9 @@ pub trait ObjectProtocol {
Self: PyTypeInfo;
/// Casts the PyObject to a concrete Python object type.
fn cast_as<'a, D>(&'a self) -> Result<&'a D, <D as PyTryFrom>::Error>
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError>
where
D: PyTryFrom<Error = PyDowncastError>,
D: PyTryFrom,
&'a PyObjectRef: std::convert::From<&'a Self>;
/// Extracts some type from the Python object.
@ -500,9 +500,9 @@ where
unsafe { self.py().mut_from_borrowed_ptr(self.as_ptr()) }
}
fn cast_as<'a, D>(&'a self) -> Result<&'a D, <D as PyTryFrom>::Error>
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError>
where
D: PyTryFrom<Error = PyDowncastError>,
D: PyTryFrom,
&'a PyObjectRef: std::convert::From<&'a Self>,
{
D::try_from(self.into())

View File

@ -93,7 +93,6 @@ macro_rules! pyobject_native_type(
($name: ty, $typeobject: expr, $checkfunction: path $(,$type_param: ident)*) => {
pyobject_native_type_named!($name $(,$type_param)*);
pyobject_native_type_convert!($name, $typeobject, $checkfunction $(,$type_param)*);
pyobject_downcast!($name, $checkfunction $(,$type_param)*);
impl<'a, $($type_param,)*> ::std::convert::From<&'a $name> for &'a $crate::PyObjectRef {
fn from(ob: &'a $name) -> Self {
@ -210,7 +209,6 @@ use python::ToPyPointer;
pub struct PyObjectRef(::PyObject);
pyobject_native_type_named!(PyObjectRef);
pyobject_native_type_convert!(PyObjectRef, ffi::PyBaseObject_Type, ffi::PyObject_Check);
pyobject_downcast!(PyObjectRef, ffi::PyObject_Check);
mod boolobject;
mod bytearray;

View File

@ -16,7 +16,6 @@ use python::ToPyPointer;
#[repr(transparent)]
pub struct PySequence(PyObject);
pyobject_native_type_named!(PySequence);
pyobject_downcast!(PySequence, ffi::PySequence_Check);
impl PySequence {
/// Returns the number of objects in sequence. This is equivalent to Python `len()`.
@ -279,9 +278,7 @@ where
}
impl PyTryFrom for PySequence {
type Error = PyDowncastError;
fn try_from(value: &PyObjectRef) -> Result<&PySequence, Self::Error> {
fn try_from(value: &PyObjectRef) -> Result<&PySequence, PyDowncastError> {
unsafe {
if ffi::PySequence_Check(value.as_ptr()) != 0 {
let ptr = value as *const _ as *mut u8 as *mut PySequence;
@ -292,11 +289,11 @@ impl PyTryFrom for PySequence {
}
}
fn try_from_exact(value: &PyObjectRef) -> Result<&PySequence, Self::Error> {
fn try_from_exact(value: &PyObjectRef) -> Result<&PySequence, PyDowncastError> {
<PySequence as PyTryFrom>::try_from(value)
}
fn try_from_mut(value: &PyObjectRef) -> Result<&mut PySequence, Self::Error> {
fn try_from_mut(value: &PyObjectRef) -> Result<&mut PySequence, PyDowncastError> {
unsafe {
if ffi::PySequence_Check(value.as_ptr()) != 0 {
let ptr = value as *const _ as *mut u8 as *mut PySequence;
@ -307,7 +304,7 @@ impl PyTryFrom for PySequence {
}
}
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut PySequence, Self::Error> {
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut PySequence, PyDowncastError> {
PySequence::try_from_mut(value)
}
}

View File

@ -12,6 +12,7 @@ use err::{PyErr, PyResult};
use instance::{Py, PyObjectWithToken, PyToken};
use objects::PyType;
use python::{IntoPyPointer, Python};
use std::os::raw::c_void;
use {class, ffi, pythonrun};
/// Python type information.
@ -65,10 +66,7 @@ pub const PY_TYPE_FLAG_BASETYPE: usize = 1 << 2;
/// The instances of this type have a dictionary containing instance variables
pub const PY_TYPE_FLAG_DICT: usize = 1 << 3;
impl<'a, T: ?Sized> PyTypeInfo for &'a T
where
T: PyTypeInfo,
{
impl<'a, T: PyTypeInfo + ?Sized> PyTypeInfo for &'a T {
type Type = T::Type;
type BaseType = T::BaseType;
const NAME: &'static str = T::NAME;
@ -262,13 +260,13 @@ where
}
match (*T::type_object()).tp_free {
Some(free) => free(obj as *mut ::c_void),
Some(free) => free(obj as *mut c_void),
None => {
let ty = ffi::Py_TYPE(obj);
if ffi::PyType_IS_GC(ty) != 0 {
ffi::PyObject_GC_Del(obj as *mut ::c_void);
ffi::PyObject_GC_Del(obj as *mut c_void);
} else {
ffi::PyObject_Free(obj as *mut ::c_void);
ffi::PyObject_Free(obj as *mut c_void);
}
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
@ -285,13 +283,13 @@ where
Self::drop(py, obj);
match (*T::type_object()).tp_free {
Some(free) => free(obj as *mut ::c_void),
Some(free) => free(obj as *mut c_void),
None => {
let ty = ffi::Py_TYPE(obj);
if ffi::PyType_IS_GC(ty) != 0 {
ffi::PyObject_GC_Del(obj as *mut ::c_void);
ffi::PyObject_GC_Del(obj as *mut c_void);
} else {
ffi::PyObject_Free(obj as *mut ::c_void);
ffi::PyObject_Free(obj as *mut c_void);
}
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,

View File

@ -90,13 +90,13 @@ impl<'p> PyObjectProtocol<'p> for StringMethods {
Ok(format!("format({})", format_spec))
}
fn __unicode__(&self) -> PyResult<PyObject> {
Ok(PyString::new(self.py(), "unicode").into())
}
fn __bytes__(&self) -> PyResult<PyObject> {
Ok(PyBytes::new(self.py(), b"bytes").into())
}
fn __unicode__(&self) -> PyResult<PyObject> {
Ok(PyString::new(self.py(), "unicode").into())
}
}
#[cfg(Py_3)]