changeset 40978:5532823e8c18

rust-cpython: start cpython crate bindings This changeset introduces the hg-cpython crate, that compiles as a shared library holding a whole Python package (mercurial.rustext), with only the empty 'ancestor' submodule for now. Such bindings will be easier and safer to develop and maintain that those of `hg-direct-ffi`. They don't involve C code, only unsafe Rust that's mostly isolated within the cpython crate. The long-term goal would be to import the provided modules, such as rustext.ancestor with mercurial.policy.importmod, same as we already do with cext modules. Differential Revision: https://phab.mercurial-scm.org/D5434
author Georges Racinet <gracinet@anybox.fr>
date Mon, 03 Dec 2018 06:52:17 +0100
parents 98a0fbda8739
children 1eaf62a67c1a
files rust/Cargo.lock rust/Cargo.toml rust/hg-cpython/Cargo.toml rust/hg-cpython/rustfmt.toml rust/hg-cpython/src/ancestors.rs rust/hg-cpython/src/exceptions.rs rust/hg-cpython/src/lib.rs
diffstat 7 files changed, 247 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/rust/Cargo.lock	Mon Dec 03 06:54:19 2018 +0100
+++ b/rust/Cargo.lock	Mon Dec 03 06:52:17 2018 +0100
@@ -1,8 +1,42 @@
+[[package]]
+name = "aho-corasick"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cpython"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "hg-core"
 version = "0.1.0"
 
 [[package]]
+name = "hg-cpython"
+version = "0.1.0"
+dependencies = [
+ "cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hg-core 0.1.0",
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "hgdirectffi"
 version = "0.1.0"
 dependencies = [
@@ -11,9 +45,104 @@
 ]
 
 [[package]]
+name = "lazy_static"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "libc"
 version = "0.2.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "memchr"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "python27-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "python3-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread_local"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ucd-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "utf8-ranges"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "version_check"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [metadata]
+"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
+"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
+"checksum cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b489034e723e7f5109fecd19b719e664f89ef925be785885252469e9822fa940"
+"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
 "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
+"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
+"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
+"checksum python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56114c37d4dca82526d74009df7782a28c871ac9d36b19d4cb9e67672258527e"
+"checksum python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61e4aac43f833fd637e429506cb2ac9d7df672c4b68f2eaaa163649b7fdc0444"
+"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
+"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
+"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
+"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
+"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
+"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
--- a/rust/Cargo.toml	Mon Dec 03 06:54:19 2018 +0100
+++ b/rust/Cargo.toml	Mon Dec 03 06:52:17 2018 +0100
@@ -1,3 +1,3 @@
 [workspace]
-members = ["hg-core", "hg-direct-ffi"]
+members = ["hg-core", "hg-direct-ffi", "hg-cpython"]
 exclude = ["chg", "hgcli"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/Cargo.toml	Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,29 @@
+[package]
+name = "hg-cpython"
+version = "0.1.0"
+authors = ["Georges Racinet <gracinet@anybox.fr>"]
+
+[lib]
+name='rusthg'
+crate-type = ["cdylib"]
+
+[features]
+default = ["python27", "python27-sys"]
+
+python27 = ["cpython/python27-sys", "cpython/extension-module-2-7"]
+
+[dependencies]
+hg-core = { path = "../hg-core" }
+libc = '*'
+
+[dependencies.cpython]
+version = "*"
+default-features = false
+
+[dependencies.python27-sys]
+version = "0.2.1"
+optional = true
+
+[dependencies.python3-sys]
+version = "0.2.1"
+optional = true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/rustfmt.toml	Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,3 @@
+max_width = 79
+wrap_comments = true
+error_on_line_overflow = true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/ancestors.rs	Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,30 @@
+// ancestors.rs
+//
+// Copyright 2018 Georges Racinet <gracinet@anybox.fr>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Bindings for the hg::ancestors module provided by the
+//! `hg-core` crate. From Python, this will be seen as `rustext.ancestor`
+use cpython::{PyDict, PyModule, PyResult, Python};
+
+/// Create the module, with __package__ given from parent
+pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
+    let dotted_name = &format!("{}.ancestor", package);
+    let m = PyModule::new(py, dotted_name)?;
+    m.add(py, "__package__", package)?;
+    m.add(
+        py,
+        "__doc__",
+        "Generic DAG ancestor algorithms - Rust implementation",
+    )?;
+
+    let sys = PyModule::import(py, "sys")?;
+    let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
+    sys_modules.set_item(py, dotted_name, &m)?;
+    // Example C code (see pyexpat.c and import.c) will "give away the
+    // reference", but we won't because it will be consumed once the
+    // Rust PyObject is dropped.
+    Ok(m)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/exceptions.rs	Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,15 @@
+use cpython::exc::ValueError;
+use cpython::{PyErr, Python};
+use hg;
+
+py_exception!(rustext, GraphError, ValueError);
+
+impl GraphError {
+    pub fn pynew(py: Python, inner: hg::GraphError) -> PyErr {
+        match inner {
+            hg::GraphError::ParentOutOfRange(r) => {
+                GraphError::new(py, ("ParentOutOfRange", r))
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/lib.rs	Mon Dec 03 06:52:17 2018 +0100
@@ -0,0 +1,40 @@
+// lib.rs
+//
+// Copyright 2018 Georges Racinet <gracinet@anybox.fr>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Python bindings of `hg-core` objects using the `cpython` crate.
+//! Once compiled, the resulting single shared library object can be placed in
+//! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
+//! It holds several modules, so that from the point of view of Python,
+//! it behaves as the `cext` package.
+//!
+//! Example:
+//! ```
+//! >>> from mercurial.rustext import ancestor
+//! >>> ancestor.__doc__
+//! 'Generic DAG ancestor algorithms - Rust implementation'
+//! ```
+
+#[macro_use]
+extern crate cpython;
+extern crate hg;
+
+mod ancestors;
+mod exceptions;
+
+py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
+    m.add(
+        py,
+        "__doc__",
+        "Mercurial core concepts - Rust implementation",
+    )?;
+
+    let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
+    m.add(py, "__package__", "mercurial")?;
+    m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
+    m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
+    Ok(())
+});