sciforge-build 2026.6.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sciforge_build-2026.6.8/PKG-INFO +15 -0
- sciforge_build-2026.6.8/README.md +7 -0
- sciforge_build-2026.6.8/pyproject.toml +26 -0
- sciforge_build-2026.6.8/sciforge_build/__init__.py +22 -0
- sciforge_build-2026.6.8/sciforge_build/include/sciforge/binding/error.hpp +63 -0
- sciforge_build-2026.6.8/sciforge_build/include/sciforge/binding/gil.hpp +55 -0
- sciforge_build-2026.6.8/sciforge_build.egg-info/PKG-INFO +15 -0
- sciforge_build-2026.6.8/sciforge_build.egg-info/SOURCES.txt +10 -0
- sciforge_build-2026.6.8/sciforge_build.egg-info/dependency_links.txt +1 -0
- sciforge_build-2026.6.8/sciforge_build.egg-info/top_level.txt +1 -0
- sciforge_build-2026.6.8/setup.cfg +4 -0
- sciforge_build-2026.6.8/setup.py +23 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sciforge-build
|
|
3
|
+
Version: 2026.6.8
|
|
4
|
+
Summary: Build-time C++ binding-substrate headers for the SciForge ecosystem (never a runtime dependency).
|
|
5
|
+
Project-URL: Homepage, https://github.com/RECHE23/sciforge
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# sciforge-build
|
|
10
|
+
|
|
11
|
+
Build-time distribution of the SciForge C++ **binding substrate** headers
|
|
12
|
+
(`<sciforge/binding/...>`). It is **never a runtime dependency** — list it in your
|
|
13
|
+
`build-system.requires` and add `sciforge_build.get_include()` to your extension's
|
|
14
|
+
`include_dirs` at wheel-build time. Pure-Python (`py3-none-any`); CalVer, aligned with
|
|
15
|
+
the SciForge tag.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# sciforge-build
|
|
2
|
+
|
|
3
|
+
Build-time distribution of the SciForge C++ **binding substrate** headers
|
|
4
|
+
(`<sciforge/binding/...>`). It is **never a runtime dependency** — list it in your
|
|
5
|
+
`build-system.requires` and add `sciforge_build.get_include()` to your extension's
|
|
6
|
+
`include_dirs` at wheel-build time. Pure-Python (`py3-none-any`); CalVer, aligned with
|
|
7
|
+
the SciForge tag.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# sciforge-build — BUILD-TIME-ONLY distribution of the SciForge C++ binding-substrate
|
|
2
|
+
# headers (<sciforge/binding/...>). It is NEVER a runtime dependency: a consumer lists
|
|
3
|
+
# it in build-system.requires and adds sciforge_build.get_include() to its extension's
|
|
4
|
+
# include_dirs at wheel-build time. Pure-Python, py3-none-any (built with
|
|
5
|
+
# `python -m build`, not cibuildwheel). CalVer, aligned with the SciForge tag.
|
|
6
|
+
[build-system]
|
|
7
|
+
requires = ["setuptools>=68", "wheel"]
|
|
8
|
+
build-backend = "setuptools.build_meta"
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
name = "sciforge-build"
|
|
12
|
+
version = "2026.6.8"
|
|
13
|
+
description = "Build-time C++ binding-substrate headers for the SciForge ecosystem (never a runtime dependency)."
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://github.com/RECHE23/sciforge"
|
|
19
|
+
|
|
20
|
+
[tool.setuptools]
|
|
21
|
+
packages = ["sciforge_build"]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.package-data]
|
|
24
|
+
# The headers are vendored into the package by setup.py (copied from the repo's
|
|
25
|
+
# canonical include/) so the wheel + sdist are self-contained and build off-sibling.
|
|
26
|
+
sciforge_build = ["include/sciforge/binding/*.hpp"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Build-time access to the SciForge C++ binding-substrate headers.
|
|
2
|
+
|
|
3
|
+
Usage (in a consumer's setup.py):
|
|
4
|
+
|
|
5
|
+
import sciforge_build
|
|
6
|
+
Extension(..., include_dirs=[sciforge_build.get_include()])
|
|
7
|
+
|
|
8
|
+
so that `#include <sciforge/binding/error.hpp>` resolves at wheel-build time. This
|
|
9
|
+
package is never a runtime dependency.
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
__all__ = ["get_include"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_include() -> str:
|
|
17
|
+
"""Return the directory to put on a C/C++ extension's include path.
|
|
18
|
+
|
|
19
|
+
The returned path carries the ``sciforge/binding/`` header tree, so
|
|
20
|
+
``#include <sciforge/binding/error.hpp>`` resolves against it.
|
|
21
|
+
"""
|
|
22
|
+
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "include")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SciForge binding substrate — error bridge (Limited-API, abi3-safe).
|
|
2
|
+
//
|
|
3
|
+
// One place for the C++ -> Python exception bridge every ecosystem binding needs:
|
|
4
|
+
// no C++ exception is allowed to cross the C-API. Parameterized by the module's
|
|
5
|
+
// error type so each binding keeps its own exception class (e.g. real.error).
|
|
6
|
+
//
|
|
7
|
+
// Uses only Py_LIMITED_API functions (PyErr_NoMemory / PyErr_SetString /
|
|
8
|
+
// PyErr_NewException / PyModule_AddObjectRef), so it compiles into one abi3 wheel
|
|
9
|
+
// per platform (cp310+). The consumer defines Py_LIMITED_API before including this.
|
|
10
|
+
#pragma once
|
|
11
|
+
|
|
12
|
+
#include <Python.h>
|
|
13
|
+
|
|
14
|
+
#include <cstring>
|
|
15
|
+
#include <exception>
|
|
16
|
+
#include <new>
|
|
17
|
+
|
|
18
|
+
namespace sciforge::binding {
|
|
19
|
+
|
|
20
|
+
// Translate the in-flight C++ exception into a Python error and return nullptr, so a
|
|
21
|
+
// binding entry point can write
|
|
22
|
+
// try { ... } catch (...) { return set_cpp_error(module_error_type); }
|
|
23
|
+
// The argument is the module's error TYPE (catch (...) binds no exception object — the
|
|
24
|
+
// in-flight exception is re-examined here via a bare `throw;`). Branch order matters:
|
|
25
|
+
// bad_alloc maps to MemoryError, every other std::exception to `error_type` with
|
|
26
|
+
// .what(), and anything else to a generic "internal error".
|
|
27
|
+
inline PyObject* set_cpp_error(PyObject* error_type)
|
|
28
|
+
{
|
|
29
|
+
try {
|
|
30
|
+
throw;
|
|
31
|
+
} catch (const std::bad_alloc&) {
|
|
32
|
+
return PyErr_NoMemory();
|
|
33
|
+
} catch (const std::exception& ex) {
|
|
34
|
+
PyErr_SetString(error_type, ex.what());
|
|
35
|
+
return nullptr;
|
|
36
|
+
} catch (...) {
|
|
37
|
+
PyErr_SetString(error_type, "internal error");
|
|
38
|
+
return nullptr;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Create the module's exception type and attach it. `qualified_name` is the dotted
|
|
43
|
+
// name for the exception (e.g. "real.error"); the module attribute is its trailing
|
|
44
|
+
// component. `base` is optional — pass a base class (e.g. re.error) or nullptr to
|
|
45
|
+
// derive from Exception. Returns a new reference to the error type (the caller keeps
|
|
46
|
+
// it for set_cpp_error), or nullptr on failure with a Python error set.
|
|
47
|
+
inline PyObject* register_error(PyObject* module, const char* qualified_name,
|
|
48
|
+
PyObject* base = nullptr)
|
|
49
|
+
{
|
|
50
|
+
PyObject* err = PyErr_NewException(qualified_name, base, nullptr);
|
|
51
|
+
if (err == nullptr) {
|
|
52
|
+
return nullptr;
|
|
53
|
+
}
|
|
54
|
+
const char* dot = std::strrchr(qualified_name, '.');
|
|
55
|
+
const char* attr = (dot != nullptr) ? dot + 1 : qualified_name;
|
|
56
|
+
if (PyModule_AddObjectRef(module, attr, err) < 0) {
|
|
57
|
+
Py_DECREF(err);
|
|
58
|
+
return nullptr;
|
|
59
|
+
}
|
|
60
|
+
return err;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
} // namespace sciforge::binding
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// SciForge binding substrate — GIL release (Limited-API, abi3-safe).
|
|
2
|
+
//
|
|
3
|
+
// RAII GIL release that restores the GIL on EVERY scope exit, including a C++
|
|
4
|
+
// exception propagating out — unlike the bare Py_BEGIN/END_ALLOW_THREADS macros,
|
|
5
|
+
// whose END is skipped on a throw, leaving the GIL released (undefined behaviour
|
|
6
|
+
// afterwards). That restore-on-throw guarantee is the load-bearing reason this is
|
|
7
|
+
// RAII. The substrate owns the mechanism; the release *policy* (the threshold value)
|
|
8
|
+
// stays with the consumer, which measures it per workload.
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
#include <Python.h>
|
|
12
|
+
|
|
13
|
+
namespace sciforge::binding {
|
|
14
|
+
|
|
15
|
+
// Release the GIL for the lifetime of the object; restore it on destruction (every
|
|
16
|
+
// path, including stack unwinding from a throw).
|
|
17
|
+
class gil_release {
|
|
18
|
+
public:
|
|
19
|
+
gil_release() : saved_(PyEval_SaveThread()) {}
|
|
20
|
+
~gil_release() { PyEval_RestoreThread(saved_); }
|
|
21
|
+
|
|
22
|
+
gil_release(const gil_release&) = delete;
|
|
23
|
+
gil_release& operator=(const gil_release&) = delete;
|
|
24
|
+
gil_release(gil_release&&) = delete;
|
|
25
|
+
gil_release& operator=(gil_release&&) = delete;
|
|
26
|
+
|
|
27
|
+
private:
|
|
28
|
+
PyThreadState* saved_;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Release the GIL only when `above` is true (the consumer's threshold test); below
|
|
32
|
+
// it the GIL is simply kept — releasing/re-acquiring would dominate a tiny scan.
|
|
33
|
+
class scoped_gil_release {
|
|
34
|
+
public:
|
|
35
|
+
explicit scoped_gil_release(bool above)
|
|
36
|
+
: saved_(above ? PyEval_SaveThread() : nullptr)
|
|
37
|
+
{
|
|
38
|
+
}
|
|
39
|
+
~scoped_gil_release()
|
|
40
|
+
{
|
|
41
|
+
if (saved_ != nullptr) {
|
|
42
|
+
PyEval_RestoreThread(saved_);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
scoped_gil_release(const scoped_gil_release&) = delete;
|
|
47
|
+
scoped_gil_release& operator=(const scoped_gil_release&) = delete;
|
|
48
|
+
scoped_gil_release(scoped_gil_release&&) = delete;
|
|
49
|
+
scoped_gil_release& operator=(scoped_gil_release&&) = delete;
|
|
50
|
+
|
|
51
|
+
private:
|
|
52
|
+
PyThreadState* saved_;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
} // namespace sciforge::binding
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sciforge-build
|
|
3
|
+
Version: 2026.6.8
|
|
4
|
+
Summary: Build-time C++ binding-substrate headers for the SciForge ecosystem (never a runtime dependency).
|
|
5
|
+
Project-URL: Homepage, https://github.com/RECHE23/sciforge
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# sciforge-build
|
|
10
|
+
|
|
11
|
+
Build-time distribution of the SciForge C++ **binding substrate** headers
|
|
12
|
+
(`<sciforge/binding/...>`). It is **never a runtime dependency** — list it in your
|
|
13
|
+
`build-system.requires` and add `sciforge_build.get_include()` to your extension's
|
|
14
|
+
`include_dirs` at wheel-build time. Pure-Python (`py3-none-any`); CalVer, aligned with
|
|
15
|
+
the SciForge tag.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
sciforge_build/__init__.py
|
|
5
|
+
sciforge_build.egg-info/PKG-INFO
|
|
6
|
+
sciforge_build.egg-info/SOURCES.txt
|
|
7
|
+
sciforge_build.egg-info/dependency_links.txt
|
|
8
|
+
sciforge_build.egg-info/top_level.txt
|
|
9
|
+
sciforge_build/include/sciforge/binding/error.hpp
|
|
10
|
+
sciforge_build/include/sciforge/binding/gil.hpp
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sciforge_build
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Vendor the canonical <sciforge/binding/...> headers into the package at build time.
|
|
2
|
+
|
|
3
|
+
The headers live once, canonically, at the repo's include/sciforge/binding/. Copying
|
|
4
|
+
them under the package (rather than committing a second copy) keeps a single source of
|
|
5
|
+
truth while making the wheel + sdist self-contained — a consumer can build its
|
|
6
|
+
extension purely from `pip install sciforge-build`, with no sibling checkout.
|
|
7
|
+
"""
|
|
8
|
+
import pathlib
|
|
9
|
+
import shutil
|
|
10
|
+
|
|
11
|
+
from setuptools import setup
|
|
12
|
+
|
|
13
|
+
_HERE = pathlib.Path(__file__).resolve().parent
|
|
14
|
+
_CANONICAL = _HERE.parent.parent / "include" / "sciforge" / "binding"
|
|
15
|
+
_VENDORED = _HERE / "sciforge_build" / "include" / "sciforge" / "binding"
|
|
16
|
+
|
|
17
|
+
if _CANONICAL.is_dir():
|
|
18
|
+
_VENDORED.parent.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
if _VENDORED.exists():
|
|
20
|
+
shutil.rmtree(_VENDORED)
|
|
21
|
+
shutil.copytree(_CANONICAL, _VENDORED)
|
|
22
|
+
|
|
23
|
+
setup()
|