Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement ffi/cpython/pystate #1687

Merged
merged 11 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/ffi/ceval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ use crate::ffi::object::PyObject;
use crate::ffi::pystate::PyThreadState;
use std::os::raw::{c_char, c_int, c_void};

// TODO: move to cpython
pub type _PyFrameEvalFunction =
extern "C" fn(*mut crate::ffi::PyFrameObject, c_int) -> *mut PyObject;

extern "C" {
#[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))]
#[cfg_attr(PyPy, link_name = "PyPyEval_CallObjectWithKeywords")]
Expand Down
7 changes: 5 additions & 2 deletions src/ffi/cpython/ceval.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use crate::ffi::object::{freefunc, PyObject};
use crate::ffi::pystate::Py_tracefunc;
use crate::ffi::Py_tracefunc;

use std::os::raw::c_int;

extern "C" {
pub fn PyEval_SetProfile(trace_func: Py_tracefunc, arg1: *mut PyObject);

pub fn _PyEval_EvalFrameDefault(
arg1: *mut crate::ffi::PyFrameObject,
exc: c_int,
) -> *mut PyObject;
pub fn _PyEval_RequestCodeExtraIndex(func: freefunc) -> c_int;
pub fn PyEval_SetProfile(trace_func: Py_tracefunc, arg1: *mut PyObject);

pub fn PyEval_SetTrace(trace_func: Py_tracefunc, arg1: *mut PyObject);
}
32 changes: 19 additions & 13 deletions src/ffi/cpython/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
pub mod abstract_;
pub(crate) mod abstract_;
// skipped bytearrayobject.h
#[cfg(not(PyPy))]
pub mod bytesobject;
pub mod ceval;
pub mod code;
pub mod compile;
pub(crate) mod bytesobject;
pub(crate) mod ceval;
pub(crate) mod code;
pub(crate) mod compile;
#[cfg(not(PyPy))]
pub mod dictobject;
pub(crate) mod dictobject;
// skipped fileobject.h
pub mod frameobject;
pub mod import;
pub(crate) mod frameobject;
pub(crate) mod import;
#[cfg(all(Py_3_8, not(PyPy)))]
pub mod initconfig;
pub(crate) mod initconfig;
// skipped interpreteridobject.h
pub mod listobject;
pub mod object;
pub mod pydebug;
pub(crate) mod listobject;
pub(crate) mod object;
pub(crate) mod pydebug;
#[cfg(all(Py_3_8, not(PyPy)))]
pub mod pylifecycle;
pub(crate) mod pylifecycle;

#[cfg(all(Py_3_8, not(PyPy)))]
pub(crate) mod pystate;

pub use self::abstract_::*;
#[cfg(not(PyPy))]
Expand All @@ -36,3 +39,6 @@ pub use self::object::*;
pub use self::pydebug::*;
#[cfg(all(Py_3_8, not(PyPy)))]
pub use self::pylifecycle::*;

#[cfg(all(Py_3_8, not(PyPy)))]
pub use self::pystate::*;
46 changes: 46 additions & 0 deletions src/ffi/cpython/pystate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use crate::ffi::pystate::{PyInterpreterState, PyThreadState};
use std::os::raw::c_int;

// Py_tracefunc is defined in ffi::pystate

pub const PyTrace_CALL: c_int = 0;
pub const PyTrace_EXCEPTION: c_int = 1;
pub const PyTrace_LINE: c_int = 2;
pub const PyTrace_RETURN: c_int = 3;
pub const PyTrace_C_CALL: c_int = 4;
pub const PyTrace_C_EXCEPTION: c_int = 5;
pub const PyTrace_C_RETURN: c_int = 6;
pub const PyTrace_OPCODE: c_int = 7;

extern "C" {
// PyGILState_Check is defined in ffi::pystate
pub fn PyInterpreterState_Main() -> *mut PyInterpreterState;
pub fn PyInterpreterState_Head() -> *mut PyInterpreterState;
pub fn PyInterpreterState_Next(interp: *mut PyInterpreterState) -> *mut PyInterpreterState;
pub fn PyInterpreterState_ThreadHead(interp: *mut PyInterpreterState) -> *mut PyThreadState;
pub fn PyThreadState_Next(tstate: *mut PyThreadState) -> *mut PyThreadState;
}

#[cfg(Py_3_9)]
#[cfg_attr(docsrs, doc(cfg(Py_3_9)))]
pub type _PyFrameEvalFunction = extern "C" fn(
*mut crate::ffi::PyThreadState,
*mut crate::ffi::PyFrameObject,
c_int,
) -> *mut crate::ffi::object::PyObject;

#[cfg(Py_3_9)]
extern "C" {
/// Get the frame evaluation function.
#[cfg_attr(docsrs, doc(cfg(Py_3_9)))]
pub fn _PyInterpreterState_GetEvalFrameFunc(
interp: *mut PyInterpreterState,
) -> _PyFrameEvalFunction;

///Set the frame evaluation function.
#[cfg_attr(docsrs, doc(cfg(Py_3_9)))]
pub fn _PyInterpreterState_SetEvalFrameFunc(
interp: *mut PyInterpreterState,
eval_frame: _PyFrameEvalFunction,
);
}
21 changes: 18 additions & 3 deletions src/ffi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
//! Raw ffi declarations for the C interface of Python.
//! Raw FFI declarations for Python's C API.
//!
//! This module provides low level bindings to the Python interpreter.
//! It is meant for advanced users only - regular PyO3 users shouldn't
//! need to interact with this module at all.
//!
//! # Safety
//!
//! The functions in this module lack individual safety documentation, but
//! generally the following apply:
//! - Pointer arguments have to point to a valid Python object of the correct type,
//! although null pointers are sometimes valid input.
//! - The vast majority can only be used safely while the GIL is held.
//! - Some functions have additional safety requirements, consult the
//! [Python/C API Reference Manual](https://docs.python.org/3/c-api/index.html)
//! for more information.
#![allow(
non_camel_case_types,
non_snake_case,
Expand Down Expand Up @@ -27,6 +42,7 @@ pub use self::code::*;
pub use self::codecs::*;
pub use self::compile::*;
pub use self::complexobject::*;
#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
pub use self::context::*;
#[cfg(not(Py_LIMITED_API))]
pub use self::datetime::*;
Expand Down Expand Up @@ -78,6 +94,7 @@ pub use self::weakrefobject::*;

#[cfg(not(Py_LIMITED_API))]
pub use self::cpython::*;

mod abstract_;
// skipped asdl.h
// skipped ast.h
Expand All @@ -95,8 +112,6 @@ mod compile; // TODO: incomplete
mod complexobject; // TODO supports PEP-384 only
#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
mod context; // It's actually 3.7.1, but no cfg for patches.
#[cfg(not(all(Py_3_8, not(Py_LIMITED_API))))]
mod context {}
#[cfg(not(Py_LIMITED_API))]
pub(crate) mod datetime;
mod descrobject; // TODO supports PEP-384 only
Expand Down
24 changes: 22 additions & 2 deletions src/ffi/pystate.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::ffi::ceval::_PyFrameEvalFunction;
#[cfg(all(Py_3_9, not(Py_LIMITED_API)))]
use crate::ffi::cpython::pystate::_PyFrameEvalFunction;
use crate::ffi::moduleobject::PyModuleDef;
use crate::ffi::object::PyObject;
use crate::ffi::PyFrameObject;
Expand All @@ -7,10 +8,29 @@ use std::os::raw::{c_int, c_long};
pub const MAX_CO_EXTRA_USERS: c_int = 255;

#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Debug, Copy, Clone)]
pub struct PyInterpreterState {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this looks dangerously broken, because it doesn't match at all the full contents of the corresponding struct in the Python headers: https://github.com/python/cpython/blob/af5fb6706219d7949c1db5c9f2b7da53198123f3/Include/internal/pycore_pystate.h#L67

Appreciate that this existed before you wrote this PR, but I don't think editing this struct in this PR achieves much.

As it's an internal type (and probably very different on PyPy),I think we should just replace with an opaque type:

opaque_struct!(PyInterpreterState);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/// Not actually a public field
pub ob_base: PyObject,

/// Not actually a public field
/// Also, it has an additional argument in 3.9
#[cfg(not(Py_3_9))]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that this is a good way to handle this, any suggestions?

pub eval_frame: extern "C" fn(*mut crate::ffi::PyFrameObject, c_int) -> *mut PyObject,

/// Not actually a public field
/// Also, it has an additional argument in 3.9
#[cfg(all(Py_3_9, not(Py_LIMITED_API)))]
pub eval_frame: _PyFrameEvalFunction,

/// Not actually a public field
/// Also, it has an additional argument in 3.9
#[cfg(all(Py_3_9, Py_LIMITED_API))]
pub eval_frame: extern "C" fn(
*mut crate::ffi::PyThreadState,
*mut crate::ffi::PyFrameObject,
c_int,
) -> *mut PyObject,
}

#[repr(C)]
Expand Down