Merge branch 'master' into smart_holder

This commit is contained in:
Ralf W. Grosse-Kunstleve 2021-07-13 21:24:02 -07:00
commit 279c93654e
15 changed files with 130 additions and 15 deletions

View File

@ -70,6 +70,19 @@ that is supported via a ``build_ext`` command override; it will only affect
ext_modules=ext_modules ext_modules=ext_modules
) )
If you have single-file extension modules that are directly stored in the
Python source tree (``foo.cpp`` in the same directory as where a ``foo.py``
would be located), you can also generate ``Pybind11Extensions`` using
``setup_helpers.intree_extensions``: ``intree_extensions(["path/to/foo.cpp",
...])`` returns a list of ``Pybind11Extensions`` which can be passed to
``ext_modules``, possibly after further customizing their attributes
(``libraries``, ``include_dirs``, etc.). By doing so, a ``foo.*.so`` extension
module will be generated and made available upon installation.
``intree_extension`` will automatically detect if you are using a ``src``-style
layout (as long as no namespace packages are involved), but you can also
explicitly pass ``package_dir`` to it (as in ``setuptools.setup``).
Since pybind11 does not require NumPy when building, a light-weight replacement Since pybind11 does not require NumPy when building, a light-weight replacement
for NumPy's parallel compilation distutils tool is included. Use it like this: for NumPy's parallel compilation distutils tool is included. Use it like this:

View File

@ -62,7 +62,8 @@ struct metaclass {
handle value; handle value;
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
metaclass() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute // NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
metaclass() {}
/// Override pybind11's default metaclass /// Override pybind11's default metaclass
explicit metaclass(handle value) : value(value) { } explicit metaclass(handle value) : value(value) { }

View File

@ -801,7 +801,8 @@ struct nodelete { template <typename T> void operator()(T*) { } };
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename... Args> template <typename... Args>
struct overload_cast_impl { struct overload_cast_impl {
constexpr overload_cast_impl() {}; // NOLINT(modernize-use-equals-default): MSVC 2015 needs this // NOLINTNEXTLINE(modernize-use-equals-default): MSVC 2015 needs this
constexpr overload_cast_impl() {}
template <typename Return> template <typename Return>
constexpr auto operator()(Return (*pf)(Args...)) const noexcept constexpr auto operator()(Return (*pf)(Args...)) const noexcept

View File

@ -1015,7 +1015,7 @@ public:
if (PyUnicode_Check(m_ptr)) { if (PyUnicode_Check(m_ptr)) {
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr)); temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr));
if (!temp) if (!temp)
pybind11_fail("Unable to extract string contents! (encoding issue)"); throw error_already_set();
} }
char *buffer = nullptr; char *buffer = nullptr;
ssize_t length = 0; ssize_t length = 0;

View File

@ -303,6 +303,42 @@ class build_ext(_build_ext): # noqa: N801
_build_ext.build_extensions(self) _build_ext.build_extensions(self)
def intree_extensions(paths, package_dir=None):
"""
Generate Pybind11Extensions from source files directly located in a Python
source tree.
``package_dir`` behaves as in ``setuptools.setup``. If unset, the Python
package root parent is determined as the first parent directory that does
not contain an ``__init__.py`` file.
"""
exts = []
for path in paths:
if package_dir is None:
parent, _ = os.path.split(path)
while os.path.exists(os.path.join(parent, "__init__.py")):
parent, _ = os.path.split(parent)
relname, _ = os.path.splitext(os.path.relpath(path, parent))
qualified_name = relname.replace(os.path.sep, ".")
exts.append(Pybind11Extension(qualified_name, [path]))
else:
found = False
for prefix, parent in package_dir.items():
if path.startswith(parent):
found = True
relname, _ = os.path.splitext(os.path.relpath(path, parent))
qualified_name = relname.replace(os.path.sep, ".")
if prefix:
qualified_name = prefix + "." + qualified_name
exts.append(Pybind11Extension(qualified_name, [path]))
if not found:
raise ValueError(
"path {} is not a child of any of the directories listed "
"in 'package_dir' ({})".format(path, package_dir)
)
return exts
def naive_recompile(obj, src): def naive_recompile(obj, src):
""" """
This will recompile only if the source file changes. It does not check This will recompile only if the source file changes. It does not check

View File

@ -1,7 +1,7 @@
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / # IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
# pre-commit). # pre-commit).
from typing import Any, Callable, Iterator, Optional, Type, TypeVar, Union from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
from types import TracebackType from types import TracebackType
from distutils.command.build_ext import build_ext as _build_ext # type: ignore from distutils.command.build_ext import build_ext as _build_ext # type: ignore
@ -33,6 +33,9 @@ def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]:
class build_ext(_build_ext): # type: ignore class build_ext(_build_ext): # type: ignore
def build_extensions(self) -> None: ... def build_extensions(self) -> None: ...
def intree_extensions(
paths: Iterator[str], package_dir: Optional[Dict[str, str]] = None
) -> List[Pybind11Extension]: ...
def no_recompile(obj: str, src: str) -> bool: ... def no_recompile(obj: str, src: str) -> bool: ...
def naive_recompile(obj: str, src: str) -> bool: ... def naive_recompile(obj: str, src: str) -> bool: ...

View File

@ -99,3 +99,45 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
subprocess.check_call( subprocess.check_call(
[sys.executable, "test.py"], stdout=sys.stdout, stderr=sys.stderr [sys.executable, "test.py"], stdout=sys.stdout, stderr=sys.stderr
) )
def test_intree_extensions(monkeypatch, tmpdir):
monkeypatch.syspath_prepend(MAIN_DIR)
from pybind11.setup_helpers import intree_extensions
monkeypatch.chdir(tmpdir)
root = tmpdir
root.ensure_dir()
subdir = root / "dir"
subdir.ensure_dir()
src = subdir / "ext.cpp"
src.ensure()
(ext,) = intree_extensions([src.relto(tmpdir)])
assert ext.name == "ext"
subdir.ensure("__init__.py")
(ext,) = intree_extensions([src.relto(tmpdir)])
assert ext.name == "dir.ext"
def test_intree_extensions_package_dir(monkeypatch, tmpdir):
monkeypatch.syspath_prepend(MAIN_DIR)
from pybind11.setup_helpers import intree_extensions
monkeypatch.chdir(tmpdir)
root = tmpdir / "src"
root.ensure_dir()
subdir = root / "dir"
subdir.ensure_dir()
src = subdir / "ext.cpp"
src.ensure()
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"})
assert ext.name == "dir.ext"
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"foo": "src"})
assert ext.name == "foo.dir.ext"
subdir.ensure("__init__.py")
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"": "src"})
assert ext.name == "dir.ext"
(ext,) = intree_extensions([src.relto(tmpdir)], package_dir={"foo": "src"})
assert ext.name == "foo.dir.ext"

View File

@ -1,10 +1,15 @@
#pragma once #pragma once
// This must be kept first for MSVC 2015.
// Do not remove the empty line between the #includes.
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/eval.h> #include <pybind11/eval.h>
#if defined(_MSC_VER) && _MSC_VER < 1910 #if defined(_MSC_VER) && _MSC_VER < 1910
// We get some really long type names here which causes MSVC 2015 to emit warnings // We get some really long type names here which causes MSVC 2015 to emit warnings
# pragma warning(disable: 4503) // warning C4503: decorated name length exceeded, name was truncated # pragma warning( \
disable : 4503) // warning C4503: decorated name length exceeded, name was truncated
#endif #endif
namespace py = pybind11; namespace py = pybind11;

View File

@ -154,7 +154,7 @@ TEST_SUBMODULE(buffers, m) {
py::format_descriptor<int32_t>::format(), 1); py::format_descriptor<int32_t>::format(), 1);
} }
ConstBuffer() : value(new int32_t{0}) { }; ConstBuffer() : value(new int32_t{0}) {}
}; };
py::class_<ConstBuffer>(m, "ConstBuffer", py::buffer_protocol()) py::class_<ConstBuffer>(m, "ConstBuffer", py::buffer_protocol())
.def(py::init<>()) .def(py::init<>())

View File

@ -122,7 +122,7 @@ TEST_SUBMODULE(callbacks, m) {
// [workaround(intel)] = default does not work here // [workaround(intel)] = default does not work here
// Defaulting this destructor results in linking errors with the Intel compiler // Defaulting this destructor results in linking errors with the Intel compiler
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827) // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
virtual ~AbstractBase() {}; // NOLINT(modernize-use-equals-default) virtual ~AbstractBase() {} // NOLINT(modernize-use-equals-default)
virtual unsigned int func() = 0; virtual unsigned int func() = 0;
}; };
m.def("func_accepting_func_accepting_base", m.def("func_accepting_func_accepting_base",

View File

@ -258,7 +258,10 @@ TEST_SUBMODULE(eigen, m) {
m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; }); m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; });
m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; }); m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; });
// test_sparse, test_sparse_signature // test_sparse, test_sparse_signature
m.def("sparse_r", [mat]() -> SparseMatrixR { return Eigen::SparseView<Eigen::MatrixXf>(mat); }); //NOLINT(clang-analyzer-core.uninitialized.UndefReturn) m.def("sparse_r", [mat]() -> SparseMatrixR {
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return Eigen::SparseView<Eigen::MatrixXf>(mat);
});
m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); }); m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); });
m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; }); m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; });
m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; }); m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; });

View File

@ -123,7 +123,7 @@ class NoneCastTester {
public: public:
int answer = -1; int answer = -1;
NoneCastTester() = default; NoneCastTester() = default;
NoneCastTester(int v) : answer(v) {}; NoneCastTester(int v) : answer(v) {}
}; };
struct StrIssue { struct StrIssue {
@ -394,14 +394,14 @@ TEST_SUBMODULE(methods_and_attributes, m) {
.def("increase_value", &RegisteredDerived::increase_value) .def("increase_value", &RegisteredDerived::increase_value)
.def_readwrite("rw_value", &RegisteredDerived::rw_value) .def_readwrite("rw_value", &RegisteredDerived::rw_value)
.def_readonly("ro_value", &RegisteredDerived::ro_value) .def_readonly("ro_value", &RegisteredDerived::ro_value)
// These should trigger a static_assert if uncommented // Uncommenting the next line should trigger a static_assert:
//.def_readwrite("fails", &UserType::value) // should trigger a static_assert if uncommented // .def_readwrite("fails", &UserType::value)
//.def_readonly("fails", &UserType::value) // should trigger a static_assert if uncommented // Uncommenting the next line should trigger a static_assert:
// .def_readonly("fails", &UserType::value)
.def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int) .def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int)
.def_property_readonly("ro_value_prop", &RegisteredDerived::get_double) .def_property_readonly("ro_value_prop", &RegisteredDerived::get_double)
// This one is in the registered class: // This one is in the registered class:
.def("sum", &RegisteredDerived::sum) .def("sum", &RegisteredDerived::sum);
;
using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing)); using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, ""); static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");

View File

@ -76,6 +76,9 @@ TEST_SUBMODULE(pytypes, m) {
m.def("str_from_object", [](const py::object& obj) { return py::str(obj); }); m.def("str_from_object", [](const py::object& obj) { return py::str(obj); });
m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); }); m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); });
m.def("str_from_handle", [](py::handle h) { return py::str(h); }); m.def("str_from_handle", [](py::handle h) { return py::str(h); });
m.def("str_from_string_from_str", [](const py::str& obj) {
return py::str(static_cast<std::string>(obj));
});
m.def("str_format", []() { m.def("str_format", []() {
auto s1 = "{} + {} = {}"_s.format(1, 2, 3); auto s1 = "{} + {} = {}"_s.format(1, 2, 3);

View File

@ -133,6 +133,14 @@ def test_str(doc):
else: else:
assert m.str_from_handle(malformed_utf8) == "b'\\x80'" assert m.str_from_handle(malformed_utf8) == "b'\\x80'"
assert m.str_from_string_from_str("this is a str") == "this is a str"
ucs_surrogates_str = u"\udcc3"
if env.PY2:
assert u"\udcc3" == m.str_from_string_from_str(ucs_surrogates_str)
else:
with pytest.raises(UnicodeEncodeError):
m.str_from_string_from_str(ucs_surrogates_str)
def test_bytes(doc): def test_bytes(doc):
assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_string().decode() == "foo"

View File

@ -24,7 +24,7 @@ template <typename T> class huge_unique_ptr {
std::unique_ptr<T> ptr; std::unique_ptr<T> ptr;
uint64_t padding[10]; uint64_t padding[10];
public: public:
huge_unique_ptr(T *p) : ptr(p) {}; huge_unique_ptr(T *p) : ptr(p) {}
T *get() { return ptr.get(); } T *get() { return ptr.get(); }
}; };