mirror of
https://github.com/pocketpy/pocketpy
synced 2025-11-06 03:30:18 +00:00
some fix.
This commit is contained in:
parent
57e8cf651b
commit
c6e24c69a2
93
.github/workflows/pybind11.yml
vendored
Normal file
93
.github/workflows/pybind11.yml
vendored
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) \
|
||||
|
||||
@ -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;
|
||||
}
|
||||
if constexpr(named_argc != 0) {
|
||||
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);
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
26
include/pybind11/tests/CMakeLists.txt
Normal file
26
include/pybind11/tests/CMakeLists.txt
Normal 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)
|
||||
@ -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) {
|
||||
|
||||
50
include/pybind11/tests/error.cpp
Normal file
50
include/pybind11/tests/error.cpp
Normal 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);
|
||||
}
|
||||
@ -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__();
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user