Merge branch 'master' into sh_merge_master

This commit is contained in:
Ralf W. Grosse-Kunstleve 2023-09-10 10:49:38 -07:00
commit 35b26794ec
21 changed files with 259 additions and 52 deletions

View File

@ -70,7 +70,7 @@ jobs:
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
@ -206,7 +206,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
uses: deadsnakes/action@v3.0.1
@ -311,7 +311,7 @@ jobs:
container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add wget and python3
run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev
@ -345,7 +345,7 @@ jobs:
container: nvidia/cuda:12.2.0-devel-ubuntu22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
# tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
- name: Install 🐍 3
@ -369,7 +369,7 @@ jobs:
# container: centos:8
#
# steps:
# - uses: actions/checkout@v3
# - uses: actions/checkout@v4
#
# - name: Add Python 3 and a few requirements
# run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules
@ -414,7 +414,7 @@ jobs:
# tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
DEBIAN_FRONTEND: 'noninteractive'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add NVHPC Repo
run: |
@ -476,7 +476,7 @@ jobs:
container: "gcc:${{ matrix.gcc }}"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add Python 3
run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev
@ -536,7 +536,7 @@ jobs:
name: "🐍 3 • ICC latest • x64"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add apt repo
run: |
@ -640,7 +640,13 @@ jobs:
container: "${{ matrix.container }}"
steps:
- uses: actions/checkout@v3
- name: Latest actions/checkout
uses: actions/checkout@v4
if: matrix.container != 'centos:7'
- name: Pin actions/checkout as required for centos:7
uses: actions/checkout@v3
if: matrix.container == 'centos:7'
- name: Add Python 3 (RHEL 7)
if: matrix.container == 'centos:7'
@ -688,7 +694,7 @@ jobs:
container: i386/debian:buster
steps:
- uses: actions/checkout@v1 # Required to run inside docker
- uses: actions/checkout@v1 # v1 is required to run inside docker
- name: Install requirements
run: |
@ -731,7 +737,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
@ -783,7 +789,7 @@ jobs:
runs-on: windows-2019
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
@ -836,7 +842,7 @@ jobs:
runs-on: windows-2019
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
@ -884,7 +890,7 @@ jobs:
runs-on: windows-2022
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
@ -962,7 +968,7 @@ jobs:
mingw-w64-${{matrix.env}}-boost
mingw-w64-${{matrix.env}}-catch
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Configure C++11
# LTO leads to many undefined reference like
@ -1033,7 +1039,7 @@ jobs:
run: env
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Clang
uses: egor-tensin/setup-clang@v1
@ -1102,7 +1108,7 @@ jobs:
run: env
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Show Clang++ version before brew install llvm
run: clang++ --version

View File

@ -50,7 +50,7 @@ jobs:
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python 3.7
uses: actions/setup-python@v4

View File

@ -26,7 +26,7 @@ jobs:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.x"
@ -44,7 +44,7 @@ jobs:
runs-on: ubuntu-latest
container: silkeh/clang:15-bullseye
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install requirements
run: apt-get update && apt-get install -y git python3-dev python3-pytest

View File

@ -29,7 +29,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup 🐍 3.6
uses: actions/setup-python@v4
@ -51,7 +51,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup 🐍 3.8
uses: actions/setup-python@v4

View File

@ -25,7 +25,7 @@ jobs:
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v4

View File

@ -31,21 +31,21 @@ repos:
types_or: [c++, c, cuda]
# Black, the code formatter, natively supports pre-commit
- repo: https://github.com/psf/black
- repo: https://github.com/psf/black-pre-commit-mirror
rev: "23.7.0" # Keep in sync with blacken-docs
hooks:
- id: black
# Ruff, the Python auto-correcting linter written in Rust
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.281
rev: v0.0.287
hooks:
- id: ruff
args: ["--fix", "--show-fixes"]
# Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.4.1"
rev: "v1.5.1"
hooks:
- id: mypy
args: []
@ -85,7 +85,7 @@ repos:
# Also code format the docs
- repo: https://github.com/asottile/blacken-docs
rev: "1.15.0"
rev: "1.16.0"
hooks:
- id: blacken-docs
additional_dependencies:
@ -93,7 +93,7 @@ repos:
# Changes tabs to spaces
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: "v1.5.1"
rev: "v1.5.4"
hooks:
- id: remove-tabs
exclude: (^docs/.*|\.patch)?$
@ -149,7 +149,7 @@ repos:
# PyLint has native support - not always usable, but works for us
- repo: https://github.com/PyCQA/pylint
rev: "v3.0.0a6"
rev: "v3.0.0a7"
hooks:
- id: pylint
files: ^pybind11

View File

@ -1,3 +1,20 @@
# https://blog.readthedocs.com/migrate-configuration-v2/
version: 2
build:
os: ubuntu-22.04
apt_packages:
- librsvg2-bin
tools:
python: "3.11"
sphinx:
configuration: docs/conf.py
python:
version: 3
requirements_file: docs/requirements.txt
install:
- requirements: docs/requirements.txt
formats:
- pdf

View File

@ -16,7 +16,7 @@ lifetime of objects managed by them. This can lead to issues when creating
bindings for functions that return a non-trivial type. Just by looking at the
type information, it is not clear whether Python should take charge of the
returned value and eventually free its resources, or if this is handled on the
C++ side. For this reason, pybind11 provides a several *return value policy*
C++ side. For this reason, pybind11 provides several *return value policy*
annotations that can be passed to the :func:`module_::def` and
:func:`class_::def` functions. The default policy is
:enum:`return_value_policy::automatic`.

View File

@ -70,7 +70,7 @@ def generate_dummy_code_boost(nclasses=10):
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]:
print("{")
for i in range(0, 10):
for i in range(10):
nclasses = 2**i
with open("test.cpp", "w") as f:
f.write(codegen(nclasses))

View File

@ -936,6 +936,10 @@ struct handle_type_name<float_> {
static constexpr auto name = const_name("float");
};
template <>
struct handle_type_name<function> {
static constexpr auto name = const_name("Callable");
};
template <>
struct handle_type_name<none> {
static constexpr auto name = const_name("None");
};

View File

@ -35,8 +35,9 @@
/// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version.
#ifndef PYBIND11_INTERNALS_VERSION
# if PY_VERSION_HEX >= 0x030C0000
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
// Version bump for Python 3.12+, before first 3.12 beta release.
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
# define PYBIND11_INTERNALS_VERSION 5
# else
# define PYBIND11_INTERNALS_VERSION 4
@ -292,9 +293,12 @@ struct type_info {
#endif
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898).
#ifndef PYBIND11_BUILD_ABI
# if defined(__GXX_ABI_VERSION)
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
# elif defined(_MSC_VER)
# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER)
# else
# define PYBIND11_BUILD_ABI ""
# endif

View File

@ -53,6 +53,45 @@ PYBIND11_WARNING_DISABLE_MSVC(4127)
PYBIND11_NAMESPACE_BEGIN(detail)
inline std::string replace_newlines_and_squash(const char *text) {
const char *whitespaces = " \t\n\r\f\v";
std::string result(text);
bool previous_is_whitespace = false;
// Do not modify string representations
char first_char = result[0];
char last_char = result[result.size() - 1];
if (first_char == last_char && first_char == '\'') {
return result;
}
result.clear();
// Replace characters in whitespaces array with spaces and squash consecutive spaces
while (*text != '\0') {
if (std::strchr(whitespaces, *text)) {
if (!previous_is_whitespace) {
result += ' ';
previous_is_whitespace = true;
}
} else {
result += *text;
previous_is_whitespace = false;
}
++text;
}
// Strip leading and trailing whitespaces
const size_t str_begin = result.find_first_not_of(whitespaces);
if (str_begin == std::string::npos) {
return "";
}
const size_t str_end = result.find_last_not_of(whitespaces);
const size_t str_range = str_end - str_begin + 1;
return result.substr(str_begin, str_range);
}
// Apply all the extensions translators from a list
// Return true if one of the translators completed without raising an exception
// itself. Return of false indicates that if there are other translators
@ -425,7 +464,7 @@ protected:
// Write default value if available.
if (!is_starred && arg_index < rec->args.size() && rec->args[arg_index].descr) {
signature += " = ";
signature += rec->args[arg_index].descr;
signature += detail::replace_newlines_and_squash(rec->args[arg_index].descr);
}
// Separator for positional-only arguments (placed after the
// argument, rather than before like *
@ -681,7 +720,7 @@ protected:
/* Iterator over the list of potentially admissible overloads */
const function_record *overloads = reinterpret_cast<function_record *>(
PyCapsule_GetPointer(self, get_function_record_capsule_name())),
*it = overloads;
*current_overload = overloads;
assert(overloads != nullptr);
/* Need to know how many arguments + keyword arguments there are to pick the right
@ -719,9 +758,10 @@ protected:
std::vector<function_call> second_pass;
// However, if there are no overloads, we can just skip the no-convert pass entirely
const bool overloaded = it != nullptr && it->next != nullptr;
const bool overloaded
= current_overload != nullptr && current_overload->next != nullptr;
for (; it != nullptr; it = it->next) {
for (; current_overload != nullptr; current_overload = current_overload->next) {
/* For each overload:
1. Copy all positional arguments we were given, also checking to make sure that
@ -742,7 +782,7 @@ protected:
a result other than PYBIND11_TRY_NEXT_OVERLOAD.
*/
const function_record &func = *it;
const function_record &func = *current_overload;
size_t num_args = func.nargs; // Number of positional arguments that we need
if (func.has_args) {
--num_args; // (but don't count py::args
@ -980,10 +1020,10 @@ protected:
}
if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) {
// The error reporting logic below expects 'it' to be valid, as it would be
// if we'd encountered this failure in the first-pass loop.
// The error reporting logic below expects 'current_overload' to be valid,
// as it would be if we'd encountered this failure in the first-pass loop.
if (!result) {
it = &call.func;
current_overload = &call.func;
}
break;
}
@ -1130,7 +1170,8 @@ protected:
if (!result) {
std::string msg = "Unable to convert function return value to a "
"Python type! The signature was\n\t";
msg += it->signature;
assert(current_overload != nullptr);
msg += current_overload->signature;
append_note_if_missing_header_is_suspected(msg);
// Attach additional error info to the exception if supported
if (PyErr_Occurred()) {
@ -2235,7 +2276,7 @@ struct enum_base {
object type_name = type::handle_of(arg).attr("__name__");
return pybind11::str("{}.{}").format(std::move(type_name), enum_name(arg));
},
name("name"),
name("__str__"),
is_method(m_base));
if (options::show_enum_members_docstring()) {
@ -2653,7 +2694,7 @@ iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
Policy);
}
return cast(state{first, last, true});
return cast(state{std::forward<Iterator>(first), std::forward<Sentinel>(last), true});
}
PYBIND11_NAMESPACE_END(detail)
@ -2670,7 +2711,9 @@ iterator make_iterator(Iterator first, Sentinel last, Extra &&...extra) {
Iterator,
Sentinel,
ValueType,
Extra...>(first, last, std::forward<Extra>(extra)...);
Extra...>(std::forward<Iterator>(first),
std::forward<Sentinel>(last),
std::forward<Extra>(extra)...);
}
/// Makes a python iterator over the keys (`.first`) of a iterator over pairs from a
@ -2686,7 +2729,9 @@ iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
Iterator,
Sentinel,
KeyType,
Extra...>(first, last, std::forward<Extra>(extra)...);
Extra...>(std::forward<Iterator>(first),
std::forward<Sentinel>(last),
std::forward<Extra>(extra)...);
}
/// Makes a python iterator over the values (`.second`) of a iterator over pairs from a
@ -2702,7 +2747,9 @@ iterator make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
Iterator,
Sentinel,
ValueType,
Extra...>(first, last, std::forward<Extra>(extra)...);
Extra...>(std::forward<Iterator>(first),
std::forward<Sentinel>(last),
std::forward<Extra>(extra)...);
}
/// Makes an iterator over values of an stl container or other container supporting

View File

@ -94,5 +94,5 @@ line-length = 120
isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"]
[tool.ruff.per-file-ignores]
"tests/**" = ["EM", "N"]
"tests/**" = ["EM", "N", "E721"]
"tests/test_call_policies.py" = ["PLC1901"]

View File

@ -216,3 +216,10 @@ def test_custom_func():
def test_custom_func2():
assert m.custom_function2(3) == 27
assert m.roundtrip(m.custom_function2)(3) == 27
def test_callback_docstring():
assert (
m.test_tuple_unpacking.__doc__.strip()
== "test_tuple_unpacking(arg0: Callable) -> object"
)

View File

@ -330,6 +330,23 @@ TEST_SUBMODULE(eigen_matrix, m) {
m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); });
m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; });
m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; });
// test_defaults
bool have_numpy = true;
try {
py::module_::import("numpy");
} catch (const py::error_already_set &) {
have_numpy = false;
}
if (have_numpy) {
py::module_::import("numpy");
Eigen::Matrix<double, 3, 3> defaultMatrix = Eigen::Matrix3d::Identity();
m.def(
"defaults_mat", [](const Eigen::Matrix3d &) {}, py::arg("mat") = defaultMatrix);
Eigen::VectorXd defaultVector = Eigen::VectorXd::Ones(32);
m.def(
"defaults_vec", [](const Eigen::VectorXd &) {}, py::arg("vec") = defaultMatrix);
}
// test_sparse, test_sparse_signature
m.def("sparse_r", [mat]() -> SparseMatrixR {
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)

View File

@ -716,6 +716,11 @@ def test_dense_signature(doc):
)
def test_defaults(doc):
assert "\n" not in str(doc(m.defaults_mat))
assert "\n" not in str(doc(m.defaults_vec))
def test_named_arguments():
a = np.array([[1.0, 2], [3, 4], [5, 6]])
b = np.ones((2, 1))

View File

@ -264,3 +264,8 @@ def test_docstring_signatures():
for attr in enum_type.__dict__.values():
# Issue #2623/PR #2637: Add argument names to enum_ methods
assert "arg0" not in (attr.__doc__ or "")
def test_str_signature():
for enum_type in [m.ScopedEnum, m.UnscopedEnum]:
assert enum_type.__str__.__doc__.startswith("__str__")

View File

@ -42,6 +42,50 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
m.def("kw_func_udl", kw_func, "x"_a, "y"_a = 300);
m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a = 0);
// test line breaks in default argument representation
struct CustomRepr {
std::string repr_string;
explicit CustomRepr(const std::string &repr) : repr_string(repr) {}
std::string __repr__() const { return repr_string; }
};
py::class_<CustomRepr>(m, "CustomRepr")
.def(py::init<const std::string &>())
.def("__repr__", &CustomRepr::__repr__);
m.def(
"kw_lb_func0",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr(" array([[A, B], [C, D]]) "));
m.def(
"kw_lb_func1",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr(" array([[A, B],\n[C, D]]) "));
m.def(
"kw_lb_func2",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("\v\n array([[A, B], [C, D]])"));
m.def(
"kw_lb_func3",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("array([[A, B], [C, D]]) \f\n"));
m.def(
"kw_lb_func4",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("array([[A, B],\n\f\n[C, D]])"));
m.def(
"kw_lb_func5",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("array([[A, B],\r [C, D]])"));
m.def(
"kw_lb_func6", [](const CustomRepr &) {}, py::arg("custom") = CustomRepr(" \v\t "));
m.def(
"kw_lb_func7",
[](const std::string &) {},
py::arg("str_arg") = "First line.\n Second line.");
// test_args_and_kwargs
m.def("args_function", [](py::args args) -> py::tuple {
PYBIND11_WARNING_PUSH

View File

@ -23,6 +23,38 @@ def test_function_signatures(doc):
doc(m.KWClass.foo1)
== "foo1(self: m.kwargs_and_defaults.KWClass, x: int, y: float) -> None"
)
assert (
doc(m.kw_lb_func0)
== "kw_lb_func0(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func1)
== "kw_lb_func1(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func2)
== "kw_lb_func2(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func3)
== "kw_lb_func3(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func4)
== "kw_lb_func4(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func5)
== "kw_lb_func5(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func6)
== "kw_lb_func6(custom: m.kwargs_and_defaults.CustomRepr = ) -> None"
)
assert (
doc(m.kw_lb_func7)
== "kw_lb_func7(str_arg: str = 'First line.\\n Second line.') -> None"
)
def test_named_arguments():

View File

@ -28,6 +28,13 @@ class NonZeroIterator {
public:
explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {}
// Make the iterator non-copyable and movable
NonZeroIterator(const NonZeroIterator &) = delete;
NonZeroIterator(NonZeroIterator &&) noexcept = default;
NonZeroIterator &operator=(const NonZeroIterator &) = delete;
NonZeroIterator &operator=(NonZeroIterator &&) noexcept = default;
const T &operator*() const { return *ptr_; }
NonZeroIterator &operator++() {
++ptr_;
@ -78,6 +85,7 @@ private:
int value_;
};
using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>);
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>);
@ -375,6 +383,17 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
private:
std::vector<std::pair<int, int>> data_;
};
{
// #4383 : Make sure `py::make_*iterator` functions work with move-only iterators
using iterator_t = NonZeroIterator<std::pair<int, int>>;
static_assert(std::is_move_assignable<iterator_t>::value, "");
static_assert(std::is_move_constructible<iterator_t>::value, "");
static_assert(!std::is_copy_assignable<iterator_t>::value, "");
static_assert(!std::is_copy_constructible<iterator_t>::value, "");
}
py::class_<IntPairs>(m, "IntPairs")
.def(py::init<std::vector<std::pair<int, int>>>())
.def(

View File

@ -209,7 +209,7 @@ def test_map_string_double_const():
def test_noncopyable_containers():
# std::vector
vnc = m.get_vnc(5)
for i in range(0, 5):
for i in range(5):
assert vnc[i].value == i + 1
for i, j in enumerate(vnc, start=1):
@ -217,7 +217,7 @@ def test_noncopyable_containers():
# std::deque
dnc = m.get_dnc(5)
for i in range(0, 5):
for i in range(5):
assert dnc[i].value == i + 1
i = 1
@ -252,7 +252,7 @@ def test_noncopyable_containers():
# nested std::map<std::vector>
nvnc = m.get_nvnc(5)
for i in range(1, 6):
for j in range(0, 5):
for j in range(5):
assert nvnc[i][j].value == j + 1
# Note: maps do not have .values()