-
Notifications
You must be signed in to change notification settings - Fork 778
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
31b871a
commit 597a184
Showing
15 changed files
with
510 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
authors = ["{{authors}}"] | ||
name = "{{project-name}}" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
name = "sequential" | ||
crate-type = ["cdylib", "lib"] | ||
|
||
[dependencies] | ||
pyo3-ffi = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
variable::set("PYO3_VERSION", "0.19.2"); | ||
file::rename(".template/Cargo.toml", "Cargo.toml"); | ||
file::rename(".template/pyproject.toml", "pyproject.toml"); | ||
file::delete(".template"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[build-system] | ||
requires = ["maturin>=1,<2"] | ||
build-backend = "maturin" | ||
|
||
[project] | ||
name = "{{project-name}}" | ||
version = "0.1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "sequential" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
name = "sequential" | ||
crate-type = ["cdylib", "lib"] | ||
|
||
[dependencies] | ||
pyo3-ffi = { path = "../../pyo3-ffi", features = ["extension-module"] } | ||
|
||
[workspace] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include pyproject.toml Cargo.toml | ||
recursive-include src * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# sequential | ||
|
||
A project built using only `pyo3_ffi`, without any of PyO3's safe api. It can be executed by subinterpreters that have their own GIL. | ||
|
||
## Building and Testing | ||
|
||
To build this package, first install `maturin`: | ||
|
||
```shell | ||
pip install maturin | ||
``` | ||
|
||
To build and test use `maturin develop`: | ||
|
||
```shell | ||
pip install -r requirements-dev.txt | ||
maturin develop | ||
pytest | ||
``` | ||
|
||
Alternatively, install nox and run the tests inside an isolated environment: | ||
|
||
```shell | ||
nox | ||
``` | ||
|
||
## Copying this example | ||
|
||
Use [`cargo-generate`](https://crates.io/crates/cargo-generate): | ||
|
||
```bash | ||
$ cargo install cargo-generate | ||
$ cargo generate --git https://github.com/PyO3/pyo3 examples/sequential | ||
``` | ||
|
||
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[template] | ||
ignore = [".nox"] | ||
|
||
[hooks] | ||
pre = [".template/pre-script.rhai"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import sys | ||
import nox | ||
|
||
|
||
@nox.session | ||
def python(session): | ||
if sys.version_info < (3, 12): | ||
session.skip("Python 3.12+ is required") | ||
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" | ||
session.install(".[dev]") | ||
session.run("pytest") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[build-system] | ||
requires = ["maturin>=1,<2"] | ||
build-backend = "maturin" | ||
|
||
[project] | ||
name = "sequential" | ||
version = "0.1.0" | ||
classifiers = [ | ||
"License :: OSI Approved :: MIT License", | ||
"Development Status :: 3 - Alpha", | ||
"Intended Audience :: Developers", | ||
"Programming Language :: Python", | ||
"Programming Language :: Rust", | ||
"Operating System :: POSIX", | ||
"Operating System :: MacOS :: MacOS X", | ||
] | ||
requires-python = ">=3.12" | ||
|
||
[project.optional-dependencies] | ||
dev = ["pytest"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
use core::sync::atomic::{AtomicU64, Ordering}; | ||
use core::{mem, ptr}; | ||
use std::os::raw::{c_char, c_int, c_uint, c_ulonglong, c_void}; | ||
|
||
use pyo3_ffi::*; | ||
|
||
#[repr(C)] | ||
pub struct PyId { | ||
_ob_base: PyObject, | ||
id: Id, | ||
} | ||
|
||
static COUNT: AtomicU64 = AtomicU64::new(0); | ||
|
||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] | ||
pub struct Id(u64); | ||
|
||
impl Id { | ||
fn new() -> Self { | ||
Id(COUNT.fetch_add(1, Ordering::Relaxed)) | ||
} | ||
} | ||
|
||
unsafe extern "C" fn id_new( | ||
subtype: *mut PyTypeObject, | ||
args: *mut PyObject, | ||
kwds: *mut PyObject, | ||
) -> *mut PyObject { | ||
if PyTuple_Size(args) != 0 || !kwds.is_null() { | ||
PyErr_SetString( | ||
PyExc_TypeError, | ||
"Id() takes no arguments\0".as_ptr().cast::<c_char>(), | ||
); | ||
return ptr::null_mut(); | ||
} | ||
|
||
let f: allocfunc = (*subtype).tp_alloc.unwrap_or(PyType_GenericAlloc); | ||
let slf = f(subtype, 0); | ||
|
||
if slf.is_null() { | ||
return ptr::null_mut(); | ||
} else { | ||
let id = Id::new(); | ||
let slf = slf.cast::<PyId>(); | ||
ptr::addr_of_mut!((*slf).id).write(id); | ||
} | ||
|
||
slf | ||
} | ||
|
||
unsafe extern "C" fn id_repr(slf: *mut PyObject) -> *mut PyObject { | ||
let slf = slf.cast::<PyId>(); | ||
let id = (*slf).id.0; | ||
let string = format!("Id({})", id); | ||
PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as Py_ssize_t) | ||
} | ||
|
||
unsafe extern "C" fn id_int(slf: *mut PyObject) -> *mut PyObject { | ||
let slf = slf.cast::<PyId>(); | ||
let id = (*slf).id.0; | ||
PyLong_FromUnsignedLongLong(id as c_ulonglong) | ||
} | ||
|
||
unsafe extern "C" fn id_richcompare( | ||
slf: *mut PyObject, | ||
other: *mut PyObject, | ||
op: c_int, | ||
) -> *mut PyObject { | ||
let pytype = Py_TYPE(slf); // guaranteed to be `sequential.Id` | ||
if Py_TYPE(other) != pytype { | ||
return Py_NewRef(Py_NotImplemented()); | ||
} | ||
let slf = (*slf.cast::<PyId>()).id; | ||
let other = (*other.cast::<PyId>()).id; | ||
|
||
let cmp = match op { | ||
pyo3_ffi::Py_LT => slf < other, | ||
pyo3_ffi::Py_LE => slf <= other, | ||
pyo3_ffi::Py_EQ => slf == other, | ||
pyo3_ffi::Py_NE => slf != other, | ||
pyo3_ffi::Py_GT => slf > other, | ||
pyo3_ffi::Py_GE => slf >= other, | ||
unrecognized => { | ||
let msg = format!("unrecognized richcompare opcode {}\0", unrecognized); | ||
PyErr_SetString(PyExc_SystemError, msg.as_ptr().cast::<c_char>()); | ||
return ptr::null_mut(); | ||
} | ||
}; | ||
|
||
if cmp { | ||
Py_NewRef(Py_True()) | ||
} else { | ||
Py_NewRef(Py_False()) | ||
} | ||
} | ||
|
||
static mut SLOTS: &[PyType_Slot] = &[ | ||
PyType_Slot { | ||
slot: Py_tp_new, | ||
pfunc: id_new as *mut c_void, | ||
}, | ||
PyType_Slot { | ||
slot: Py_tp_doc, | ||
pfunc: "An id that is increased every time an instance is created\0".as_ptr() | ||
as *mut c_void, | ||
}, | ||
PyType_Slot { | ||
slot: Py_tp_repr, | ||
pfunc: id_repr as *mut c_void, | ||
}, | ||
PyType_Slot { | ||
slot: Py_nb_int, | ||
pfunc: id_int as *mut c_void, | ||
}, | ||
PyType_Slot { | ||
slot: Py_tp_richcompare, | ||
pfunc: id_richcompare as *mut c_void, | ||
}, | ||
PyType_Slot { | ||
slot: 0, | ||
pfunc: ptr::null_mut(), | ||
}, | ||
]; | ||
|
||
pub static mut ID_SPEC: PyType_Spec = PyType_Spec { | ||
name: "sequential.Id\0".as_ptr().cast::<c_char>(), | ||
basicsize: mem::size_of::<PyId>() as c_int, | ||
itemsize: 0, | ||
flags: (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE) as c_uint, | ||
slots: unsafe { SLOTS as *const [PyType_Slot] as *mut PyType_Slot }, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
use std::ptr; | ||
|
||
use pyo3_ffi::*; | ||
|
||
mod id; | ||
mod module; | ||
use crate::module::MODULE_DEF; | ||
|
||
// The module initialization function, which must be named `PyInit_<your_module>`. | ||
#[allow(non_snake_case)] | ||
#[no_mangle] | ||
pub unsafe extern "C" fn PyInit_sequential() -> *mut PyObject { | ||
PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use core::{mem, ptr}; | ||
use pyo3_ffi::*; | ||
use std::os::raw::{c_char, c_int, c_void}; | ||
|
||
pub static mut MODULE_DEF: PyModuleDef = PyModuleDef { | ||
m_base: PyModuleDef_HEAD_INIT, | ||
m_name: "sequential\0".as_ptr().cast::<c_char>(), | ||
m_doc: "A library for generating sequential ids, written in Rust.\0" | ||
.as_ptr() | ||
.cast::<c_char>(), | ||
m_size: mem::size_of::<sequential_state>() as Py_ssize_t, | ||
m_methods: std::ptr::null_mut(), | ||
m_slots: unsafe { SEQUENTIAL_SLOTS as *const [PyModuleDef_Slot] as *mut PyModuleDef_Slot }, | ||
m_traverse: Some(sequential_traverse), | ||
m_clear: Some(sequential_clear), | ||
m_free: Some(sequential_free), | ||
}; | ||
|
||
static mut SEQUENTIAL_SLOTS: &[PyModuleDef_Slot] = &[ | ||
PyModuleDef_Slot { | ||
slot: Py_mod_exec, | ||
value: sequential_exec as *mut c_void, | ||
}, | ||
PyModuleDef_Slot { | ||
slot: Py_mod_multiple_interpreters, | ||
value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED, | ||
}, | ||
PyModuleDef_Slot { | ||
slot: 0, | ||
value: ptr::null_mut(), | ||
}, | ||
]; | ||
|
||
unsafe extern "C" fn sequential_exec(module: *mut PyObject) -> c_int { | ||
let state: *mut sequential_state = PyModule_GetState(module).cast(); | ||
|
||
let id_type = PyType_FromModuleAndSpec( | ||
module, | ||
ptr::addr_of_mut!(crate::id::ID_SPEC), | ||
ptr::null_mut(), | ||
); | ||
if id_type.is_null() { | ||
PyErr_SetString( | ||
PyExc_SystemError, | ||
"cannot locate type object\0".as_ptr().cast::<c_char>(), | ||
); | ||
return -1; | ||
} | ||
(*state).id_type = id_type.cast::<PyTypeObject>(); | ||
|
||
PyModule_AddObjectRef(module, "Id\0".as_ptr().cast::<c_char>(), id_type) | ||
} | ||
|
||
unsafe extern "C" fn sequential_traverse( | ||
module: *mut PyObject, | ||
visit: visitproc, | ||
arg: *mut c_void, | ||
) -> c_int { | ||
let state: *mut sequential_state = PyModule_GetState(module.cast()).cast(); | ||
let id_type: *mut PyObject = (*state).id_type.cast(); | ||
|
||
if id_type.is_null() { | ||
0 | ||
} else { | ||
(visit)(id_type, arg) | ||
} | ||
} | ||
|
||
unsafe extern "C" fn sequential_clear(module: *mut PyObject) -> c_int { | ||
let state: *mut sequential_state = PyModule_GetState(module.cast()).cast(); | ||
Py_CLEAR(ptr::addr_of_mut!((*state).id_type).cast()); | ||
0 | ||
} | ||
|
||
unsafe extern "C" fn sequential_free(module: *mut c_void) { | ||
sequential_clear(module.cast()); | ||
} | ||
|
||
#[repr(C)] | ||
struct sequential_state { | ||
id_type: *mut PyTypeObject, | ||
} |
Oops, something went wrong.