Skip to content

Commit faa3fed

Browse files
committed
Merge branch 'master' into doctest
2 parents f1b0288 + 7149a1f commit faa3fed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1122
-119
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ matrix:
2222
python: "3.7"
2323
# Keep this synced up with build.rs
2424
env: TRAVIS_RUST_VERSION=nightly-2019-02-07
25+
# Tested via anaconda PyPy (since travis's PyPy version is too old)
26+
- name: PyPy3.5 7.0
27+
python: "3.7"
28+
env: FEATURES="pypy" PATH="$PATH:/opt/anaconda/envs/pypy3/bin"
2529
allow_failures:
2630
- python: "3.8-dev"
2731

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99

1010
### Added
1111

12+
* PyPy support by omerbenamram in [#393](https://github.com/PyO3/pyo3/pull/393)
1213
* Have `PyModule` generate an index of its members (`__all__` list).
1314

15+
### Changed
16+
17+
* Methods decorated with `#[getter]` and `#[setter]` can now omit wrapping the
18+
result type in `PyResult` if they don't raise exceptions.
19+
1420
### Fixed
1521

1622
* `type_object::PyTypeObject` has been marked unsafe because breaking the contract `type_object::PyTypeObject::init_type` can lead to UB.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pyo3"
3-
version = "0.6.0"
3+
version = "0.7.0-alpha.1"
44
description = "Bindings to Python interpreter"
55
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
66
readme = "README.md"
@@ -22,7 +22,7 @@ appveyor = { repository = "fafhrd91/pyo3" }
2222
libc = "0.2.48"
2323
spin = "0.5.0"
2424
num-traits = "0.2.6"
25-
pyo3cls = { path = "pyo3cls", version = "=0.6.0" }
25+
pyo3cls = { path = "pyo3cls", version = "=0.7.0-alpha.1" }
2626
mashup = "0.1.9"
2727
num-complex = { version = "0.2.1", optional = true }
2828
inventory = "0.1.3"

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste
1818

1919
PyO3 supports Python 3.5 and up. The minimum required rust version is 1.34.0-nightly 2019-02-06.
2020

21+
PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0.
22+
Please refer to the guide for installation instruction against PyPy.
23+
2124
You can either write a native Python module in rust or use Python from a Rust binary.
2225

2326
However, on some OSs, you need some additional packages. E.g. if you are on *Ubuntu 18.04*, please run
@@ -43,7 +46,7 @@ name = "string_sum"
4346
crate-type = ["cdylib"]
4447

4548
[dependencies.pyo3]
46-
version = "0.6.0"
49+
version = "0.7.0-alpha.1"
4750
features = ["extension-module"]
4851
```
4952

@@ -88,7 +91,7 @@ Add `pyo3` this to your `Cargo.toml`:
8891

8992
```toml
9093
[dependencies]
91-
pyo3 = "0.6.0-alpha.4"
94+
pyo3 = "0.7.0-alpha.1"
9295
```
9396

9497
Example program displaying the value of `sys.version`:

build.rs

Lines changed: 129 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,18 @@ use version_check::{is_min_date, is_min_version, supports_features};
1818
const MIN_DATE: &'static str = "2019-02-06";
1919
const MIN_VERSION: &'static str = "1.34.0-nightly";
2020

21+
#[derive(Debug, Clone, PartialEq)]
22+
pub enum PythonInterpreterKind {
23+
CPython,
24+
PyPy,
25+
}
26+
2127
#[derive(Debug)]
2228
struct PythonVersion {
2329
major: u8,
2430
// minor == None means any minor version will do
2531
minor: Option<u8>,
32+
implementation: PythonInterpreterKind,
2633
}
2734

2835
impl PartialEq for PythonVersion {
@@ -142,6 +149,7 @@ fn load_cross_compile_info() -> Result<(PythonVersion, HashMap<String, String>,
142149
let python_version = PythonVersion {
143150
major,
144151
minor: Some(minor),
152+
implementation: PythonInterpreterKind::CPython,
145153
};
146154

147155
let config_map = parse_header_defines(python_include_dir.join("pyconfig.h"))?;
@@ -280,17 +288,43 @@ fn run_python_script(interpreter: &str, script: &str) -> Result<String, String>
280288
Ok(String::from_utf8(out.stdout).unwrap())
281289
}
282290

291+
fn get_library_link_name(version: &PythonVersion, ld_version: &str) -> String {
292+
if cfg!(target_os = "windows") {
293+
let minor_or_empty_string = match version.minor {
294+
Some(minor) => format!("{}", minor),
295+
None => String::new(),
296+
};
297+
match version.implementation {
298+
PythonInterpreterKind::CPython => {
299+
format!("python{}{}", version.major, minor_or_empty_string)
300+
}
301+
PythonInterpreterKind::PyPy => format!("pypy{}-c", version.major),
302+
}
303+
} else {
304+
match version.implementation {
305+
PythonInterpreterKind::CPython => format!("python{}", ld_version),
306+
PythonInterpreterKind::PyPy => format!("pypy{}-c", version.major),
307+
}
308+
}
309+
}
310+
283311
#[cfg(not(target_os = "macos"))]
284312
#[cfg(not(target_os = "windows"))]
285313
fn get_rustc_link_lib(
286-
_: &PythonVersion,
314+
version: &PythonVersion,
287315
ld_version: &str,
288316
enable_shared: bool,
289317
) -> Result<String, String> {
290318
if enable_shared {
291-
Ok(format!("cargo:rustc-link-lib=python{}", ld_version))
319+
Ok(format!(
320+
"cargo:rustc-link-lib={}",
321+
get_library_link_name(&version, ld_version)
322+
))
292323
} else {
293-
Ok(format!("cargo:rustc-link-lib=static=python{}", ld_version))
324+
Ok(format!(
325+
"cargo:rustc-link-lib=static={}",
326+
get_library_link_name(&version, ld_version)
327+
))
294328
}
295329
}
296330

@@ -311,42 +345,63 @@ else:
311345
}
312346

313347
#[cfg(target_os = "macos")]
314-
fn get_rustc_link_lib(_: &PythonVersion, ld_version: &str, _: bool) -> Result<String, String> {
348+
fn get_rustc_link_lib(
349+
version: &PythonVersion,
350+
ld_version: &str,
351+
_: bool,
352+
) -> Result<String, String> {
315353
// os x can be linked to a framework or static or dynamic, and
316354
// Py_ENABLE_SHARED is wrong; framework means shared library
317355
match get_macos_linkmodel().unwrap().as_ref() {
318-
"static" => Ok(format!("cargo:rustc-link-lib=static=python{}", ld_version)),
319-
"shared" => Ok(format!("cargo:rustc-link-lib=python{}", ld_version)),
320-
"framework" => Ok(format!("cargo:rustc-link-lib=python{}", ld_version)),
356+
"static" => Ok(format!(
357+
"cargo:rustc-link-lib=static={}",
358+
get_library_link_name(&version, ld_version)
359+
)),
360+
"shared" => Ok(format!(
361+
"cargo:rustc-link-lib={}",
362+
get_library_link_name(&version, ld_version)
363+
)),
364+
"framework" => Ok(format!(
365+
"cargo:rustc-link-lib={}",
366+
get_library_link_name(&version, ld_version)
367+
)),
321368
other => Err(format!("unknown linkmodel {}", other)),
322369
}
323370
}
324371

372+
#[cfg(target_os = "windows")]
373+
fn get_rustc_link_lib(
374+
version: &PythonVersion,
375+
ld_version: &str,
376+
_: bool,
377+
) -> Result<String, String> {
378+
// Py_ENABLE_SHARED doesn't seem to be present on windows.
379+
Ok(format!(
380+
"cargo:rustc-link-lib=pythonXY:{}",
381+
get_library_link_name(&version, ld_version)
382+
))
383+
}
384+
325385
/// Parse string as interpreter version.
326-
fn get_interpreter_version(line: &str) -> Result<PythonVersion, String> {
386+
fn get_interpreter_version(line: &str, implementation: &str) -> Result<PythonVersion, String> {
327387
let version_re = Regex::new(r"\((\d+), (\d+)\)").unwrap();
328388
match version_re.captures(&line) {
329389
Some(cap) => Ok(PythonVersion {
330390
major: cap.get(1).unwrap().as_str().parse().unwrap(),
331391
minor: Some(cap.get(2).unwrap().as_str().parse().unwrap()),
392+
implementation: match implementation {
393+
"CPython" => PythonInterpreterKind::CPython,
394+
"PyPy" => PythonInterpreterKind::PyPy,
395+
_ => panic!(format!(
396+
"Unsupported python implementation `{}`",
397+
implementation
398+
)),
399+
},
332400
}),
333401
None => Err(format!("Unexpected response to version query {}", line)),
334402
}
335403
}
336404

337-
#[cfg(target_os = "windows")]
338-
fn get_rustc_link_lib(version: &PythonVersion, _: &str, _: bool) -> Result<String, String> {
339-
// Py_ENABLE_SHARED doesn't seem to be present on windows.
340-
Ok(format!(
341-
"cargo:rustc-link-lib=pythonXY:python{}{}",
342-
version.major,
343-
match version.minor {
344-
Some(minor) => minor.to_string(),
345-
None => "".to_owned(),
346-
}
347-
))
348-
}
349-
350405
/// Locate a suitable python interpreter and extract config from it.
351406
///
352407
/// The following locations are checked in the order listed:
@@ -387,10 +442,17 @@ fn find_interpreter_and_get_config(
387442
let expected_version = version.unwrap_or(PythonVersion {
388443
major: 3,
389444
minor: None,
445+
implementation: PythonInterpreterKind::CPython,
390446
});
391447

448+
let binary_name = match expected_version.implementation {
449+
PythonInterpreterKind::CPython => "python",
450+
PythonInterpreterKind::PyPy => "pypy",
451+
};
452+
392453
// check default python
393-
let interpreter_path = "python";
454+
let interpreter_path = binary_name;
455+
394456
let (interpreter_version, lines) = get_config_from_interpreter(interpreter_path)?;
395457
if expected_version == interpreter_version {
396458
return Ok((
@@ -430,20 +492,45 @@ fn get_config_from_interpreter(interpreter: &str) -> Result<(PythonVersion, Vec<
430492
let script = r#"
431493
import sys
432494
import sysconfig
495+
import platform
496+
497+
PYPY = platform.python_implementation() == "PyPy"
433498
434499
print(sys.version_info[0:2])
435500
print(sysconfig.get_config_var('LIBDIR'))
436-
print(sysconfig.get_config_var('Py_ENABLE_SHARED'))
501+
if PYPY:
502+
print("1")
503+
else:
504+
print(sysconfig.get_config_var('Py_ENABLE_SHARED'))
437505
print(sysconfig.get_config_var('LDVERSION') or sysconfig.get_config_var('py_version_short'))
438506
print(sys.exec_prefix)
507+
print(platform.python_implementation())
439508
"#;
440509
let out = run_python_script(interpreter, script)?;
441510
let lines: Vec<String> = out.lines().map(|line| line.to_owned()).collect();
442-
let interpreter_version = get_interpreter_version(&lines[0])?;
511+
let interpreter_version = get_interpreter_version(&lines[0], &lines[5])?;
443512
Ok((interpreter_version, lines))
444513
}
445514

515+
fn ensure_python_version_is_supported(version: &PythonVersion) -> Result<(), String> {
516+
match (&version.implementation, version.major, version.minor) {
517+
(PythonInterpreterKind::PyPy, 2, _) => {
518+
Err("PyPy cpyext bindings is only supported for Python3".to_string())
519+
}
520+
(_, 3, Some(minor)) if minor < PY3_MIN_MINOR => Err(format!(
521+
"Python 3 required version is 3.{}, current version is 3.{}",
522+
PY3_MIN_MINOR, minor
523+
)),
524+
_ => Ok(()),
525+
}
526+
}
527+
446528
fn configure(interpreter_version: &PythonVersion, lines: Vec<String>) -> Result<(String), String> {
529+
ensure_python_version_is_supported(&interpreter_version).expect(&format!(
530+
"Unsupported interpreter {:?}",
531+
interpreter_version
532+
));
533+
447534
let libpath: &str = &lines[1];
448535
let enable_shared: &str = &lines[2];
449536
let ld_version: &str = &lines[3];
@@ -464,26 +551,28 @@ fn configure(interpreter_version: &PythonVersion, lines: Vec<String>) -> Result<
464551

465552
let mut flags = String::new();
466553

554+
if interpreter_version.implementation == PythonInterpreterKind::PyPy {
555+
println!("cargo:rustc-cfg=PyPy");
556+
flags += format!("CFG_PyPy").as_ref();
557+
};
558+
467559
if let PythonVersion {
468560
major: 3,
469561
minor: some_minor,
562+
implementation: _,
470563
} = interpreter_version
471564
{
472565
if env::var_os("CARGO_FEATURE_ABI3").is_some() {
473566
println!("cargo:rustc-cfg=Py_LIMITED_API");
474567
}
568+
475569
if let Some(minor) = some_minor {
476-
if minor < &PY3_MIN_MINOR {
477-
return Err(format!(
478-
"Python 3 required version is 3.{}, current version is 3.{}",
479-
PY3_MIN_MINOR, minor
480-
));
481-
}
482570
for i in 5..(minor + 1) {
483571
println!("cargo:rustc-cfg=Py_3_{}", i);
484572
flags += format!("CFG_Py_3_{},", i).as_ref();
485573
}
486574
}
575+
println!("cargo:rustc-cfg=Py_3");
487576
} else {
488577
// fail PYTHON_SYS_EXECUTABLE=python2 cargo ...
489578
return Err("Python 2 is not supported".to_string());
@@ -512,6 +601,7 @@ fn version_from_env() -> Option<PythonVersion> {
512601
Some(s) => Some(s.as_str().parse().unwrap()),
513602
None => None,
514603
},
604+
implementation: PythonInterpreterKind::CPython,
515605
});
516606
}
517607
None => (),
@@ -589,6 +679,15 @@ fn main() -> Result<(), String> {
589679
}
590680
}
591681

682+
// These flags need to be enabled manually for PyPy, because it does not expose
683+
// them in `sysconfig.get_config_vars()`
684+
if interpreter_version.implementation == PythonInterpreterKind::PyPy {
685+
config_map.insert("WITH_THREAD".to_owned(), "1".to_owned());
686+
config_map.insert("Py_USING_UNICODE".to_owned(), "1".to_owned());
687+
config_map.insert("Py_UNICODE_SIZE".to_owned(), "4".to_owned());
688+
config_map.insert("Py_UNICODE_WIDE".to_owned(), "1".to_owned());
689+
}
690+
592691
// WITH_THREAD is always on for 3.7
593692
if interpreter_version.major == 3 && interpreter_version.minor.unwrap_or(0) >= 7 {
594693
config_map.insert("WITH_THREAD".to_owned(), "1".to_owned());

ci/travis/cover.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
#!/bin/sh
1+
#!/bin/bash
22

33
set -ex
44

5+
### PyPy does not run the test suite ###########################################
6+
7+
if [[ $FEATURES == *"pypy"* ]]; then
8+
exit 0
9+
fi
10+
511
### Run kcov ###################################################################
612

713
rm -f target/debug/pyo3*.d

ci/travis/guide.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/bin/sh
1+
#!/bin/bash
22

33
set -ex
44

0 commit comments

Comments
 (0)