Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ IncludeCategories:
- Regex: '^<'
Priority: 40
# Your project's h files.
- Regex: '^"drake'
- Regex: '^"drake/'
Priority: 50
# Other libraries' h files (with quotes).
- Regex: '^"'
Expand Down
2 changes: 1 addition & 1 deletion bindings/pydrake/.clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ IncludeCategories:
- Regex: '^<'
Priority: 40
# Your project's h files.
- Regex: '^"drake'
- Regex: '^"drake/'
Priority: 50
# Other libraries' h files (with quotes).
- Regex: '^"'
Expand Down
5 changes: 4 additions & 1 deletion bindings/pydrake/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ drake_py_library(

drake_cc_library(
name = "pydrake_pybind",
hdrs = ["pydrake_pybind.h"],
hdrs = [
"numpy_object_pybind.h",
"pydrake_pybind.h",
],
declare_installed_headers = 0,
defines = select({
"//tools/workspace/nanobind:enabled": [
Expand Down
16 changes: 16 additions & 0 deletions bindings/pydrake/common/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ drake_pybind_library(

drake_py_unittest(
name = "eigen_pybind_test",
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":eigen_pybind_test_util_py",
],
Expand Down Expand Up @@ -474,6 +476,8 @@ drake_py_unittest(
name = "deprecation_test",
# kcov messes with module ref counts.
opt_out_conditions = ["//tools/kcov:enabled"],
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":common",
":deprecation_example",
Expand All @@ -484,6 +488,8 @@ drake_py_unittest(
# See source for an explanation why this is separate from `deprecation_test`.
drake_py_unittest(
name = "deprecation_autocomplete_test",
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":common",
":deprecation_example",
Expand All @@ -493,6 +499,8 @@ drake_py_unittest(
# Provides a unittest for high-level deprecation API.
drake_py_unittest(
name = "deprecation_utility_test",
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":deprecation_example",
"//bindings/pydrake/common/test_utilities:deprecation_py",
Expand Down Expand Up @@ -534,6 +542,8 @@ drake_pybind_library(

drake_py_unittest(
name = "ref_cycle_test",
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":common",
":ref_cycle_test_util_py",
Expand Down Expand Up @@ -650,6 +660,8 @@ drake_pybind_library(

drake_py_unittest(
name = "sorted_pair_test",
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":common",
":sorted_pair_test_util_py",
Expand Down Expand Up @@ -758,6 +770,8 @@ drake_pybind_library(

drake_py_unittest(
name = "wrap_pybind_test",
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":wrap_test_util_py",
],
Expand All @@ -775,6 +789,8 @@ drake_py_unittest(
data = [
"//common/yaml:test/yaml_io_test_input_1.yaml",
],
# TODO(#21572) Remove this opt-out.
test_alt_binder = False,
deps = [
":common",
":serialize_test_util_py",
Expand Down
8 changes: 8 additions & 0 deletions bindings/pydrake/common/cpp_param_pybind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,23 @@ py::object GetPyParamScalarImpl(const std::type_info& tinfo) {
// This type is not aliased. Get the pybind-registered type,
// erroring out if it's not registered.
// WARNING: Internal API :(
#ifdef PYDRAKE_USE_PYBIND11
auto* info = py::detail::get_type_info(tinfo);
#else // PYDRAKE_USE_NANOBIND
auto* info = py::detail::nb_type_lookup(&tinfo);
#endif
if (!info) {
// TODO(eric.cousineau): Use NiceTypeName::Canonicalize(...Demangle(...))
// once simpler dependencies are used (or something else is used to
// justify linking in `libdrake.so`).
const std::string name = tinfo.name();
throw std::runtime_error("C++ type is not registered in pybind: " + name);
}
#ifdef PYDRAKE_USE_PYBIND11
py::handle h(reinterpret_cast<PyObject*>(info->type));
#else // PYDRAKE_USE_NANOBIND
py::handle h(reinterpret_cast<PyObject*>(info));
#endif
return py::borrow<py::object>(h);
}
}
Expand Down
10 changes: 5 additions & 5 deletions bindings/pydrake/common/cpp_param_pybind.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ py::object GetPyParamScalarImpl(const std::type_info& tinfo);
// Gets Python type for a C++ type (base case).
template <typename T>
inline py::object GetPyParamScalarImpl(type_pack<T> = {}) {
static_assert(!py::detail::is_pyobject<T>::value,
static_assert(!internal::is_pyobject<T>::value,
"You cannot use `pybind11` types (e.g. `py::object`). Use a publicly "
"visible replacement type instead (e.g. `drake::pydrake::Object`).");
return GetPyParamScalarImpl(typeid(T));
Expand All @@ -114,12 +114,12 @@ inline py::object GetPyParamScalarImpl(
}

// Gets Python type for a C++ vector that is not registered using
// PYBIND11_MAKE_OPAQUE.
// {PYBIND11,NB}_MAKE_OPAQUE.
template <typename T>
inline py::object GetPyParamScalarImpl(type_pack<std::vector<T>> = {}) {
// Get inner type for validation.
py::object py_T = GetPyParamScalarImpl(type_pack<T>{});
if constexpr (!internal::is_generic_pybind_v<std::vector<T>>) {
if constexpr (!internal::is_generic_caster_v<std::vector<T>>) {
return py::module_::import_("pydrake.common.cpp_param").attr("List")[py_T];
} else {
return GetPyParamScalarImpl(typeid(std::vector<T>));
Expand All @@ -146,7 +146,7 @@ inline py::tuple GetPyParam(type_pack<Ts...> = {}) {
} // namespace pydrake
} // namespace drake

namespace pybind11 {
namespace PYDRAKE_BINDER_NAMESPACE {
namespace detail {

template <>
Expand All @@ -155,4 +155,4 @@ struct type_caster<drake::pydrake::Object>
drake::pydrake::internal::wrapper_pydrake_object> {};

} // namespace detail
} // namespace pybind11
} // namespace PYDRAKE_BINDER_NAMESPACE
16 changes: 8 additions & 8 deletions bindings/pydrake/common/cpp_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,14 +439,14 @@ def _rename_callable(f, scope, name, cls=None):
qualname = cls.__qualname__ + "." + name
else:
qualname = name
# If Python2, we have to wrap instancemethods + built-in functions to spoof
# the metadata.
type_requires_wrap = (
types.MethodType,
types.BuiltinMethodType,
types.BuiltinFunctionType,
)
if isinstance(f, type_requires_wrap):
# Native functions (built-in or nanobind-bound) don't allowing resetting the
# metadata unless we wrap them in Python first.
if (
isinstance(f, types.BuiltinMethodType)
or isinstance(f, types.BuiltinFunctionType)
or f.__class__.__name__ == "nb_method"
or f.__class__.__name__ == "nb_func"
):
orig = f

def f(*args, **kwargs):
Expand Down
7 changes: 6 additions & 1 deletion bindings/pydrake/common/cpp_template_pybind.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,12 @@ py::object AddTemplateMethod( // BR
const std::string instantiation_name =
internal::GetInstantiationName(py_template, param, mangle);
py::object py_func = py::cpp_function(std::forward<Method>(method),
py::name(instantiation_name.c_str()), py::is_method(scope),
py::name(instantiation_name.c_str()),
#ifdef PYDRAKE_USE_PYBIND11
py::is_method(scope),
#else // PYDRAKE_USE_NANOBIND
py::is_method(),
#endif
std::forward<Extra>(extra)...);
internal::AddInstantiation(py_template, py_func, param);
return py_template;
Expand Down
19 changes: 10 additions & 9 deletions bindings/pydrake/common/eigen_pybind.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,14 @@ inline py::object WrapToMatchInputShape(py::handle func) {
} // namespace pydrake
} // namespace drake

namespace pybind11 {
namespace PYDRAKE_BINDER_NAMESPACE {
namespace detail {

/**
Provides pybind11 `type_caster`s for drake::EigenPtr.

Provides `type_caster`s for drake::EigenPtr.
Uses `type_caster<Eigen::Ref>` internally to avoid code duplication.
See http://pybind11.readthedocs.io/en/stable/advanced/cast/custom.html for
more details on custom type casters.

TODO(eric.cousineau): Place all logic inside of `drake` namespace once our
pybind11 fork includes PYBIND11_TYPE_CASTER macro w/ fully qualified symbols.
*/
#ifdef PYDRAKE_USE_PYBIND11
template <typename T>
struct type_caster<drake::EigenPtr<T>> {
using PtrType = drake::EigenPtr<T>;
Expand Down Expand Up @@ -126,6 +121,12 @@ struct type_caster<drake::EigenPtr<T>> {
private:
InnerCaster inner_caster;
};
#else // PYDRAKE_USE_NANOBIND
template <typename T>
struct type_caster<drake::EigenPtr<T>> {
// TODO(#21572) Implement me.
};
#endif // PYDRAKE_USE_PYBIND11

} // namespace detail
} // namespace pybind11
} // namespace PYDRAKE_BINDER_NAMESPACE
59 changes: 40 additions & 19 deletions bindings/pydrake/common/module_py.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,23 @@ void trigger_an_assertion_failure() {
// Resolves to a Python handle given a type erased pointer. If the instance or
// lowest-level RTTI type are unregistered, returns an empty handle.
py::handle ResolvePyObject(const type_erased_ptr& ptr) {
#ifdef PYDRAKE_USE_PYBIND11
auto py_type_info = py::detail::get_type_info(ptr.info);
return py::detail::get_object_handle(ptr.raw, py_type_info);
#else
// TODO(#21572) Use is_polymorphic for nanobind (or remove it from the struct,
// if we change our mind and end up not needing it).
unused(ptr.is_polymorphic);
auto py_type_info = py::detail::get_type_info(ptr.info);
return py::detail::get_object_handle(ptr.raw, py_type_info);
bool is_new{false};
PyObject* result{};
result = py::detail::nb_type_put(&ptr.info, const_cast<void*>(ptr.raw),
py_rvp::reference, nullptr, &is_new);
if (is_new) {
py::object delete_me = py::steal(result);
return py::handle();
}
return py::steal(result);
#endif
}

// Override for SetNiceTypeNamePtrOverride, to ensure that instances that are
Expand All @@ -71,7 +83,9 @@ namespace testing {
// Registered type. Also a base class for UnregisteredDerivedType.
class RegisteredType {
public:
virtual ~RegisteredType() {}
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(RegisteredType);
RegisteredType() = default;
virtual ~RegisteredType() = default;
};
// Completely unregistered type.
class UnregisteredType {};
Expand Down Expand Up @@ -244,7 +258,7 @@ void InitLowLevelModules(py::module_ m) {
const std::string_view name_str(name.c_str());
if (name_str == "contents" || name_str == "extension" ||
name_str == "filename_hint") {
name = py::str(fmt::format("_{}", name_str));
name = py::str(fmt::format("_{}", name_str).c_str());
}
py::eval("object.__setattr__", py::globals())(self, name, value);
});
Expand All @@ -256,8 +270,8 @@ void InitLowLevelModules(py::module_ m) {
return py::bytes(contents.c_str(), contents.size());
},
[](Class& self, const py::bytes& contents) {
self = MemoryFile{
std::string{contents}, self.extension(), self.filename_hint()};
self = MemoryFile{std::string{contents.c_str(), contents.size()},
self.extension(), self.filename_hint()};
});
cls.def_prop_rw(
"_extension", [](const Class& self) { return self.extension(); },
Expand Down Expand Up @@ -325,15 +339,21 @@ discussion), use e.g.
// Admittedly, it's unusual for a python library like pydrake to raise
// SystemExit, but for now its better than C++ ::abort() taking down the
// whole interpreter with a worse diagnostic message.
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) {
std::rethrow_exception(p);
}
} catch (const drake::internal::assertion_error& e) {
PyErr_SetString(PyExc_SystemExit, e.what());
}
});
py::register_exception_translator(
#ifdef PYDRAKE_USE_PYBIND11
[](std::exception_ptr p)
#else // PYDRAKE_USE_NANOBIND
[](const std::exception_ptr& p, void*)
#endif
{
try {
if (p) {
std::rethrow_exception(p);
}
} catch (const drake::internal::assertion_error& e) {
PyErr_SetString(PyExc_SystemExit, e.what());
}
});
// Convenient wrapper for querying FindResource(resource_path).
m.def("FindResourceOrThrow", &FindResourceOrThrow,
"Attempts to locate a Drake resource named by the given path string. "
Expand All @@ -357,7 +377,7 @@ discussion), use e.g.
[]() {
py::object result;
if (auto optional_result = MaybeGetDrakePath()) {
result = py::str(*optional_result);
result = py::str(optional_result->c_str());
}
return result;
},
Expand All @@ -377,9 +397,10 @@ discussion), use e.g.
// =========================================================================

// Define `_testing` submodule.
py::module_ pydrake_top = py::eval("sys.modules['pydrake']", py::globals());
py::module_ pydrake_common =
py::eval("sys.modules['pydrake.common']", py::globals());
py::module_ pydrake_top =
py::cast<py::module_>(py::eval("sys.modules['pydrake']", py::globals()));
py::module_ pydrake_common = py::cast<py::module_>(
py::eval("sys.modules['pydrake.common']", py::globals()));

py::module_ testing = pydrake_common.def_submodule("_testing");
testing::def_testing(testing);
Expand Down
Loading