some fix.

This commit is contained in:
ykiko 2024-08-23 00:08:16 +08:00
parent 57e8cf651b
commit c6e24c69a2
16 changed files with 508 additions and 112 deletions

93
.github/workflows/pybind11.yml vendored Normal file
View File

@ -0,0 +1,93 @@
name: CMake Build and Test
on:
push:
branches:
- pkpy-v2
pull_request:
branches:
- pkpy-v2
jobs:
build_linux:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up GCC
run: |
sudo apt-get update
sudo apt-get install -y gcc g++
- name: Set up CMake
uses: jwlawson/actions-setup-cmake@v1.10
- name: Install dependencies
run: |
git submodule update --init --recursive
git clone https://github.com/google/googletest.git
- name: Build
run: |
cmake -B build -DENABLE_TEST=ON
cmake --build build --config Release --parallel
- name: Test
run: |
build/pybind11_test
build_win:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up MSVC
uses: ilammy/msvc-dev-cmd@v1
- name: Set up CMake
uses: jwlawson/actions-setup-cmake@v1.10
- name: Install dependencies
run: |
git submodule update --init --recursive
git clone https://github.com/google/googletest.git
- name: Build
run: |
cmake -B build -DENABLE_TEST=ON
cmake --build build --config Release --parallel
- name: Test
run: |
build\Release\pybind11_test.exe
build_mac:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Clang
run: |
brew install llvm
echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
- name: Set up CMake
uses: jwlawson/actions-setup-cmake@v1.10
- name: Install dependencies
run: |
git submodule update --init --recursive
git clone https://github.com/google/googletest.git
- name: Build
run: |
cmake -B build -DENABLE_TEST=ON
cmake --build build --config Release --parallel
- name: Test
run: |
build/pybind11_test

View File

@ -18,7 +18,9 @@ class accessor : public interface<accessor<policy>> {
public:
auto ptr() const {
if(!m_value) { m_value = policy::get(m_obj, m_key); }
if(m_value.empty()) {
m_value = borrow(policy::get(m_obj, m_key));
}
return m_value.ptr();
}
@ -38,11 +40,12 @@ public:
private:
handle m_obj;
mutable handle m_value;
mutable object m_value;
key_type m_key;
};
namespace policy {
struct attr {
using key_type = name;
@ -136,6 +139,11 @@ inline item_accessor<handle> interface<Derived>::operator[] (handle key) const {
return {ptr(), key};
}
template <typename... Args>
object str::format(Args&&... args) {
return attr("format")(std::forward<Args>(args)...);
}
inline tuple_accessor tuple::operator[] (int index) const { return {m_ptr, index}; }
inline list_accessor list::operator[] (int index) const { return {m_ptr, index}; };
@ -148,9 +156,9 @@ inline dict_accessor<handle> dict::operator[] (handle key) const { return {m_ptr
inline dict::iterator::iterator(handle h) : items(h.attr("items")()), iter(items.begin()) {}
inline std::pair<handle, handle> dict::iterator::operator* () const {
tuple pair = tuple(*iter, object::ref_t{});
return {pair[0], pair[1]};
inline std::pair<object, object> dict::iterator::operator* () const {
tuple pair = *iter;
return {borrow(pair[0]), borrow(pair[1])};
}
} // namespace pkbind

View File

@ -72,6 +72,8 @@ inline py_i64 hash(handle obj) {
inline bool isinstance(handle obj, type type) { return py_isinstance(obj.ptr(), type.index()); }
inline bool python_error::match(type type) const { return isinstance(m_exception.ptr(), type); }
template <typename T>
constexpr inline bool is_pyobject_v = std::is_base_of_v<object, std::decay_t<T>> || std::is_same_v<type, T>;
@ -173,13 +175,12 @@ T cast(handle obj, bool convert = true) {
return std::move(caster.value());
}
} else {
std::string msg = "cast python instance to c++ failed, ";
msg += "obj type is: {";
std::string msg = "cast python instance to c++ failed, obj type is: {";
msg += type::of(obj).name();
msg += "}, target type is: {";
msg += type_name<T>();
msg += "}.";
throw std::runtime_error(msg);
throw cast_error(msg);
}
}

View File

@ -31,9 +31,9 @@ struct type_caster {
static_assert(!std::is_reference_v<T>, "type caster for reference type must be specialized.");
template <typename U>
static object cast(U&& value, return_value_policy policy, handle parent = none()) {
static object cast(U&& value, return_value_policy policy, handle parent) {
// TODO: support implicit cast
return instance::create(std::forward<U>(value), type::of<T>(), policy, parent.ptr());
return instance::create(type::of<T>(), std::forward<U>(value), parent, policy);
}
bool load(handle src, bool convert) {

View File

@ -37,16 +37,10 @@ public:
[[maybe_unused]] auto args = py_offset(stack, 1);
[[maybe_unused]] auto kwargs = py_offset(stack, 2);
// if constexpr(types_count_v<dynamic_attr, Args...> != 0) {
// // FIXME:
// // bind dynamic attributes
// }
auto type = pkbind::type(cls, object::ref_t{});
auto info = &type_info::of<T>();
void* data = py_newobject(py_retval(), type.index(), 1, sizeof(instance));
int slot = (type_list<Args...>::template count<dynamic_attr> ? -1 : 0);
void* data = py_newobject(retv, steal<type>(cls).index(), slot, sizeof(instance));
new (data) instance{instance::Flag::Own, operator new (info->size), info};
return true;
},
nullptr,

View File

@ -16,6 +16,10 @@ public:
~python_error() { std::free(m_what); }
bool match(py_Type type) const { return py_isinstance(m_exception.ptr(), type); }
bool match(class type) const;
private:
char* m_what;
object m_exception;
@ -30,19 +34,54 @@ inline auto raise_call(Args&&... args) {
using type = decltype(result);
if constexpr(std::is_same_v<type, bool>) {
if(result != false) { return result; }
if(result != false) {
return result;
}
} else if constexpr(std::is_same_v<type, int>) {
if(result != -1) { return result; }
if(result != -1) {
return result;
}
} else {
static_assert(dependent_false<type>, "invalid return type");
}
bool o = py_matchexc(tp_Exception);
object e = object::from_ret();
auto what = py_formatexc();
py_matchexc(tp_Exception);
py_clearexc(pc);
throw python_error(what, object(retv, object::realloc_t{}));
throw python_error(what, std::move(e));
}
class stop_iteration {};
class cast_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
class index_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
class key_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
class value_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
class type_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
class import_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
class attribute_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
inline object::operator bool () const { return raise_call<py_bool>(m_ptr); }
#define PKBIND_BINARY_OPERATOR(name, lop, rop) \

View File

@ -130,10 +130,14 @@ class function_record {
friend struct template_parser;
using destructor_t = void (*)(function_record*);
using wrapper_t = bool (*)(function_record&, std::vector<handle>&, dict&, bool convert, handle parent);
using wrapper_t = bool (*)(function_record&,
std::vector<handle>&,
std::vector<std::pair<handle, handle>>&,
bool convert,
handle parent);
struct arguments_t {
std::vector<name> names;
std::vector<std::string> names;
std::vector<object> defaults;
};
@ -174,10 +178,18 @@ public:
function_record& operator= (function_record&&) = delete;
~function_record() {
if(destructor) { destructor(this); }
if(arguments) { delete arguments; }
if(next) { delete next; }
if(signature) { delete[] signature; }
if(destructor) {
destructor(this);
}
if(arguments) {
delete arguments;
}
if(next) {
delete next;
}
if(signature) {
delete[] signature;
}
}
void append(function_record* record) {
@ -202,32 +214,44 @@ public:
return *static_cast<function_record*>(py_touserdata(slot));
}
bool operator() (int argc, handle stack) {
void operator() (int argc, handle stack) {
function_record* p = this;
bool has_self = argc == 3;
std::vector<handle> args2;
if(has_self) { args2.push_back(py_offset(stack.ptr(), 0)); }
tuple args(py_offset(stack.ptr(), 0 + has_self), object::ref_t{});
for(int i = 0; i < args.size(); ++i) {
args2.push_back(args[i]);
std::vector<handle> args;
handle self = py_offset(stack.ptr(), 0);
if(has_self) {
args.push_back(self);
}
dict kwargs(py_offset(stack.ptr(), 1 + has_self), object::ref_t{});
auto tuple = py_offset(stack.ptr(), 0 + has_self);
for(int i = 0; i < py_tuple_len(tuple); ++i) {
args.push_back(py_tuple_getitem(tuple, i));
}
auto dict = steal<pkbind::dict>(py_offset(stack.ptr(), 1 + has_self));
std::vector<std::pair<handle, handle>> kwargs;
dict.apply([&](handle key, handle value) {
kwargs.emplace_back(key, value);
});
// foreach function record and call the function with not convert
while(p != nullptr) {
auto result = p->wrapper(*p, args2, kwargs, false, {});
if(result) { return result; }
auto result = p->wrapper(*p, args, kwargs, false, self);
if(result) {
return;
}
p = p->next;
}
p = this;
// foreach function record and call the function with convert
while(p != nullptr) {
auto result = p->wrapper(*p, args2, kwargs, true, {});
if(result) { return result; }
auto result = p->wrapper(*p, args, kwargs, true, self);
if(result) {
return;
}
p = p->next;
}
@ -344,7 +368,9 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
static void initialize(function_record& record, const Extras&... extras) {
auto extras_tuple = std::make_tuple(extras...);
constexpr static bool has_named_args = (named_argc > 0);
if constexpr(policy_pos != -1) { record.policy = std::get<policy_pos>(extras_tuple); }
if constexpr(policy_pos != -1) {
record.policy = std::get<policy_pos>(extras_tuple);
}
// TODO: set others
@ -382,17 +408,17 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
sig += record.arguments->names[index].c_str();
sig += ": ";
sig += type_info::of<T>().name;
if(!record.arguments->defaults[index].is_empty()) {
if(!record.arguments->defaults[index].empty()) {
sig += " = ";
// FIXME:
// sig += record.arguments->defaults[index].repr();
sig += record.arguments->defaults[index].attr("__repr__")().cast<std::string_view>();
}
} else {
sig += "_: ";
sig += type_info::of<T>().name;
}
if(index + 1 < argc) { sig += ", "; }
if(index + 1 < argc) {
sig += ", ";
}
index++;
};
(append(type_identity<Args>{}), ...);
@ -406,7 +432,11 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
/// try to call a C++ function(store in function_record) with the arguments which are from Python.
/// if success, return true, otherwise return false.
static bool call(function_record& record, std::vector<handle>& args, dict& kwargs, bool convert, handle parent) {
static bool call(function_record& record,
std::vector<handle>& args,
std::vector<std::pair<handle, handle>>& kwargs,
bool convert,
handle parent) {
// first, we try to load arguments into the stack.
// use argc + 1 to avoid compile error when argc is 0.
handle stack[argc + 1] = {};
@ -421,48 +451,58 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
// load arguments from call arguments
if(args.size() > normal_argc) {
if constexpr(args_pos == -1) { return false; }
if constexpr(args_pos == -1) {
return false;
}
}
for(std::size_t i = 0; i < std::min(normal_argc, (int)args.size()); ++i) {
stack[i] = args[i];
}
object repack_args;
// pack the args
if constexpr(args_pos != -1) {
const auto n = args.size() > normal_argc ? args.size() - normal_argc : 0;
reg<0> = tuple(n);
stack[args_pos] = reg<0>;
auto pack = tuple(reg<0>, object::ref_t{});
auto pack = tuple(n);
for(std::size_t i = 0; i < n; ++i) {
pack[i] = args[normal_argc + i];
}
repack_args = std::move(pack);
stack[args_pos] = repack_args;
}
// pack the kwargs
int index = 0;
bool init_dict = false;
dict pack2;
kwargs.apply([&](handle key, handle value) {
if constexpr(named_argc != 0) {
for(int i = index; i < named_argc; ++i) {
if(key.cast<std::string_view>() == record.arguments->names[i]) {
stack[i] = value;
index = i + 1;
return;
int arg_index = 0;
while(arg_index < named_argc && index < kwargs.size()) {
const auto name = kwargs[index].first;
const auto value = kwargs[index].second;
if(name.cast<std::string_view>() == record.arguments->names[arg_index]) {
stack[arg_index] = value;
index += 1;
}
arg_index += 1;
}
}
if constexpr(kwargs_pos != -1) { pack2[key] = value; }
});
if constexpr(kwargs_pos != -1) { stack[kwargs_pos] = pack2; }
object repacked_kwargs;
if constexpr(kwargs_pos != -1) {
auto pack = dict();
while(index < kwargs.size()) {
pack[kwargs[index].first] = kwargs[index].second;
index += 1;
}
repacked_kwargs = std::move(pack);
stack[kwargs_pos] = repacked_kwargs;
}
// check if all the arguments are valid
for(std::size_t i = 0; i < argc; ++i) {
if(!stack[i]) { return false; }
if(!stack[i]) {
return false;
}
}
// ok, all the arguments are valid, call the function
@ -493,7 +533,9 @@ class cpp_function : public function {
static bool is_function_record(handle h) {
if(isinstance<function>(h)) {
auto slot = py_getslot(h.ptr(), 0);
if(slot) { return py_typeof(slot) == m_type; }
if(slot) {
return py_typeof(slot) == m_type;
}
}
return false;
}
@ -505,8 +547,39 @@ class cpp_function : public function {
sig += is_method ? "(self, *args, **kwargs)" : "(*args, **kwargs)";
auto call = [](int argc, py_Ref stack) {
handle func = py_inspect_currentfunction();
auto& record = *static_cast<impl::function_record*>(py_touserdata(py_getslot(func.ptr(), 0)));
return record(argc, stack);
auto data = py_touserdata(py_getslot(func.ptr(), 0));
auto& record = *static_cast<impl::function_record*>(data);
try {
record(argc, stack);
return true;
} catch(std::domain_error& e) {
py_exception(tp_ValueError, e.what());
} catch(std::invalid_argument& e) {
py_exception(tp_ValueError, e.what());
} catch(std::length_error& e) {
py_exception(tp_ValueError, e.what());
} catch(std::out_of_range& e) {
py_exception(tp_IndexError, e.what());
} catch(std::range_error& e) {
py_exception(tp_ValueError, e.what());
} catch(stop_iteration& e) {
StopIteration();
} catch(index_error& e) {
py_exception(tp_IndexError, e.what());
} catch(key_error& e) {
py_exception(tp_KeyError, e.what());
} catch(value_error& e) {
py_exception(tp_ValueError, e.what());
} catch(type_error& e) {
py_exception(tp_TypeError, e.what());
} catch(import_error& e) {
py_exception(tp_ImportError, e.what());
} catch(attribute_error& e) {
py_exception(tp_AttributeError, e.what());
} catch(std::exception& e) {
py_exception(tp_RuntimeError, e.what());
}
return false;
};
py_newfunction(m_ptr, sig.c_str(), call, nullptr, 1);

View File

@ -29,8 +29,7 @@ struct type_info {
};
// all registered C++ class will be ensured as instance type.
class instance {
public:
struct instance {
// use to record the type information of C++ class.
enum Flag {
None = 0,
@ -41,13 +40,11 @@ public:
Flag flag;
void* data;
const type_info* info;
object parent;
public:
template <typename Value>
static object create(Value&& value_,
type type,
return_value_policy policy = return_value_policy::automatic_reference,
handle parent_ = {}) {
static object create(type type, Value&& value_, handle parent_, return_value_policy policy) {
using underlying_type = remove_cvref_t<Value>;
auto& value = [&]() -> auto& {
@ -66,7 +63,7 @@ public:
void* data = nullptr;
Flag flag = Flag::None;
handle parent = parent_;
object parent;
if(policy == return_value_policy::take_ownership) {
data = &value;
@ -95,16 +92,19 @@ public:
} else if(policy == return_value_policy::reference_internal) {
data = &value;
flag = Flag::Ref;
parent = borrow(parent_);
}
object result(object::alloc_t{});
void* temp = py_newobject(result.ptr(), type.index(), 1, sizeof(instance));
new (temp) instance{flag, data, info};
new (temp) instance{flag, data, info, std::move(parent)};
return result;
}
~instance() {
if(flag & Flag::Own) { info->destructor(data); }
if(flag & Flag::Own) {
info->destructor(data);
}
}
template <typename T>

View File

@ -198,7 +198,9 @@ public:
object() = default;
object(const object& other) : handle(other), m_index(other.m_index) {
if(other.in_pool()) { object_pool::inc_ref(other); }
if(other.in_pool()) {
object_pool::inc_ref(other);
}
}
object(object&& other) : handle(other), m_index(other.m_index) {
@ -208,8 +210,12 @@ public:
object& operator= (const object& other) {
if(this != &other) {
if(in_pool()) { object_pool::dec_ref(*this); }
if(other.in_pool()) { object_pool::inc_ref(other); }
if(in_pool()) {
object_pool::dec_ref(*this);
}
if(other.in_pool()) {
object_pool::inc_ref(other);
}
m_ptr = other.m_ptr;
m_index = other.m_index;
}
@ -218,7 +224,9 @@ public:
object& operator= (object&& other) {
if(this != &other) {
if(in_pool()) { object_pool::dec_ref(*this); }
if(in_pool()) {
object_pool::dec_ref(*this);
}
m_ptr = other.m_ptr;
m_index = other.m_index;
other.m_ptr = nullptr;
@ -228,12 +236,14 @@ public:
}
~object() {
if(in_pool()) { object_pool::dec_ref(*this); }
if(in_pool()) {
object_pool::dec_ref(*this);
}
}
bool is_singleton() const { return m_ptr && m_index == -1; }
bool is_empty() const { return m_ptr == nullptr && m_index == -1; }
bool empty() const { return m_ptr == nullptr && m_index == -1; }
/// return whether the object is in the object pool.
bool in_pool() const { return m_ptr && m_index != -1; }
@ -271,6 +281,16 @@ protected:
int m_index = -1;
};
template <typename T = object>
T steal(handle h) {
return T(h, object::ref_t{});
}
template <typename T = object>
T borrow(handle h) {
return T(h, object::realloc_t{});
}
template <int N>
void reg_t<N>::operator= (handle h) & {
py_setreg(N, h.ptr());

View File

@ -13,8 +13,8 @@ private:
public: \
using parent::parent; \
using parent::operator=; \
child(const object& o) : parent(o) {} \
child(object&& o) : parent(std::move(o)) {}
child(const object& o) : parent(type::isinstance<child>(o) ? o : type::of<child>()(o)) {} \
child(object&& o) : parent(type::isinstance<child>(o) ? std::move(o) : type::of<child>()(std::move(o))) {}
class type : public object {
protected:
@ -106,7 +106,7 @@ public:
return *this;
}
handle operator* () const { return m_value; }
object operator* () const { return m_value; }
friend bool operator== (const iterator& lhs, const iterator& rhs) { return lhs.m_value.ptr() == rhs.m_value.ptr(); }
@ -137,6 +137,9 @@ class str : public object {
str(const char* data) : str(data, strlen(data)) {}
str(std::string_view s) : str(s.data(), static_cast<int>(s.size())) {}
template <typename... Args>
object format(Args&&... args);
};
class tuple : public object {
@ -172,7 +175,7 @@ class tuple : public object {
return *this;
}
handle operator* () const { return py_tuple_getitem(ptr, index); }
object operator* () const { return borrow(py_tuple_getitem(ptr, index)); }
private:
py_Ref ptr;
@ -231,7 +234,7 @@ class list : public object {
return *this;
}
handle operator* () const { return py_list_getitem(ptr, index); }
object operator* () const { return borrow(py_list_getitem(ptr, index)); }
private:
py_Ref ptr;
@ -295,7 +298,7 @@ class dict : public object {
return *this;
}
std::pair<handle, handle> operator* () const;
std::pair<object, object> operator* () const;
private:
object items;
@ -335,7 +338,9 @@ class capsule : public object {
static void register_() {
m_type = py_newtype("capsule", tp_object, nullptr, [](void* data) {
auto impl = static_cast<capsule_impl*>(data);
if(impl->data && impl->destructor) { impl->destructor(impl->data); }
if(impl->data && impl->destructor) {
impl->destructor(impl->data);
}
});
}

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.10)
project(PKBIND_TEST)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../.." "${CMAKE_CURRENT_BINARY_DIR}/pocketpy")
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
DOWNLOAD_EXTRACT_TIMESTAMP true
)
if(WIN32)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
FetchContent_MakeAvailable(googletest)
file(GLOB CPP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
add_executable(PKBIND_TEST ${CPP_SOURCES})
target_include_directories(PKBIND_TEST PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
target_link_libraries(PKBIND_TEST PRIVATE pocketpy gtest_main)

View File

@ -158,7 +158,7 @@ assert p.z == 30
EXPECT_EQ(constructor_calls, 4);
}
// FIXME:
TEST_F(PYBIND11_TEST, dynamic_attr) {
py::module_ m = py::module_::import("__main__");
@ -178,8 +178,8 @@ TEST_F(PYBIND11_TEST, dynamic_attr) {
EXPECT_EQ(p.attr("x").cast<int>(), 1);
EXPECT_EQ(p.attr("y").cast<int>(), 2);
// p.attr("z") = py::int_(3);
// EXPECT_EQ(p.attr("z").cast<int>(), 3);
p.attr("z") = py::int_(3);
EXPECT_EQ(p.attr("z").cast<int>(), 3);
}
TEST_F(PYBIND11_TEST, enum) {

View File

@ -0,0 +1,50 @@
#include "test.h"
TEST_F(PYBIND11_TEST, exception_python_to_cpp) {
auto test = [](const char* code, py_Type type) {
try {
py::exec(code);
} catch(py::python_error& e) {
EXPECT_TRUE(e.match(type));
}
};
test("raise ValueError()", tp_ValueError);
test("raise KeyError()", tp_KeyError);
test("raise IndexError()", tp_IndexError);
test("raise StopIteration()", tp_StopIteration);
test("raise Exception()", tp_Exception);
}
TEST_F(PYBIND11_TEST, exception_cpp_to_python) {
auto m = py::module_::__main__();
#define TEST_EXCEPTION(cppe, pye) \
m.def("test_" #cppe, []() { \
throw cppe(#cppe); \
}); \
py::exec("try:\n test_" #cppe "()\nexcept Exception as e:\n assert isinstance(e, " #pye \
")\n assert str(e) == '" #cppe "'")
using namespace std;
TEST_EXCEPTION(runtime_error, RuntimeError);
TEST_EXCEPTION(domain_error, ValueError);
TEST_EXCEPTION(invalid_argument, ValueError);
TEST_EXCEPTION(length_error, ValueError);
TEST_EXCEPTION(out_of_range, IndexError);
TEST_EXCEPTION(range_error, ValueError);
using namespace py;
m.def("test_stop_iteration", []() {
throw py::stop_iteration();
});
py::exec("try:\n test_stop_iteration()\nexcept StopIteration as e:\n pass");
TEST_EXCEPTION(index_error, IndexError);
// FIXME: TEST_EXCEPTION(key_error, KeyError);
TEST_EXCEPTION(value_error, ValueError);
TEST_EXCEPTION(type_error, TypeError);
TEST_EXCEPTION(import_error, ImportError);
TEST_EXCEPTION(attribute_error, AttributeError);
TEST_EXCEPTION(runtime_error, RuntimeError);
}

View File

@ -2,6 +2,31 @@
namespace {
TEST_F(PYBIND11_TEST, vectorcall) {
auto m = py::module_::__main__();
py::exec(R"(
def add(a, b):
return a + b
)");
// position only
EXPECT_CAST_EQ(m.attr("add")(1, 2), 3);
// FIXME: pkpy does not support such calling.
// keyword only
// EXPECT_CAST_EQ(m.attr("add")(py::arg("a") = 1, py::arg("b") = 2), 3);
// mix
// EXPECT_CAST_EQ(m.attr("add")(1, py::arg("b") = 2), 3);
py::exec(R"(
def add2(a, *args):
return a + sum(args)
)");
EXPECT_CAST_EQ(m.attr("add2")(1, 2, 3, 4), 10);
EXPECT_EQ(py::type::of<py::tuple>()(py::eval("[1, 2, 3]")), py::eval("(1, 2, 3)"));
EXPECT_EQ(py::type::of<py::list>()(py::eval("(1, 2, 3)")), py::eval("[1, 2, 3]"));
}
TEST_F(PYBIND11_TEST, constructor) {
auto m = py::module_::__main__();

View File

@ -63,11 +63,11 @@ TEST_F(PYBIND11_TEST, object) {
py::exec("p = Point(3, 4)");
py::object p = py::eval("p");
// test is
// is
EXPECT_FALSE(p.is_none());
EXPECT_TRUE(p.is(p));
// test attrs
// attrs
EXPECT_EQ(p.attr("x").cast<int>(), 3);
EXPECT_EQ(p.attr("y").cast<int>(), 4);
@ -78,13 +78,21 @@ TEST_F(PYBIND11_TEST, object) {
EXPECT_EQ(p.attr("y").cast<int>(), 6);
EXPECT_EXEC_EQ("p", "Point(5, 6)");
// test operators
// operators
EXPECT_EVAL_EQ("Point(10, 12)", p + p);
EXPECT_EVAL_EQ("Point(0, 0)", p - p);
EXPECT_EVAL_EQ("Point(25, 36)", p * p);
EXPECT_EVAL_EQ("Point(1, 1)", p / p);
// EXPECT_EVAL_EQ("Point(0, 0)", p // p);
EXPECT_EVAL_EQ("Point(0, 0)", p % p);
// iterators
py::object l = py::eval("[1, 2, 3]");
int index = 0;
for(auto item: l) {
EXPECT_EQ(item.cast<int>(), index + 1);
index++;
}
}
} // namespace

View File

@ -2,7 +2,7 @@
TEST_F(PYBIND11_TEST, int) {
py::object obj = py::int_(123);
py::handle obj2 = py::eval("123");
py::object obj2 = py::eval("123");
EXPECT_EQ(obj, obj2);
@ -13,7 +13,7 @@ TEST_F(PYBIND11_TEST, int) {
TEST_F(PYBIND11_TEST, float) {
py::object obj = py::float_(123.0);
py::handle obj2 = py::eval("123.0");
py::object obj2 = py::eval("123.0");
EXPECT_EQ(obj, obj2);
@ -23,17 +23,24 @@ TEST_F(PYBIND11_TEST, float) {
TEST_F(PYBIND11_TEST, str) {
py::object obj = py::str("123");
py::handle obj2 = py::eval("'123'");
py::object obj2 = py::eval("'123'");
EXPECT_EQ(obj, obj2);
EXPECT_STREQ(obj.cast<const char*>(), "123");
EXPECT_EQ(obj.cast<std::string>(), "123");
EXPECT_EQ(obj.cast<std::string_view>(), "123");
auto s = py::str("Hello, {}");
EXPECT_EQ(s.format("world").cast<std::string>(), "Hello, world");
}
TEST_F(PYBIND11_TEST, tuple) {
py::tuple tuple = py::tuple{py::int_(1), py::str("123"), py::int_(3)};
py::tuple tuple = py::tuple{
py::int_(1),
py::str("123"),
py::int_(3),
};
EXPECT_EQ(tuple, py::eval("(1, '123', 3)"));
EXPECT_EQ(tuple.size(), 3);
EXPECT_FALSE(tuple.empty());
@ -41,26 +48,56 @@ TEST_F(PYBIND11_TEST, tuple) {
tuple[0] = py::int_(3);
tuple[2] = py::int_(1);
EXPECT_EQ(tuple, py::eval("(3, '123', 1)"));
// iterators.
int index = 0;
for(auto item: tuple) {
if(index == 0) {
EXPECT_EQ(item, py::int_(3));
} else if(index == 1) {
EXPECT_EQ(item, py::str("123"));
} else if(index == 2) {
EXPECT_EQ(item, py::int_(1));
}
index++;
}
}
TEST_F(PYBIND11_TEST, list) {
// test constructors
// constructors
py::list list = py::list();
EXPECT_EQ(list, py::eval("[]"));
EXPECT_EQ(list.size(), 0);
EXPECT_TRUE(list.empty());
list = py::list{py::int_(1), py::int_(2), py::int_(3)};
list = py::list{
py::int_(1),
py::int_(2),
py::int_(3),
};
EXPECT_EQ(list, py::eval("[1, 2, 3]"));
EXPECT_EQ(list.size(), 3);
EXPECT_FALSE(list.empty());
// test accessor
// accessor
list[0] = py::int_(3);
list[2] = py::int_(1);
EXPECT_EQ(list, py::eval("[3, 2, 1]"));
// test other apis
// iterators
int index = 0;
for(auto item: list) {
if(index == 0) {
EXPECT_EQ(item, py::int_(3));
} else if(index == 1) {
EXPECT_EQ(item, py::int_(2));
} else if(index == 2) {
EXPECT_EQ(item, py::int_(1));
}
index++;
}
// others
list.append(py::int_(4));
EXPECT_EQ(list, py::eval("[3, 2, 1, 4]"));
@ -69,22 +106,39 @@ TEST_F(PYBIND11_TEST, list) {
}
TEST_F(PYBIND11_TEST, dict) {
// test constructors
// constructors
py::dict dict = py::dict();
EXPECT_EQ(dict, py::eval("{}"));
EXPECT_EQ(dict.size(), 0);
EXPECT_TRUE(dict.empty());
// test accessor
// accessor
dict["a"] = py::int_(1);
dict["b"] = py::int_(2);
dict["c"] = py::int_(3);
EXPECT_EQ(dict, py::eval("{'a': 1, 'b': 2, 'c': 3}"));
EXPECT_EQ(dict,
py::dict({
{"a", py::int_(1)},
{"b", py::int_(2)},
{"c", py::int_(3)},
}));
// FIXME:
// test other apis
// dict.clear();
// EXPECT_EQ(dict, py::eval("{}"));
// iterators
int index = 0;
for(auto item: dict) {
if(index == 0) {
EXPECT_EQ(item.first.cast<std::string>(), "a");
EXPECT_EQ(item.second, py::int_(1));
} else if(index == 1) {
EXPECT_EQ(item.first.cast<std::string>(), "b");
EXPECT_EQ(item.second, py::int_(2));
} else if(index == 2) {
EXPECT_EQ(item.first.cast<std::string>(), "c");
EXPECT_EQ(item.second, py::int_(3));
}
index++;
}
}
TEST_F(PYBIND11_TEST, capsule) {