diff --git a/.clang-tidy b/.clang-tidy index d01ca352c..e82443c4c 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -25,11 +25,12 @@ modernize-replace-random-shuffle, modernize-shrink-to-fit, modernize-use-auto, modernize-use-bool-literals, +modernize-use-default-member-init, modernize-use-equals-default, modernize-use-equals-delete, -modernize-use-default-member-init, -modernize-use-noexcept, modernize-use-emplace, +modernize-use-noexcept, +modernize-use-nullptr, modernize-use-override, modernize-use-using, *performance*, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3eb4e7b42..f784764b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: - '3.10' - 'pypy-3.7' - 'pypy-3.8' + - 'pypy-3.9' # Items in here will either be added to the build matrix (if not # present), or add new keys to an existing matrix element if all the @@ -45,6 +46,10 @@ jobs: args: > -DPYBIND11_FINDPYTHON=ON -DCMAKE_CXX_FLAGS="-D_=1" + - runs-on: ubuntu-latest + python: 'pypy-3.8' + args: > + -DPYBIND11_FINDPYTHON=ON - runs-on: windows-2019 python: '3.6' args: > @@ -668,7 +673,7 @@ jobs: # This verifies that the documentation is not horribly broken, and does a - # basic sanity check on the SDist. + # basic validation check on the SDist. doxygen: name: "Documentation build test" runs-on: ubuntu-latest @@ -756,55 +761,60 @@ jobs: - name: Python tests run: cmake --build build -t pytest - win32-msvc2017: - name: "🐍 ${{ matrix.python }} • MSVC 2017 • x64" - runs-on: windows-2016 + win32-debug: strategy: fail-fast: false matrix: python: - - 3.6 - - 3.7 - std: - - 14 + - 3.8 + - 3.9 include: - - python: 3.7 - std: 17 - args: > - -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" + - python: 3.9 + args: -DCMAKE_CXX_STANDARD=20 + - python: 3.8 + args: -DCMAKE_CXX_STANDARD=17 + + name: "🐍 ${{ matrix.python }} • MSVC 2019 (Debug) • x86 ${{ matrix.args }}" + runs-on: windows-2019 steps: - uses: actions/checkout@v2 - - name: Setup 🐍 ${{ matrix.python }} + - name: Setup Python ${{ matrix.python }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} + architecture: x86 - name: Update CMake uses: jwlawson/actions-setup-cmake@v1.12 + - name: Prepare MSVC + uses: ilammy/msvc-dev-cmd@v1.10.0 + with: + arch: x86 + - name: Prepare env run: | python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - - name: Configure + - name: Configure ${{ matrix.args }} run: > cmake -S . -B build - -G "Visual Studio 15 2017" -A x64 + -G "Visual Studio 16 2019" -A Win32 + -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} ${{ matrix.args }} + - name: Build C++11 + run: cmake --build build --config Debug -j 2 - - name: Build ${{ matrix.std }} - run: cmake --build build -j 2 + - name: Python tests + run: cmake --build build --config Debug -t pytest - - name: Run all checks - run: cmake --build build -t check windows-2022: strategy: diff --git a/.github/workflows/configure.yml b/.github/workflows/configure.yml index 66ab0e3d7..55be5bd34 100644 --- a/.github/workflows/configure.yml +++ b/.github/workflows/configure.yml @@ -18,7 +18,7 @@ jobs: matrix: runs-on: [ubuntu-latest, macos-latest, windows-latest] arch: [x64] - cmake: ["3.21"] + cmake: ["3.23"] include: - runs-on: ubuntu-latest @@ -29,12 +29,8 @@ jobs: arch: x64 cmake: 3.7 - - runs-on: windows-2016 - arch: x86 - cmake: 3.8 - - - runs-on: windows-2016 - arch: x86 + - runs-on: windows-2019 + arch: x64 # x86 compilers seem to be missing on 2019 image cmake: 3.18 name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9a0e03f2..099f0dc26 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: "v4.1.0" + rev: "v4.2.0" hooks: - id: check-added-large-files - id: check-case-conflict @@ -32,7 +32,7 @@ repos: # Upgrade old Python syntax - repo: https://github.com/asottile/pyupgrade - rev: "v2.31.0" + rev: "v2.32.0" hooks: - id: pyupgrade args: [--py36-plus] @@ -45,7 +45,7 @@ repos: # Black, the code formatter, natively supports pre-commit - repo: https://github.com/psf/black - rev: "22.1.0" # Keep in sync with blacken-docs + rev: "22.3.0" # Keep in sync with blacken-docs hooks: - id: black @@ -55,7 +55,7 @@ repos: hooks: - id: blacken-docs additional_dependencies: - - black==22.1.0 # keep in sync with black hook + - black==22.3.0 # keep in sync with black hook # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks @@ -64,16 +64,17 @@ repos: - id: remove-tabs - repo: https://github.com/sirosen/texthooks - rev: "0.2.2" + rev: "0.3.1" hooks: - id: fix-ligatures - id: fix-smartquotes # Autoremoves unused imports - repo: https://github.com/hadialqattan/pycln - rev: "v1.2.4" + rev: "v1.3.1" hooks: - id: pycln + stages: [manual] # Checking for common mistakes - repo: https://github.com/pre-commit/pygrep-hooks @@ -106,7 +107,7 @@ repos: # PyLint has native support - not always usable, but works for us - repo: https://github.com/PyCQA/pylint - rev: "v2.12.2" + rev: "v2.13.5" hooks: - id: pylint files: ^pybind11 @@ -122,16 +123,16 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v0.931" + rev: "v0.942" hooks: - id: mypy - args: [--show-error-codes] + args: [] exclude: ^(tests|docs)/ additional_dependencies: [nox, rich] # Checks the manifest for missing files (native support) - repo: https://github.com/mgedmin/check-manifest - rev: "0.47" + rev: "0.48" hooks: - id: check-manifest # This is a slow hook, so only run this if --hook-stage manual is passed @@ -163,7 +164,7 @@ repos: # Clang format the codebase automatically - repo: https://github.com/pre-commit/mirrors-clang-format - rev: "v13.0.1" + rev: "v14.0.1" hooks: - id: clang-format types_or: [c++, c, cuda] diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index 8ad341004..b6ef019ed 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -87,7 +87,7 @@ buffer objects (e.g. a NumPy matrix). /* Request a buffer descriptor from Python */ py::buffer_info info = b.request(); - /* Some sanity checks ... */ + /* Some basic validation checks ... */ if (info.format != py::format_descriptor::format()) throw std::runtime_error("Incompatible format: expected a double array!"); diff --git a/docs/changelog.rst b/docs/changelog.rst index 9d7693cdd..67ac79cdd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,10 +6,129 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. +Changes will be added here periodically from the "Suggested changelog entry" +block in pull request descriptions. + IN DEVELOPMENT -------------- -Changes will be added here periodically. +Removed support for Python 2.7, Python 3.5, and MSVC 2015. Support for MSVC +2017 is limited due to availability of CI runners; we highly recommend MSVC +2019 or 2022 be used. + +New features: + +* ``type_caster`` was added. ``std::monostate`` is a tag type + that allows ``std::variant`` to act as an optional, or allows default + construction of a ``std::variant`` holding a non-default constructible type. + `#3818 `_ + +* Support bytearray casting to string. + `#3707 `_ + +Changes: + +* Python 2 support was removed completely. + `#3688 `_ + +* The minimum version for MSVC is now 2017. + `#3722 `_ + +* Improve exception handling in python ``str`` bindings. + `#3826 `_ + +* The bindings for capsules now have more consistent exception handling. + `#3825 `_ + +* Fix exception handling when ``pybind11::weakref()`` fails. + `#3739 `_ + + +Bug fixes: + +* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can be used + to define classes in namespaces other than pybind11. + `#3797 `_ + +Build system improvements: + +* Add MSVC builds in debug mode to CI. + `#3784 `_ + +* MSVC 2022 C++20 coverage was added to GitHub Actions, including Eigen. + `#3732 `_, + `#3741 `_ + +* Avoid ``setup.py `` usage in internal tests. + `#3734 `_ + + +Backend and tidying up: + +* Remove idioms in code comments. Use inclusive language. + `#3809 `_ + + +Version 2.9.2 (Mar 29, 2022) +---------------------------- + +Changes: + +* Enum now has an ``__index__`` method on Python <3.8 too. + `#3700 `_ + +* Local internals are now cleared after finalizing the interpreter. + `#3744 `_ + +Bug fixes: + +* Better support for Python 3.11 alphas. + `#3694 `_ + +* ``PYBIND11_TYPE_CASTER`` now uses fully qualified symbols, so it can be used + outside of ``pybind11::detail``. + `#3758 `_ + +* Some fixes for PyPy 3.9. + `#3768 `_ + +* Fixed a potential memleak in PyPy in ``get_type_override``. + `#3774 `_ + +* Fix usage of ``VISIBILITY_INLINES_HIDDEN``. + `#3721 `_ + + +Build system improvements: + +* Uses ``sysconfig`` module to determine installation locations on Python >= + 3.10, instead of ``distutils`` which has been deprecated. + `#3764 `_ + +* Support Catch 2.13.5+ (supporting GLIBC 2.34+). + `#3679 `_ + +* Fix test failures with numpy 1.22 by ignoring whitespace when comparing + ``str()`` of dtypes. + `#3682 `_ + + +Backend and tidying up: + +* clang-tidy: added ``readability-qualified-auto``, + ``readability-braces-around-statements``, + ``cppcoreguidelines-prefer-member-initializer``, + ``clang-analyzer-optin.performance.Padding``, + ``cppcoreguidelines-pro-type-static-cast-downcast``, and + ``readability-inconsistent-declaration-parameter-name``. + `#3702 `_, + `#3699 `_, + `#3716 `_, + `#3709 `_ + +* clang-format was added to the pre-commit actions, and the entire code base + automatically reformatted (after several iterations preparing for this leap). + `#3713 `_ Version 2.9.1 (Feb 2, 2022) diff --git a/docs/compiling.rst b/docs/compiling.rst index 4ae9234e1..2b543be0b 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -505,7 +505,10 @@ You can use these targets to build complex applications. For example, the target_link_libraries(example PRIVATE pybind11::module pybind11::lto pybind11::windows_extras) pybind11_extension(example) - pybind11_strip(example) + if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + # Strip unnecessary sections of the binary on Linux/macOS + pybind11_strip(example) + endif() set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden" CUDA_VISIBILITY_PRESET "hidden") diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index d45b49c52..e8128710e 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -514,7 +514,7 @@ struct type_caster, template struct type_caster::value>> { using StringType = std::basic_string; - using StringCaster = type_caster; + using StringCaster = make_caster; StringCaster str_caster; bool none = false; CharT one_char = 0; @@ -1155,15 +1155,18 @@ enable_if_t::value, T> cast_ref(object &&, // static_assert, even though if it's in dead code, so we provide a "trampoline" to pybind11::cast // that only does anything in cases where pybind11::cast is valid. template -enable_if_t::value, T> cast_safe(object &&o) { - return pybind11::cast(std::move(o)); -} -template enable_if_t::value, T> cast_safe(object &&) { pybind11_fail("Internal error: cast_safe fallback invoked"); } -template <> -inline void cast_safe(object &&) {} +template +enable_if_t>::value, void> cast_safe(object &&) {} +template +enable_if_t, + std::is_same>>::value, + T> +cast_safe(object &&o) { + return pybind11::cast(std::move(o)); +} PYBIND11_NAMESPACE_END(detail) @@ -1243,8 +1246,8 @@ struct arg_v : arg { private: template arg_v(arg &&base, T &&x, const char *descr = nullptr) - : arg(base), value(reinterpret_steal( - detail::make_caster::cast(x, return_value_policy::automatic, {}))), + : arg(base), value(reinterpret_steal(detail::make_caster::cast( + std::forward(x), return_value_policy::automatic, {}))), descr(descr) #if !defined(NDEBUG) , @@ -1491,7 +1494,7 @@ private: type_id()); #endif } - args_list.append(o); + args_list.append(std::move(o)); } void process(list &args_list, detail::args_proxy ap) { diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index ea09bb3fd..a130ebea0 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -992,6 +992,8 @@ constexpr const char struct error_scope { PyObject *type, *value, *trace; error_scope() { PyErr_Fetch(&type, &value, &trace); } + error_scope(const error_scope &) = delete; + error_scope &operator=(const error_scope &) = delete; ~error_scope() { PyErr_Restore(type, value, trace); } }; diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index bfdcf49e1..767b9044a 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -225,8 +225,8 @@ PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp, if (throw_if_missing) { std::string tname = tp.name(); detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname - + "\""); + pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + + std::move(tname) + '"'); } return nullptr; } diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index 930c4f7fa..f658168de 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -668,7 +668,7 @@ struct type_caster::value>> { Type::Flags &(Eigen::RowMajor | Eigen::ColMajor), StorageIndex>(shape[0].cast(), shape[1].cast(), - nnz, + std::move(nnz), outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); @@ -686,7 +686,8 @@ struct type_caster::value>> { array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); array innerIndices(src.nonZeros(), src.innerIndexPtr()); - return matrix_type(std::make_tuple(data, innerIndices, outerIndices), + return matrix_type(std::make_tuple( + std::move(data), std::move(innerIndices), std::move(outerIndices)), std::make_pair(src.rows(), src.cols())) .release(); } diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index c157a0095..bd5f981f5 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -82,7 +82,7 @@ template object eval(const char (&s)[N], object global = globals(), object local = object()) { /* Support raw string literals by removing common leading whitespace */ auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s); - return eval(expr, global, local); + return eval(expr, std::move(global), std::move(local)); } inline void exec(const str &expr, object global = globals(), object local = object()) { @@ -91,7 +91,7 @@ inline void exec(const str &expr, object global = globals(), object local = obje template void exec(const char (&s)[N], object global = globals(), object local = object()) { - eval(s, global, local); + eval(s, std::move(global), std::move(local)); } #if defined(PYPY_VERSION) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 5e68f6053..2713e13a8 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -263,7 +263,7 @@ private: static npy_api lookup() { module_ m = module_::import("numpy.core.multiarray"); auto c = m.attr("_ARRAY_API"); - void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr); npy_api api; #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); @@ -540,18 +540,18 @@ public: PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); explicit dtype(const buffer_info &info) { - dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); + dtype descr(_dtype_from_pep3118()(pybind11::str(info.format))); // If info.itemsize == 0, use the value calculated from the format string m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize()) .release() .ptr(); } - explicit dtype(const std::string &format) { - m_ptr = from_args(pybind11::str(format)).release().ptr(); - } + explicit dtype(const pybind11::str &format) : dtype(from_args(format)) {} - explicit dtype(const char *format) : dtype(std::string(format)) {} + explicit dtype(const std::string &format) : dtype(pybind11::str(format)) {} + + explicit dtype(const char *format) : dtype(pybind11::str(format)) {} dtype(list names, list formats, list offsets, ssize_t itemsize) { dict args; @@ -559,11 +559,18 @@ public: args["formats"] = std::move(formats); args["offsets"] = std::move(offsets); args["itemsize"] = pybind11::int_(itemsize); - m_ptr = from_args(std::move(args)).release().ptr(); + m_ptr = from_args(args).release().ptr(); + } + + explicit dtype(int typenum) + : object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) { + if (m_ptr == nullptr) { + throw error_already_set(); + } } /// This is essentially the same as calling numpy.dtype(args) in Python. - static dtype from_args(object args) { + static dtype from_args(const object &args) { PyObject *ptr = nullptr; if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr) { throw error_already_set(); @@ -596,6 +603,23 @@ public: return detail::array_descriptor_proxy(m_ptr)->type; } + /// type number of dtype. + ssize_t num() const { + // Note: The signature, `dtype::num` follows the naming of NumPy's public + // Python API (i.e., ``dtype.num``), rather than its internal + // C API (``PyArray_Descr::type_num``). + return detail::array_descriptor_proxy(m_ptr)->type_num; + } + + /// Single character for byteorder + char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; } + + /// Alignment of the data type + int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; } + + /// Flags for the array descriptor + char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; } + private: static object _dtype_from_pep3118() { static PyObject *obj = module_::import("numpy.core._internal") @@ -614,7 +638,7 @@ private: } struct field_descr { - PYBIND11_STR_TYPE name; + pybind11::str name; object format; pybind11::int_ offset; }; @@ -629,7 +653,7 @@ private: continue; } field_descriptors.push_back( - {(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); + {(pybind11::str) name, format.strip_padding(format.itemsize()), offset}); } std::sort(field_descriptors.begin(), @@ -640,9 +664,9 @@ private: list names, formats, offsets; for (auto &descr : field_descriptors) { - names.append(descr.name); - formats.append(descr.format); - offsets.append(descr.offset); + names.append(std::move(descr.name)); + formats.append(std::move(descr.format)); + offsets.append(std::move(descr.offset)); } return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize); } @@ -940,7 +964,7 @@ protected: void fail_dim_check(ssize_t dim, const std::string &msg) const { throw index_error(msg + ": " + std::to_string(dim) + " (ndim = " + std::to_string(ndim()) - + ")"); + + ')'); } template @@ -1144,11 +1168,11 @@ struct format_descriptor::value> template struct format_descriptor { - static std::string format() { return std::to_string(N) + "s"; } + static std::string format() { return std::to_string(N) + 's'; } }; template struct format_descriptor> { - static std::string format() { return std::to_string(N) + "s"; } + static std::string format() { return std::to_string(N) + 's'; } }; template @@ -1288,7 +1312,8 @@ public: static pybind11::dtype dtype() { list shape; array_info::append_extents(shape); - return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); + return pybind11::dtype::from_args( + pybind11::make_tuple(base_descr::dtype(), std::move(shape))); } }; @@ -1334,7 +1359,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ " + tinfo.name()); } - names.append(PYBIND11_STR_TYPE(field.name)); + names.append(pybind11::str(field.name)); formats.append(field.descr); offsets.append(pybind11::int_(field.offset)); } @@ -1526,7 +1551,7 @@ public: void *data() const { return p_ptr; } private: - char *p_ptr{0}; + char *p_ptr{nullptr}; container_type m_strides; }; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 4f6b2cdf4..ce05b6e4e 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -561,14 +561,14 @@ protected: for (auto *it = chain_start; it != nullptr; it = it->next) { if (options::show_function_signatures()) { if (index > 0) { - signatures += "\n"; + signatures += '\n'; } if (chain) { signatures += std::to_string(++index) + ". "; } signatures += rec->name; signatures += it->signature; - signatures += "\n"; + signatures += '\n'; } if (it->doc && it->doc[0] != '\0' && options::show_user_defined_docstrings()) { // If we're appending another docstring, and aren't printing function signatures, @@ -577,15 +577,15 @@ protected: if (first_user_def) { first_user_def = false; } else { - signatures += "\n"; + signatures += '\n'; } } if (options::show_function_signatures()) { - signatures += "\n"; + signatures += '\n'; } signatures += it->doc; if (options::show_function_signatures()) { - signatures += "\n"; + signatures += '\n'; } } } @@ -1055,7 +1055,7 @@ protected: msg += it2->signature; } - msg += "\n"; + msg += '\n'; } msg += "\nInvoked with: "; auto args_ = reinterpret_borrow(args_in); @@ -1817,7 +1817,8 @@ private: if (holder_ptr) { init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); v_h.set_holder_constructed(); - } else if (inst->owned || detail::always_construct_holder::value) { + } else if (PYBIND11_SILENCE_MSVC_C4127(detail::always_construct_holder::value) + || inst->owned) { new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); v_h.set_holder_constructed(); } @@ -2402,7 +2403,8 @@ template iterator make_iterator(Type &value, Extra &&...extra) { - return make_iterator(std::begin(value), std::end(value), extra...); + return make_iterator( + std::begin(value), std::end(value), std::forward(extra)...); } /// Makes an iterator over the keys (`.first`) of a stl map-like container supporting @@ -2411,7 +2413,8 @@ template iterator make_key_iterator(Type &value, Extra &&...extra) { - return make_key_iterator(std::begin(value), std::end(value), extra...); + return make_key_iterator( + std::begin(value), std::end(value), std::forward(extra)...); } /// Makes an iterator over the values (`.second`) of a stl map-like container supporting @@ -2420,7 +2423,8 @@ template iterator make_value_iterator(Type &value, Extra &&...extra) { - return make_value_iterator(std::begin(value), std::end(value), extra...); + return make_value_iterator( + std::begin(value), std::end(value), std::forward(extra)...); } template @@ -2485,7 +2489,7 @@ public: exception(handle scope, const char *name, handle base = PyExc_Exception) { std::string full_name = scope.attr("__name__").cast() + std::string(".") + name; - m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base.ptr(), NULL); + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base.ptr(), nullptr); if (hasattr(scope, "__dict__") && scope.attr("__dict__").contains(name)) { pybind11_fail("Error during initialization: multiple incompatible " "definitions with name \"" @@ -2694,9 +2698,9 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char * d.ptr()); if (result == nullptr) throw error_already_set(); + Py_DECREF(result); if (d["self"].is_none()) return function(); - Py_DECREF(result); #endif return override; diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index f6f5acd18..9584443d1 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -270,10 +270,7 @@ public: /// Copy constructor; always increases the reference count object(const object &o) : handle(o) { inc_ref(); } /// Move constructor; steals the object from ``other`` and preserves its reference count - object(object &&other) noexcept { - m_ptr = other.m_ptr; - other.m_ptr = nullptr; - } + object(object &&other) noexcept : handle(other) { other.m_ptr = nullptr; } /// Destructor; automatically calls `handle::dec_ref()` ~object() { dec_ref(); } @@ -636,13 +633,13 @@ inline handle get_function(handle value) { inline PyObject *dict_getitemstring(PyObject *v, const char *key) { PyObject *kv = nullptr, *rv = nullptr; kv = PyUnicode_FromString(key); - if (kv == NULL) { + if (kv == nullptr) { throw error_already_set(); } rv = PyDict_GetItemWithError(v, kv); Py_DECREF(kv); - if (rv == NULL && PyErr_Occurred()) { + if (rv == nullptr && PyErr_Occurred()) { throw error_already_set(); } return rv; @@ -650,7 +647,7 @@ inline PyObject *dict_getitemstring(PyObject *v, const char *key) { inline PyObject *dict_getitem(PyObject *v, PyObject *key) { PyObject *rv = PyDict_GetItemWithError(v, key); - if (rv == NULL && PyErr_Occurred()) { + if (rv == nullptr && PyErr_Occurred()) { throw error_already_set(); } return rv; @@ -1071,12 +1068,12 @@ public: Name(const object &o) \ : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) { \ if (!m_ptr) \ - throw error_already_set(); \ + throw ::pybind11::error_already_set(); \ } \ /* NOLINTNEXTLINE(google-explicit-constructor) */ \ Name(object &&o) : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) { \ if (!m_ptr) \ - throw error_already_set(); \ + throw ::pybind11::error_already_set(); \ } #define PYBIND11_OBJECT_CVT_DEFAULT(Name, Parent, CheckFun, ConvertFun) \ @@ -1276,8 +1273,8 @@ public: } char *buffer = nullptr; ssize_t length = 0; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) { - pybind11_fail("Unable to extract string contents! (invalid type)"); + if (PyBytes_AsStringAndSize(temp.ptr(), &buffer, &length) != 0) { + throw error_already_set(); } return std::string(buffer, (size_t) length); } @@ -1332,14 +1329,7 @@ public: explicit bytes(const pybind11::str &s); // NOLINTNEXTLINE(google-explicit-constructor) - operator std::string() const { - char *buffer = nullptr; - ssize_t length = 0; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) { - pybind11_fail("Unable to extract bytes contents!"); - } - return std::string(buffer, (size_t) length); - } + operator std::string() const { return string_op(); } #ifdef PYBIND11_HAS_STRING_VIEW // enable_if is needed to avoid "ambiguous conversion" errors (see PR #3521). @@ -1351,15 +1341,18 @@ public: // valid so long as the `bytes` instance remains alive and so generally should not outlive the // lifetime of the `bytes` instance. // NOLINTNEXTLINE(google-explicit-constructor) - operator std::string_view() const { + operator std::string_view() const { return string_op(); } +#endif +private: + template + T string_op() const { char *buffer = nullptr; ssize_t length = 0; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) { - pybind11_fail("Unable to extract bytes contents!"); + if (PyBytes_AsStringAndSize(m_ptr, &buffer, &length) != 0) { + throw error_already_set(); } return {buffer, static_cast(length)}; } -#endif }; // Note: breathe >= 4.17.0 will fail to build docs if the below two constructors // are included in the doxygen group; close here and reopen after as a workaround @@ -1370,13 +1363,13 @@ inline bytes::bytes(const pybind11::str &s) { if (PyUnicode_Check(s.ptr())) { temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); if (!temp) { - pybind11_fail("Unable to extract string contents! (encoding issue)"); + throw error_already_set(); } } char *buffer = nullptr; ssize_t length = 0; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) { - pybind11_fail("Unable to extract string contents! (invalid type)"); + if (PyBytes_AsStringAndSize(temp.ptr(), &buffer, &length) != 0) { + throw error_already_set(); } auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); if (!obj) { @@ -1388,8 +1381,8 @@ inline bytes::bytes(const pybind11::str &s) { inline str::str(const bytes &b) { char *buffer = nullptr; ssize_t length = 0; - if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) { - pybind11_fail("Unable to extract bytes contents!"); + if (PyBytes_AsStringAndSize(b.ptr(), &buffer, &length) != 0) { + throw error_already_set(); } auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, length)); if (!obj) { @@ -1556,8 +1549,8 @@ private: class slice : public object { public: PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) - slice(handle start, handle stop, handle step) { - m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); + slice(handle start, handle stop, handle step) + : object(PySlice_New(start.ptr(), stop.ptr(), step.ptr()), stolen_t{}) { if (!m_ptr) { pybind11_fail("Could not allocate slice object!"); } @@ -1607,7 +1600,7 @@ public: void (*destructor)(PyObject *) = nullptr) : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { if (!m_ptr) { - pybind11_fail("Could not allocate capsule object!"); + throw error_already_set(); } } @@ -1615,34 +1608,44 @@ public: capsule(const void *value, void (*destruct)(PyObject *)) : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { if (!m_ptr) { - pybind11_fail("Could not allocate capsule object!"); + throw error_already_set(); } } capsule(const void *value, void (*destructor)(void *)) { m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); - void *ptr = PyCapsule_GetPointer(o, nullptr); + if (destructor == nullptr) { + if (PyErr_Occurred()) { + throw error_already_set(); + } + pybind11_fail("Unable to get capsule context"); + } + const char *name = get_name_in_error_scope(o); + void *ptr = PyCapsule_GetPointer(o, name); + if (ptr == nullptr) { + throw error_already_set(); + } destructor(ptr); }); - if (!m_ptr) { - pybind11_fail("Could not allocate capsule object!"); - } - - if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) { - pybind11_fail("Could not set capsule context!"); + if (!m_ptr || PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) { + throw error_already_set(); } } explicit capsule(void (*destructor)()) { m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { - auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); + const char *name = get_name_in_error_scope(o); + auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, name)); + if (destructor == nullptr) { + throw error_already_set(); + } destructor(); }); if (!m_ptr) { - pybind11_fail("Could not allocate capsule object!"); + throw error_already_set(); } } @@ -1657,8 +1660,7 @@ public: const auto *name = this->name(); T *result = static_cast(PyCapsule_GetPointer(m_ptr, name)); if (!result) { - PyErr_Clear(); - pybind11_fail("Unable to extract capsule contents!"); + throw error_already_set(); } return result; } @@ -1666,12 +1668,37 @@ public: /// Replaces a capsule's pointer *without* calling the destructor on the existing one. void set_pointer(const void *value) { if (PyCapsule_SetPointer(m_ptr, const_cast(value)) != 0) { - PyErr_Clear(); - pybind11_fail("Could not set capsule pointer"); + throw error_already_set(); } } - const char *name() const { return PyCapsule_GetName(m_ptr); } + const char *name() const { + const char *name = PyCapsule_GetName(m_ptr); + if ((name == nullptr) && PyErr_Occurred()) { + throw error_already_set(); + } + return name; + } + + /// Replaces a capsule's name *without* calling the destructor on the existing one. + void set_name(const char *new_name) { + if (PyCapsule_SetName(m_ptr, new_name) != 0) { + throw error_already_set(); + } + } + +private: + static const char *get_name_in_error_scope(PyObject *o) { + error_scope error_guard; + + const char *name = PyCapsule_GetName(o); + if ((name == nullptr) && PyErr_Occurred()) { + // write out and consume error raised by call to PyCapsule_GetName + PyErr_WriteUnraisable(o); + } + + return name; + } }; class tuple : public object { @@ -1920,8 +1947,8 @@ public: return memoryview::from_buffer(reinterpret_cast(ptr), sizeof(T), format_descriptor::value, - shape, - strides, + std::move(shape), + std::move(strides), readonly); } @@ -1929,7 +1956,8 @@ public: static memoryview from_buffer(const T *ptr, detail::any_container shape, detail::any_container strides) { - return memoryview::from_buffer(const_cast(ptr), shape, strides, true); + return memoryview::from_buffer( + const_cast(ptr), std::move(shape), std::move(strides), true); } /** \rst diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index b66129688..51b57a92b 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -79,7 +79,7 @@ struct set_caster { for (auto &&value : src) { auto value_ = reinterpret_steal( key_conv::cast(forward_like(value), policy, parent)); - if (!value_ || !s.add(value_)) { + if (!value_ || !s.add(std::move(value_))) { return handle(); } } @@ -372,7 +372,7 @@ struct variant_caster> { bool load_alternative(handle src, bool convert, type_list) { auto caster = make_caster(); if (caster.load(src, convert)) { - value = cast_op(caster); + value = cast_op(std::move(caster)); return true; } return load_alternative(src, convert, type_list{}); @@ -406,6 +406,9 @@ struct variant_caster> { #if defined(PYBIND11_HAS_VARIANT) template struct type_caster> : variant_caster> {}; + +template <> +struct type_caster : public void_caster {}; #endif PYBIND11_NAMESPACE_END(detail) diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index 93727424c..e26f42177 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -13,21 +13,28 @@ #include #ifdef __has_include -# if defined(PYBIND11_CPP17) && __has_include() -# include -# define PYBIND11_HAS_FILESYSTEM 1 +# if defined(PYBIND11_CPP17) +# if __has_include() && \ + PY_VERSION_HEX >= 0x03060000 +# include +# define PYBIND11_HAS_FILESYSTEM 1 +# elif __has_include() +# include +# define PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM 1 +# endif # endif #endif -#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL) +#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) \ + && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL) # error \ - "#include is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)" + "Neither #include nor #include struct path_caster { @@ -94,9 +101,16 @@ public: PYBIND11_TYPE_CASTER(T, const_name("os.PathLike")); }; +#endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) + +#if defined(PYBIND11_HAS_FILESYSTEM) template <> struct type_caster : public path_caster {}; -#endif // PYBIND11_HAS_FILESYSTEM +#elif defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) +template <> +struct type_caster + : public path_caster {}; +#endif PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 3a3e54dd4..22a29b476 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -232,7 +232,7 @@ void vector_modifiers( /// Slicing protocol cl.def( "__getitem__", - [](const Vector &v, slice slice) -> Vector * { + [](const Vector &v, const slice &slice) -> Vector * { size_t start = 0, stop = 0, step = 0, slicelength = 0; if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { @@ -253,7 +253,7 @@ void vector_modifiers( cl.def( "__setitem__", - [](Vector &v, slice slice, const Vector &value) { + [](Vector &v, const slice &slice, const Vector &value) { size_t start = 0, stop = 0, step = 0, slicelength = 0; if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { throw error_already_set(); @@ -281,7 +281,7 @@ void vector_modifiers( cl.def( "__delitem__", - [](Vector &v, slice slice) { + [](Vector &v, const slice &slice) { size_t start = 0, stop = 0, step = 0, slicelength = 0; if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { diff --git a/pyproject.toml b/pyproject.toml index f80cdc1f4..3ba1b4b22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,10 @@ profile = "black" [tool.mypy] files = ["pybind11"] python_version = "3.6" -warn_unused_configs = true strict = true +show_error_codes = true +enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +warn_unreachable = true [[tool.mypy.overrides]] module = ["ghapi.*", "setuptools.*"] diff --git a/tests/cross_module_gil_utils.cpp b/tests/cross_module_gil_utils.cpp index 2e64da052..1436c35d6 100644 --- a/tests/cross_module_gil_utils.cpp +++ b/tests/cross_module_gil_utils.cpp @@ -25,8 +25,8 @@ void gil_acquire() { py::gil_scoped_acquire gil; } constexpr char kModuleName[] = "cross_module_gil_utils"; -struct PyModuleDef moduledef - = {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL}; +struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr}; } // namespace @@ -34,7 +34,7 @@ extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() { PyObject *m = PyModule_Create(&moduledef); - if (m != NULL) { + if (m != nullptr) { static_assert(sizeof(&gil_acquire) == sizeof(void *), "Function pointer must have the same size as void*"); PyModule_AddObject( diff --git a/tests/test_numpy_dtypes.cpp b/tests/test_numpy_dtypes.cpp index dd5b123dc..c25bc042b 100644 --- a/tests/test_numpy_dtypes.cpp +++ b/tests/test_numpy_dtypes.cpp @@ -289,8 +289,9 @@ py::list test_dtype_ctors() { dict["itemsize"] = py::int_(20); list.append(py::dtype::from_args(dict)); list.append(py::dtype(names, formats, offsets, 20)); - list.append(py::dtype(py::buffer_info((void *) 0, sizeof(unsigned int), "I", 1))); - list.append(py::dtype(py::buffer_info((void *) 0, 0, "T{i:a:f:b:}", 1))); + list.append(py::dtype(py::buffer_info((void *) nullptr, sizeof(unsigned int), "I", 1))); + list.append(py::dtype(py::buffer_info((void *) nullptr, 0, "T{i:a:f:b:}", 1))); + list.append(py::dtype(py::detail::npy_api::NPY_DOUBLE_)); return list; } @@ -440,6 +441,34 @@ TEST_SUBMODULE(numpy_dtypes, m) { } return list; }); + m.def("test_dtype_num", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).num()); + } + return list; + }); + m.def("test_dtype_byteorder", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).byteorder()); + } + return list; + }); + m.def("test_dtype_alignment", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).alignment()); + } + return list; + }); + m.def("test_dtype_flags", [dtype_names]() { + py::list list; + for (const auto &dt_name : dtype_names) { + list.append(py::dtype(dt_name).flags()); + } + return list; + }); m.def("test_dtype_methods", []() { py::list list; auto dt1 = py::dtype::of(); @@ -581,5 +610,5 @@ TEST_SUBMODULE(numpy_dtypes, m) { []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); }); // test_str_leak - m.def("dtype_wrapper", [](py::object d) { return py::dtype::from_args(std::move(d)); }); + m.def("dtype_wrapper", [](const py::object &d) { return py::dtype::from_args(d); }); } diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 7df60583f..fcfd587b1 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -160,6 +160,7 @@ def test_dtype(simple_dtype): d1, np.dtype("uint32"), d2, + np.dtype("d"), ] assert m.test_dtype_methods() == [ @@ -175,8 +176,13 @@ def test_dtype(simple_dtype): np.zeros(1, m.trailing_padding_dtype()) ) + expected_chars = "bhilqBHILQefdgFDG?MmO" assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmO") - assert m.test_dtype_char_() == list("bhilqBHILQefdgFDG?MmO") + assert m.test_dtype_char_() == list(expected_chars) + assert m.test_dtype_num() == [np.dtype(ch).num for ch in expected_chars] + assert m.test_dtype_byteorder() == [np.dtype(ch).byteorder for ch in expected_chars] + assert m.test_dtype_alignment() == [np.dtype(ch).alignment for ch in expected_chars] + assert m.test_dtype_flags() == [chr(np.dtype(ch).flags) for ch in expected_chars] def test_recarray(simple_dtype, packed_dtype): diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 1ed237ea2..d1e9b81a7 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -11,6 +11,34 @@ #include +namespace external { +namespace detail { +bool check(PyObject *o) { return PyFloat_Check(o) != 0; } + +PyObject *conv(PyObject *o) { + PyObject *ret = nullptr; + if (PyLong_Check(o)) { + double v = PyLong_AsDouble(o); + if (!(v == -1.0 && PyErr_Occurred())) { + ret = PyFloat_FromDouble(v); + } + } else { + PyErr_SetString(PyExc_TypeError, "Unexpected type"); + } + return ret; +} + +PyObject *default_constructed() { return PyFloat_FromDouble(0.0); } +} // namespace detail +class float_ : public py::object { + PYBIND11_OBJECT_CVT(float_, py::object, external::detail::check, external::detail::conv) + + float_() : py::object(external::detail::default_constructed(), stolen_t{}) {} + + double get_value() const { return PyFloat_AsDouble(this->ptr()); } +}; +} // namespace external + TEST_SUBMODULE(pytypes, m) { // test_bool m.def("get_bool", [] { return py::bool_(false); }); @@ -131,6 +159,15 @@ TEST_SUBMODULE(pytypes, m) { return py::capsule([]() { py::print("destructing capsule"); }); }); + m.def("return_renamed_capsule_with_destructor", []() { + py::print("creating capsule"); + auto cap = py::capsule([]() { py::print("destructing capsule"); }); + static const char *capsule_name = "test_name1"; + py::print("renaming capsule"); + cap.set_name(capsule_name); + return cap; + }); + m.def("return_capsule_with_destructor_2", []() { py::print("creating capsule"); return py::capsule((void *) 1234, [](void *ptr) { @@ -138,6 +175,17 @@ TEST_SUBMODULE(pytypes, m) { }); }); + m.def("return_renamed_capsule_with_destructor_2", []() { + py::print("creating capsule"); + auto cap = py::capsule((void *) 1234, [](void *ptr) { + py::print("destructing capsule: {}"_s.format((size_t) ptr)); + }); + static const char *capsule_name = "test_name2"; + py::print("renaming capsule"); + cap.set_name(capsule_name); + return cap; + }); + m.def("return_capsule_with_name_and_destructor", []() { auto capsule = py::capsule((void *) 12345, "pointer type description", [](PyObject *ptr) { if (ptr) { @@ -545,4 +593,9 @@ TEST_SUBMODULE(pytypes, m) { py::detail::accessor_policies::tuple_item::set(o, (py::size_t) 0, s0); return o; }); + + m.def("square_float_", [](const external::float_ &x) -> double { + double v = x.get_value(); + return v * v; + }); } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index becd1cc8a..5c715ada6 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -195,6 +195,19 @@ def test_capsule(capture): """ ) + with capture: + a = m.return_renamed_capsule_with_destructor() + del a + pytest.gc_collect() + assert ( + capture.unordered + == """ + creating capsule + renaming capsule + destructing capsule + """ + ) + with capture: a = m.return_capsule_with_destructor_2() del a @@ -207,6 +220,19 @@ def test_capsule(capture): """ ) + with capture: + a = m.return_renamed_capsule_with_destructor_2() + del a + pytest.gc_collect() + assert ( + capture.unordered + == """ + creating capsule + renaming capsule + destructing capsule: 1234 + """ + ) + with capture: a = m.return_capsule_with_name_and_destructor() del a @@ -634,3 +660,8 @@ def test_implementation_details(): assert m.tuple_item_set_ssize_t() == ("emely", "edmond") assert m.tuple_item_get_size_t(tup) == 93 assert m.tuple_item_set_size_t() == ("candy", "cat") + + +def test_external_float_(): + r1 = m.square_float_(2.0) + assert r1 == 4.0 diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index fd1824beb..b56a91953 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -37,9 +37,10 @@ struct type_caster : void_caster {}; // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 #if defined(PYBIND11_HAS_VARIANT) using std::variant; +# define PYBIND11_TEST_VARIANT 1 #elif defined(PYBIND11_TEST_BOOST) # include -# define PYBIND11_HAS_VARIANT 1 +# define PYBIND11_TEST_VARIANT 1 using boost::variant; namespace pybind11 { @@ -424,7 +425,7 @@ TEST_SUBMODULE(stl, m) { m.def("parent_path", [](const std::filesystem::path &p) { return p.parent_path(); }); #endif -#ifdef PYBIND11_HAS_VARIANT +#ifdef PYBIND11_TEST_VARIANT static_assert(std::is_same::value, "visitor::result_type is required by boost::variant in C++11 mode"); @@ -435,6 +436,9 @@ TEST_SUBMODULE(stl, m) { result_type operator()(const std::string &) { return "std::string"; } result_type operator()(double) { return "double"; } result_type operator()(std::nullptr_t) { return "std::nullptr_t"; } +# if defined(PYBIND11_HAS_VARIANT) + result_type operator()(std::monostate) { return "std::monostate"; } +# endif }; // test_variant @@ -448,6 +452,18 @@ TEST_SUBMODULE(stl, m) { using V = variant; return py::make_tuple(V(5), V("Hello")); }); + +# if defined(PYBIND11_HAS_VARIANT) + // std::monostate tests. + m.def("load_monostate_variant", + [](const variant &v) -> const char * { + return py::detail::visit_helper::call(visitor(), v); + }); + m.def("cast_monostate_variant", []() { + using V = variant; + return py::make_tuple(V{}, V(5), V("Hello")); + }); +# endif #endif // #528: templated constructor diff --git a/tests/test_stl.py b/tests/test_stl.py index a3d686689..3dc55230a 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -263,6 +263,22 @@ def test_variant(doc): ) +@pytest.mark.skipif( + not hasattr(m, "load_monostate_variant"), reason="no std::monostate" +) +def test_variant_monostate(doc): + assert m.load_monostate_variant(None) == "std::monostate" + assert m.load_monostate_variant(1) == "int" + assert m.load_monostate_variant("1") == "std::string" + + assert m.cast_monostate_variant() == (None, 5, "Hello") + + assert ( + doc(m.load_monostate_variant) + == "load_monostate_variant(arg0: Union[None, int, str]) -> str" + ) + + def test_vec_of_reference_wrapper(): """#171: Can't return reference wrappers (or STL structures containing them)""" assert (