Skip to content

Commit

Permalink
Merge branch 'master' into globals
Browse files Browse the repository at this point in the history
  • Loading branch information
wjakob authored Oct 6, 2023
2 parents 217c5a5 + 7e4a88b commit d710590
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 13 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
fail-fast: false
matrix:
os: ['ubuntu-latest', 'windows-2022', 'macos-latest']
python: ['3.8', '3.9', '3.10', '3.11', '3.12.0', 'pypy3.9', 'pypy3.10']
python: ['3.8', '3.9', '3.10', '3.11', '3.12.0', 'pypy3.9-v7.3.12', 'pypy3.10-v7.3.12']

name: "Python ${{ matrix.python }} / ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
Expand All @@ -48,7 +48,7 @@ jobs:
python -m pip install pytest pytest-github-actions-annotate-failures
- name: Install NumPy
if: matrix.python != 'pypy3.9' && matrix.python != 'pypy3.10' && matrix.python != '3.12.0'
if: ${{ !startsWith(matrix.python, 'pypy') }}
run: |
python -m pip install numpy scipy
Expand Down
5 changes: 5 additions & 0 deletions docs/api_core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,11 @@ Global flags
implicit conversion, and when that conversion is not successful. Call this
function to disable or re-enable the warnings.

.. cpp:function:: inline bool is_alive() noexcept

The function returns ``true`` when nanobind is initialized and ready for
use. It returns ``false`` when the Python interpreter has shut down, causing
the destruction various nanobind-internal data structures.

Miscellaneous
-------------
Expand Down
2 changes: 2 additions & 0 deletions include/nanobind/nb_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ NB_CORE void slice_compute(PyObject *slice, Py_ssize_t size,
NB_CORE PyObject *repr_list(PyObject *o);
NB_CORE PyObject *repr_map(PyObject *o);

NB_CORE bool is_alive() noexcept;

#if NB_TYPE_GET_SLOT_IMPL
NB_CORE void *type_get_slot(PyTypeObject *t, int slot_id);
#endif
Expand Down
6 changes: 5 additions & 1 deletion include/nanobind/nb_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ inline void set_implicit_cast_warnings(bool value) noexcept {
inline dict globals() {
PyObject *p = PyEval_GetGlobals();
if (!p)
detail::raise_python_error();
detail::raise("nanobind::globals(): no frame is currently executing!");
return borrow<dict>(p);
}

inline bool is_alive() noexcept {
return detail::is_alive();
}

NAMESPACE_END(NB_NAMESPACE)
6 changes: 6 additions & 0 deletions src/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,12 @@ PyObject **seq_get(PyObject *seq, size_t *size_out, PyObject **temp_out) noexcep
goes wrong, it fails gracefully without reporting errors. Other
overloads will then be tried. */

if (PyUnicode_CheckExact(seq) || PyBytes_CheckExact(seq)) {
*size_out = 0;
*temp_out = nullptr;
return nullptr;
}

#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION)
if (PyTuple_CheckExact(seq)) {
size = (size_t) PyTuple_GET_SIZE(seq);
Expand Down
29 changes: 19 additions & 10 deletions src/nb_internals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

/// Tracks the ABI of nanobind
#ifndef NB_INTERNALS_VERSION
# define NB_INTERNALS_VERSION 10
# define NB_INTERNALS_VERSION 11
#endif

/// On MSVC, debug and release builds are not ABI-compatible!
Expand Down Expand Up @@ -220,11 +220,20 @@ void default_exception_translator(const std::exception_ptr &p, void *) {
nb_internals *internals = nullptr;
PyTypeObject *nb_meta_cache = nullptr;

#if !defined(PYPY_VERSION)
static bool is_alive_value = false;
static bool *is_alive_ptr = &is_alive_value;
bool is_alive() noexcept { return *is_alive_ptr; }

static void internals_cleanup() {
if (!internals)
return;

*is_alive_ptr = false;

#if !defined(PYPY_VERSION)
/* The memory leak checker is unsupported on PyPy, see
see https://foss.heptapod.net/pypy/pypy/-/issues/3855 */

bool leak = false;

if (!internals->inst_c2p.empty()) {
Expand Down Expand Up @@ -286,12 +295,12 @@ static void internals_cleanup() {
"counting issue in the binding code.\n");
}

#if NB_ABORT_ON_LEAK == 1
abort(); // Extra-strict behavior for the CI server
#endif
#if NB_ABORT_ON_LEAK == 1
abort(); // Extra-strict behavior for the CI server
#endif
}
}
#endif
}

NB_NOINLINE void init(const char *name) {
if (internals)
Expand All @@ -317,6 +326,7 @@ NB_NOINLINE void init(const char *name) {
check(internals,
"nanobind::detail::internals_fetch(): capsule pointer is NULL!");
nb_meta_cache = internals->nb_meta;
is_alive_ptr = internals->is_alive_ptr;
return;
}

Expand Down Expand Up @@ -360,6 +370,9 @@ NB_NOINLINE void init(const char *name) {
#endif

p->translators = { default_exception_translator, nullptr, nullptr };
is_alive_value = true;
is_alive_ptr = &is_alive_value;
p->is_alive_ptr = is_alive_ptr;

#if PY_VERSION_HEX < 0x030C0000 && !defined(PYPY_VERSION)
/* The implementation of typing.py on CPython <3.12 tends to introduce
Expand Down Expand Up @@ -395,17 +408,13 @@ NB_NOINLINE void init(const char *name) {
}
#endif

#if !defined(PYPY_VERSION)
/* Install the memory leak checker. This feature is unsupported on
PyPy, see https://foss.heptapod.net/pypy/pypy/-/issues/3855 */
if (Py_AtExit(internals_cleanup))
fprintf(stderr,
"Warning: could not install the nanobind cleanup handler! This "
"is needed to check for reference leaks and release remaining "
"resources at interpreter shutdown (e.g., to avoid leaks being "
"reported by tools like 'valgrind'). If you are a user of a "
"python extension library, you can ignore this warning.");
#endif

capsule = PyCapsule_New(p, "nb_internals", nullptr);
int rv = PyDict_SetItem(dict, key, capsule);
Expand Down
3 changes: 3 additions & 0 deletions src/nb_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ struct nb_internals {
/// Should nanobind print warnings after implicit cast failures?
bool print_implicit_cast_warnings = true;

/// Pointer to a boolean that denotes if nanobind is fully initialized.
bool *is_alive_ptr = nullptr;

#if defined(Py_LIMITED_API)
// Cache important functions from PyType_Type and PyProperty_Type
freefunc PyType_Type_tp_free;
Expand Down
1 change: 1 addition & 0 deletions src/nb_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,7 @@ void keep_alive(PyObject *nurse, PyObject *patient) {
} else {
PyObject *callback =
PyCFunction_New(&keep_alive_callback_def, patient);

PyObject *weakref = PyWeakref_NewRef(nurse, callback);
if (!weakref) {
Py_DECREF(callback);
Expand Down
6 changes: 6 additions & 0 deletions tests/test_stl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,4 +439,10 @@ NB_MODULE(test_stl_ext, m) {
return x;
});

m.def("vector_str", [](const std::vector<std::string>& x){
return x;
});
m.def("vector_str", [](std::string& x){
return x;
});
}
4 changes: 4 additions & 0 deletions tests/test_stl.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,3 +820,7 @@ def test69_complex_array():
assert t.complex_array_float(np.array([val1_64, -1j, val2_64],dtype=np.complex64)) == [val1_32, (-0-1j), val2_32]
except ImportError:
pass

def test70_vec_char():
assert isinstance(t.vector_str("123"), str)
assert isinstance(t.vector_str(["123", "345"]), list)

0 comments on commit d710590

Please sign in to comment.