Skip to content

Commit 1121f25

Browse files
rwgksizmailovpre-commit-ci[bot]henryiiilpapp-foundry
authored
git merge smart_holder (#30066)
* fix: Use lowercase builtin collection names (pybind#4833) * Update render for buffer sequence and handle (pybind#4831) * fix: Add capitalize render name of `py::buffer` and `py::sequence` * fix: Render `py::handle` same way as `py::object` * tests: Fix tests `handle` -> `object` * tests: Test capitaliation of `py::sequence` and `py::buffer` * style: pre-commit fixes * fix: Render `py::object` as `Any` * Revert "fix: Render `py::object` as `Any`" This reverts commit 7861dcf. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ralf W. Grosse-Kunstleve <[email protected]> * fix: Missing typed variants of `iterator` and `iterable` (pybind#4832) * Fix small bug introduced with PR pybind#4735 (pybind#4845) * Bug fix: `result[0]` called if `result.empty()` * Add unit test that fails without the fix. * fix(cmake): correctly detect FindPython policy and better warning (pybind#4806) * fix(cmake): support DEBUG_POSTFIX correctly (pybind#4761) * cmake: split extension Into suffix and debug postfix. Pybind11 is currently treating both as suffix, which is problematic when something else defines the DEBUG_POSTFIX because they will be concatenated. pybind11_extension sets SUFFIX to _d.something and if DEBUG_POSTFIX is set to _d. _d + _d.something = _d_d.something The issue has been reported at: pybind#4699 * style: pre-commit fixes * fix(cmake): support postfix for old FindPythonInterp mode too Signed-off-by: Henry Schreiner <[email protected]> --------- Signed-off-by: Henry Schreiner <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Henry Schreiner <[email protected]> * Avoid copy in iteration by using const auto & (pybind#4861) This change is fixing a Coverity AUTO_CAUSES_COPY issues. * Add 2 missing `throw error_already_set();` (pybind#4863) Fixes oversights in PR pybind#4570. * MAINT: Include `numpy._core` imports (pybind#4857) * MAINT: Include numpy._core imports * style: pre-commit fixes * Apply review comments * style: pre-commit fixes * Add no-inline attribute * Select submodule name based on numpy version * style: pre-commit fixes * Update pre-commit check * Add error_already_set and simplify if statement * Update .pre-commit-config.yaml Co-authored-by: Ralf W. Grosse-Kunstleve <[email protected]> --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ralf W. Grosse-Kunstleve <[email protected]> * MAINT: Remove np.int_ (pybind#4867) * chore(deps): update pre-commit hooks (pybind#4868) * chore(deps): update pre-commit hooks updates: - [github.com/psf/black-pre-commit-mirror: 23.7.0 → 23.9.1](psf/black-pre-commit-mirror@23.7.0...23.9.1) - [github.com/astral-sh/ruff-pre-commit: v0.0.287 → v0.0.292](astral-sh/ruff-pre-commit@v0.0.287...v0.0.292) - [github.com/codespell-project/codespell: v2.2.5 → v2.2.6](codespell-project/codespell@v2.2.5...v2.2.6) - [github.com/shellcheck-py/shellcheck-py: v0.9.0.5 → v0.9.0.6](shellcheck-py/shellcheck-py@v0.9.0.5...v0.9.0.6) - [github.com/PyCQA/pylint: v3.0.0a7 → v3.0.0](pylint-dev/pylint@v3.0.0a7...v3.0.0) * Update .pre-commit-config.yaml * style: pre-commit fixes * Update .pre-commit-config.yaml --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Henry Schreiner <[email protected]> --------- Signed-off-by: Henry Schreiner <[email protected]> Co-authored-by: Sergei Izmailov <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Henry Schreiner <[email protected]> Co-authored-by: László Papp <[email protected]> Co-authored-by: Oleksandr Pavlyk <[email protected]> Co-authored-by: Mateusz Sokół <[email protected]>
1 parent f0fd911 commit 1121f25

25 files changed

+194
-57
lines changed

.github/CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ The valid options are:
135135
* Use `-G` and the name of a generator to use something different. `cmake
136136
--help` lists the generators available.
137137
- On Unix, setting `CMAKE_GENERATER=Ninja` in your environment will give
138-
you automatic mulithreading on all your CMake projects!
138+
you automatic multithreading on all your CMake projects!
139139
* Open the `CMakeLists.txt` with QtCreator to generate for that IDE.
140140
* You can use `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate the `.json` file
141141
that some tools expect.

.pre-commit-config.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ repos:
3232

3333
# Black, the code formatter, natively supports pre-commit
3434
- repo: https://github.com/psf/black-pre-commit-mirror
35-
rev: "23.7.0" # Keep in sync with blacken-docs
35+
rev: "23.9.1" # Keep in sync with blacken-docs
3636
hooks:
3737
- id: black
3838

3939
# Ruff, the Python auto-correcting linter written in Rust
4040
- repo: https://github.com/astral-sh/ruff-pre-commit
41-
rev: v0.0.287
41+
rev: v0.0.292
4242
hooks:
4343
- id: ruff
4444
args: ["--fix", "--show-fixes"]
@@ -126,15 +126,15 @@ repos:
126126
# Use tools/codespell_ignore_lines_from_errors.py
127127
# to rebuild .codespell-ignore-lines
128128
- repo: https://github.com/codespell-project/codespell
129-
rev: "v2.2.5"
129+
rev: "v2.2.6"
130130
hooks:
131131
- id: codespell
132132
exclude: ".supp$"
133133
args: ["-x.codespell-ignore-lines", "-Lccompiler"]
134134

135135
# Check for common shell mistakes
136136
- repo: https://github.com/shellcheck-py/shellcheck-py
137-
rev: "v0.9.0.5"
137+
rev: "v0.9.0.6"
138138
hooks:
139139
- id: shellcheck
140140

@@ -144,12 +144,12 @@ repos:
144144
- id: disallow-caps
145145
name: Disallow improper capitalization
146146
language: pygrep
147-
entry: PyBind|Numpy|Cmake|CCache|PyTest
147+
entry: PyBind|\bNumpy\b|Cmake|CCache|PyTest
148148
exclude: ^\.pre-commit-config.yaml$
149149

150150
# PyLint has native support - not always usable, but works for us
151151
- repo: https://github.com/PyCQA/pylint
152-
rev: "v3.0.0a7"
152+
rev: "v3.0.0"
153153
hooks:
154154
- id: pylint
155155
files: ^pybind11

CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
# All rights reserved. Use of this source code is governed by a
66
# BSD-style license that can be found in the LICENSE file.
77

8+
# Propagate this policy (FindPythonInterp removal) so it can be detected later
9+
if(NOT CMAKE_VERSION VERSION_LESS "3.27")
10+
cmake_policy(GET CMP0148 _pybind11_cmp0148)
11+
endif()
12+
813
cmake_minimum_required(VERSION 3.5)
914

1015
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
@@ -16,6 +21,11 @@ else()
1621
cmake_policy(VERSION 3.26)
1722
endif()
1823

24+
if(_pybind11_cmp0148)
25+
cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
26+
unset(_pybind11_cmp0148)
27+
endif()
28+
1929
# Avoid infinite recursion if tests include this as a subdirectory
2030
if(DEFINED PYBIND11_MASTER_PROJECT)
2131
return()

include/pybind11/cast.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ class tuple_caster {
817817
}
818818

819819
static constexpr auto name
820-
= const_name("Tuple[") + concat(make_caster<Ts>::name...) + const_name("]");
820+
= const_name("tuple[") + concat(make_caster<Ts>::name...) + const_name("]");
821821

822822
template <typename T>
823823
using cast_op_type = type;
@@ -1044,6 +1044,10 @@ struct handle_type_name<bytes> {
10441044
static constexpr auto name = const_name(PYBIND11_BYTES_NAME);
10451045
};
10461046
template <>
1047+
struct handle_type_name<buffer> {
1048+
static constexpr auto name = const_name("Buffer");
1049+
};
1050+
template <>
10471051
struct handle_type_name<int_> {
10481052
static constexpr auto name = const_name("int");
10491053
};
@@ -1064,10 +1068,18 @@ struct handle_type_name<function> {
10641068
static constexpr auto name = const_name("Callable");
10651069
};
10661070
template <>
1071+
struct handle_type_name<handle> {
1072+
static constexpr auto name = handle_type_name<object>::name;
1073+
};
1074+
template <>
10671075
struct handle_type_name<none> {
10681076
static constexpr auto name = const_name("None");
10691077
};
10701078
template <>
1079+
struct handle_type_name<sequence> {
1080+
static constexpr auto name = const_name("Sequence");
1081+
};
1082+
template <>
10711083
struct handle_type_name<args> {
10721084
static constexpr auto name = const_name("*args");
10731085
};

include/pybind11/detail/cross_extension_shared_state.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ inline object get_python_state_dict() {
3636
#endif
3737
if (!state_dict) {
3838
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
39+
throw error_already_set();
3940
}
4041
return state_dict;
4142
}
@@ -95,6 +96,7 @@ struct cross_extension_shared_state {
9596
" Retrieve payload_type** from capsule FAILED for ABI ID \""
9697
+ std::string(AdapterType::abi_id()) + "\"")
9798
.c_str());
99+
throw error_already_set();
98100
}
99101
payload_pp() = static_cast<payload_type **>(raw_ptr);
100102
return *payload_pp();

include/pybind11/detail/init.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ constexpr bool is_alias(void *) {
6969
}
7070

7171
// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
72-
// back to brace aggregate initiailization so that for aggregate initialization can be used with
72+
// back to brace aggregate initialization so that for aggregate initialization can be used with
7373
// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
7474
// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
7575
// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).

include/pybind11/detail/internals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ inline internals **get_internals_pp_from_capsule(handle obj) {
324324
void *raw_ptr = PyCapsule_GetPointer(obj.ptr(), /*name=*/nullptr);
325325
if (raw_ptr == nullptr) {
326326
raise_from(PyExc_SystemError, "pybind11::detail::get_internals_pp_from_capsule() FAILED");
327+
throw error_already_set();
327328
}
328329
return static_cast<internals **>(raw_ptr);
329330
}

include/pybind11/detail/native_enum_data.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ inline void native_enum_add_to_parent(object parent, const detail::native_enum_d
9898
if (!enum_module) {
9999
raise_from(PyExc_SystemError,
100100
"`import enum` FAILED at " __FILE__ ":" PYBIND11_TOSTRING(__LINE__));
101+
throw error_already_set();
101102
}
102103
auto py_enum_type = enum_module.attr(data.use_int_enum ? "IntEnum" : "Enum");
103104
auto py_enum = py_enum_type(data.enum_name, data.members);

include/pybind11/numpy.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,20 @@ inline numpy_internals &get_numpy_internals() {
120120
return *ptr;
121121
}
122122

123+
PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) {
124+
module_ numpy = module_::import("numpy");
125+
str version_string = numpy.attr("__version__");
126+
127+
module_ numpy_lib = module_::import("numpy.lib");
128+
object numpy_version = numpy_lib.attr("NumpyVersion")(version_string);
129+
int major_version = numpy_version.attr("major").cast<int>();
130+
131+
/* `numpy.core` was renamed to `numpy._core` in NumPy 2.0 as it officially
132+
became a private module. */
133+
std::string numpy_core_path = major_version >= 2 ? "numpy._core" : "numpy.core";
134+
return module_::import((numpy_core_path + "." + submodule_name).c_str());
135+
}
136+
123137
template <typename T>
124138
struct same_size {
125139
template <typename U>
@@ -263,9 +277,13 @@ struct npy_api {
263277
};
264278

265279
static npy_api lookup() {
266-
module_ m = module_::import("numpy.core.multiarray");
280+
module_ m = detail::import_numpy_core_submodule("multiarray");
267281
auto c = m.attr("_ARRAY_API");
268282
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
283+
if (api_ptr == nullptr) {
284+
raise_from(PyExc_SystemError, "FAILURE obtaining numpy _ARRAY_API pointer.");
285+
throw error_already_set();
286+
}
269287
npy_api api;
270288
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
271289
DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
@@ -626,11 +644,8 @@ class dtype : public object {
626644

627645
private:
628646
static object _dtype_from_pep3118() {
629-
static PyObject *obj = module_::import("numpy.core._internal")
630-
.attr("_dtype_from_pep3118")
631-
.cast<object>()
632-
.release()
633-
.ptr();
647+
module_ m = detail::import_numpy_core_submodule("_internal");
648+
static PyObject *obj = m.attr("_dtype_from_pep3118").cast<object>().release().ptr();
634649
return reinterpret_borrow<object>(obj);
635650
}
636651

include/pybind11/pybind11.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ inline std::string replace_newlines_and_squash(const char *text) {
5959
std::string result(text);
6060
bool previous_is_whitespace = false;
6161

62-
// Do not modify string representations
63-
char first_char = result[0];
64-
char last_char = result[result.size() - 1];
65-
if (first_char == last_char && first_char == '\'') {
66-
return result;
62+
if (result.size() >= 2) {
63+
// Do not modify string representations
64+
char first_char = result[0];
65+
char last_char = result[result.size() - 1];
66+
if (first_char == last_char && first_char == '\'') {
67+
return result;
68+
}
6769
}
6870
result.clear();
6971

@@ -1146,7 +1148,7 @@ class cpp_function : public function {
11461148
}
11471149
msg += "kwargs: ";
11481150
bool first = true;
1149-
for (auto kwarg : kwargs) {
1151+
for (const auto &kwarg : kwargs) {
11501152
if (first) {
11511153
first = false;
11521154
} else {

include/pybind11/stl.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ while pybind11 was very far on the strict side. Originally PyCLIF accepted any
5050
Python iterable as input for a C++ `vector`/`set`/`map` argument, as long as
5151
the elements were convertible. The obvious (in hindsight) problem was that
5252
any empty Python iterable could be passed to any of these C++ types, e.g. `{}`
53-
was accpeted for C++ `vector`/`set` arguments, or `[]` for C++ `map` arguments.
53+
was accepted for C++ `vector`/`set` arguments, or `[]` for C++ `map` arguments.
5454
5555
The functions below strike a practical permissive-vs-strict compromise,
5656
informed by tens of thousands of use cases in the wild. A main objective is
@@ -202,7 +202,7 @@ struct set_caster {
202202
return s.release();
203203
}
204204

205-
PYBIND11_TYPE_CASTER_RVPP(type, const_name("Set[") + key_conv::name + const_name("]"));
205+
PYBIND11_TYPE_CASTER_RVPP(type, const_name("set[") + key_conv::name + const_name("]"));
206206
};
207207

208208
template <typename Type, typename Key, typename Value>
@@ -273,7 +273,7 @@ struct map_caster {
273273
}
274274

275275
PYBIND11_TYPE_CASTER_RVPP(Type,
276-
const_name("Dict[") + key_conv::name + const_name(", ")
276+
const_name("dict[") + key_conv::name + const_name(", ")
277277
+ value_conv::name + const_name("]"));
278278
};
279279

@@ -340,7 +340,7 @@ struct list_caster {
340340
return l.release();
341341
}
342342

343-
PYBIND11_TYPE_CASTER_RVPP(Type, const_name("List[") + value_conv::name + const_name("]"));
343+
PYBIND11_TYPE_CASTER_RVPP(Type, const_name("list[") + value_conv::name + const_name("]"));
344344
};
345345

346346
template <typename Type, typename Alloc>
@@ -422,7 +422,7 @@ struct array_caster {
422422

423423
PYBIND11_TYPE_CASTER_RVPP(ArrayType,
424424
const_name<Resizable>(const_name(""), const_name("Annotated["))
425-
+ const_name("List[") + value_conv::name + const_name("]")
425+
+ const_name("list[") + value_conv::name + const_name("]")
426426
+ const_name<Resizable>(const_name(""),
427427
const_name(", FixedSize(")
428428
+ const_name<Size>()

include/pybind11/typing.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ class Set : public set {
4545
using set::set;
4646
};
4747

48+
template <typename T>
49+
class Iterable : public iterable {
50+
using iterable::iterable;
51+
};
52+
53+
template <typename T>
54+
class Iterator : public iterator {
55+
using iterator::iterator;
56+
};
57+
4858
template <typename Signature>
4959
class Callable;
5060

@@ -85,6 +95,16 @@ struct handle_type_name<typing::Set<T>> {
8595
static constexpr auto name = const_name("set[") + make_caster<T>::name + const_name("]");
8696
};
8797

98+
template <typename T>
99+
struct handle_type_name<typing::Iterable<T>> {
100+
static constexpr auto name = const_name("Iterable[") + make_caster<T>::name + const_name("]");
101+
};
102+
103+
template <typename T>
104+
struct handle_type_name<typing::Iterator<T>> {
105+
static constexpr auto name = const_name("Iterator[") + make_caster<T>::name + const_name("]");
106+
};
107+
88108
template <typename Return, typename... Args>
89109
struct handle_type_name<typing::Callable<Return(Args...)>> {
90110
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;

tests/test_buffers.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,10 @@ def test_ctypes_from_buffer():
219219
assert cinfo.shape == pyinfo.shape
220220
assert cinfo.strides == pyinfo.strides
221221
assert not cinfo.readonly
222+
223+
224+
def test_buffer_docstring():
225+
assert (
226+
m.get_buffer_info.__doc__.strip()
227+
== "get_buffer_info(arg0: Buffer) -> pybind11_tests.buffers.buffer_info"
228+
)

tests/test_builtin_casters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,15 +352,15 @@ def test_tuple(doc):
352352
assert (
353353
doc(m.pair_passthrough)
354354
== """
355-
pair_passthrough(arg0: Tuple[bool, str]) -> Tuple[str, bool]
355+
pair_passthrough(arg0: tuple[bool, str]) -> tuple[str, bool]
356356
357357
Return a pair in reversed order
358358
"""
359359
)
360360
assert (
361361
doc(m.tuple_passthrough)
362362
== """
363-
tuple_passthrough(arg0: Tuple[bool, str, int]) -> Tuple[int, str, bool]
363+
tuple_passthrough(arg0: tuple[bool, str, int]) -> tuple[int, str, bool]
364364
365365
Return a triple in reversed order
366366
"""

tests/test_factory_constructors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def test_init_factory_signature(msg):
7777
1. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int)
7878
2. m.factory_constructors.TestFactory1(arg0: str)
7979
3. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.pointer_tag)
80-
4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle)
80+
4. m.factory_constructors.TestFactory1(arg0: object, arg1: int, arg2: object)
8181
8282
Invoked with: 'invalid', 'constructor', 'arguments'
8383
"""
@@ -95,7 +95,7 @@ def test_init_factory_signature(msg):
9595
9696
3. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None
9797
98-
4. __init__(self: m.factory_constructors.TestFactory1, arg0: handle, arg1: int, arg2: handle) -> None
98+
4. __init__(self: m.factory_constructors.TestFactory1, arg0: object, arg1: int, arg2: object) -> None
9999
"""
100100
)
101101

tests/test_kwargs_and_defaults.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
8585
"kw_lb_func7",
8686
[](const std::string &) {},
8787
py::arg("str_arg") = "First line.\n Second line.");
88+
m.def(
89+
"kw_lb_func8", [](const CustomRepr &) {}, py::arg("custom") = CustomRepr(""));
8890

8991
// test_args_and_kwargs
9092
m.def("args_function", [](py::args args) -> py::tuple {

tests/test_kwargs_and_defaults.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def test_function_signatures(doc):
88
assert doc(m.kw_func1) == "kw_func1(x: int, y: int) -> str"
99
assert doc(m.kw_func2) == "kw_func2(x: int = 100, y: int = 200) -> str"
1010
assert doc(m.kw_func3) == "kw_func3(data: str = 'Hello world!') -> None"
11-
assert doc(m.kw_func4) == "kw_func4(myList: List[int] = [13, 17]) -> str"
11+
assert doc(m.kw_func4) == "kw_func4(myList: list[int] = [13, 17]) -> str"
1212
assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int = 300) -> str"
1313
assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int = 0) -> str"
1414
assert doc(m.args_function) == "args_function(*args) -> tuple"
@@ -55,6 +55,10 @@ def test_function_signatures(doc):
5555
doc(m.kw_lb_func7)
5656
== "kw_lb_func7(str_arg: str = 'First line.\\n Second line.') -> None"
5757
)
58+
assert (
59+
doc(m.kw_lb_func8)
60+
== "kw_lb_func8(custom: m.kwargs_and_defaults.CustomRepr = ) -> None"
61+
)
5862

5963

6064
def test_named_arguments():

0 commit comments

Comments
 (0)