Skip to content

Commit

Permalink
Implementation details for clair change (fix 4) and any
Browse files Browse the repository at this point in the history
  • Loading branch information
parcollet committed Nov 12, 2024
1 parent 8ea8c90 commit 45193ca
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 42 deletions.
52 changes: 52 additions & 0 deletions src/c2py/converters/stl/any.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once
#include <any>
#include <cmath>
#include "./common.hpp"

namespace c2py {

template <> struct py_converter<std::any> {

static void capsule_destructor(PyObject *capsule) {
void *p = PyCapsule_GetPointer(capsule, "std::any");
assert(p);
auto *a_ptr = static_cast<std::any *>(p);
delete a_ptr; //NOLINT
}

template <typename U> static PyObject *c2py(U &&u) {
auto *p = new std::any{std::forward<U>(u)}; //NOLINT
return PyCapsule_New(p, "std::any", capsule_destructor);
}

// --------------------------------------

static bool is_convertible(PyObject *ob, bool raise_exception) {
bool ok = (PyCapsule_CheckExact(ob) and (std::string_view{PyCapsule_GetName(ob)} == "std::any"));
if (!ok and raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::any"s).c_str()); }
return ok;
}

// --------------------------------------

static std::any &py2c(PyObject *ob) {
void *p = PyCapsule_GetPointer(ob, "std::any");
assert(p);
return *static_cast<std::any *>(p);
}
};

// -------------------------------------------------------

template <typename T> struct py_converter_as_any : py_converter<std::any> {

using base_t = py_converter<std::any>;

static bool is_convertible(PyObject *ob, bool raise_exception) {
if (!base_t::is_convertible(ob, raise_exception)) return false;
return base_t::py2c(ob).type() == std::type_index(typeid(T));
}

static T &py2c(PyObject *ob) { return std::any_cast<T &>(base_t::py2c(ob)); }
};
} // namespace c2py
2 changes: 1 addition & 1 deletion src/c2py/user_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// ------------- Annotations for function ---------------

#define C2PY_IGNORE __attribute__((annotate("c2py_ignore")))
#define C2PY_WRAP __attribute__((annotate("c2py_wrap")))
#define C2PY_OPAQUE __attribute__((annotate("c2py_wrap_as_opaque")))
#define C2PY_NOGIL __attribute__((annotate("c2py_nogil")))

//#define C2PY_METHODS_AS_PROPERTY __attribute__((annotate("c2py_methods_as_property")))
Expand Down
4 changes: 3 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ add_custom_command(
# ----------------------------
set(c2py_all_low_level_tests cls CACHE INTERNAL "")
set(c2py_all_full_tests
annote basicfun
any
basicfun
cls_basic
cls_der
comparison
issue9
callables
itertool
ignore
generator
enumcxx
synth_init
Expand Down
22 changes: 0 additions & 22 deletions test/annote.cpp

This file was deleted.

18 changes: 0 additions & 18 deletions test/annote_test.py

This file was deleted.

46 changes: 46 additions & 0 deletions test/any.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <c2py/c2py.hpp>
#include <c2py/converters/stl/any.hpp>
#include <complex>

struct C2PY_OPAQUE opaque {
int c = 17; // not exposed
int *non_convertible = 0;
int f(int x) { return x + 1; }; // not exposed
};

// maker
opaque make_opaque() { return {}; }

// passing the object back from Python
int take_opaque(opaque const &x) { return x.c; }

// we can modify the object passed from Python
int inc_opaque(opaque &x) { return ++(x.c); }

// -------------------------------------
/// Same test again, with the regex

struct opaque2 {
int c = 17; // not exposed
int *non_convertible = 0;
int f(int x) { return x + 1; }; // not exposed
};

namespace NN {
// maker
opaque2 make_opaque2() { return {}; }

// passing the object back from Python
int take_opaque2(opaque2 const &x) { return x.c; }

// we can modify the object passed from Python
int inc_opaque2(opaque2 &x) { return ++(x.c); }
} // namespace NN
// -------------------------------------------------

namespace c2py_module {
#pragma clang diagnostic ignored "-Wunused-const-variable"

constexpr auto opaque_match_names = ".*opaque2";

} // namespace c2py_module
33 changes: 33 additions & 0 deletions test/any_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import unittest
import numpy as np
import any as A

def is_capsule(o):
t = type(o)
return t.__module__ == 'builtins' and t.__name__ == 'PyCapsule'

class TestIterable(unittest.TestCase):

def test_opaque(self):
a = A.make_opaque()
self.assertTrue(is_capsule(a))
self.assertEqual(A.take_opaque(a), 17)
self.assertEqual(A.inc_opaque(a), 18)
self.assertEqual(A.inc_opaque(a), 19)
self.assertEqual(A.take_opaque(a), 19)


def test_opaque2(self):
a = A.make_opaque2()
self.assertTrue(is_capsule(a))
self.assertEqual(A.take_opaque2(a), 17)
self.assertEqual(A.inc_opaque2(a), 18)
self.assertEqual(A.inc_opaque2(a), 19)
self.assertEqual(A.take_opaque2(a), 19)


if __name__ == '__main__':
unittest.main()



23 changes: 23 additions & 0 deletions test/ignore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

#include <vector>
#include <iostream>
#include <c2py/c2py.hpp>

struct a_struct {
int a;
C2PY_IGNORE int b = 1;
double x_ignore_me = 0; // should be ignored by the regex

C2PY_IGNORE int bad_method() { return 0; }
int method_ignore_me() { return 1; }
};
// =============== Declare module ===========================

namespace c2py_module {

constexpr auto reject_names = ".*ignore_me";

} // namespace c2py_module
38 changes: 38 additions & 0 deletions test/ignore_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import unittest
import numpy as np
import pickle

import ignore as M
A = M.AStruct

class TestIterable(unittest.TestCase):

def test_ignore(self):
a = A(a = 10)
print(a.a) # ok

def f():
A(a= 10, b =2)
self.assertRaises(RuntimeError, f)

def f(x):
print(x.b)
self.assertRaises(AttributeError, f, a)

def f(x):
print(x.x_ignore_me)
self.assertRaises(AttributeError, f, a)

def f(x):
x.bad_method(1)
self.assertRaises(AttributeError, f, a)

def f(x):
x.method_ignore_me(1)
self.assertRaises(AttributeError, f, a)


if __name__ == '__main__':
unittest.main()


0 comments on commit 45193ca

Please sign in to comment.