@@ -104,6 +104,134 @@ inline std::string replace_newlines_and_squash(const char *text) {
104
104
return result.substr (str_begin, str_range);
105
105
}
106
106
107
+ /* Generate a proper function signature */
108
+ inline std::string generate_function_signature (const char *type_caster_name_field,
109
+ detail::function_record *func_rec,
110
+ const std::type_info *const *types,
111
+ size_t &type_index,
112
+ size_t &arg_index) {
113
+ std::string signature;
114
+ bool is_starred = false ;
115
+ bool is_annotation = func_rec == nullptr ;
116
+ // `is_return_value.top()` is true if we are currently inside the return type of the
117
+ // signature. Using `@^`/`@$` we can force types to be arg/return types while `@!` pops
118
+ // back to the previous state.
119
+ std::stack<bool > is_return_value ({false });
120
+ // The following characters have special meaning in the signature parsing. Literals
121
+ // containing these are escaped with `!`.
122
+ std::string special_chars (" !@%{}-" );
123
+ for (const auto *pc = type_caster_name_field; *pc != ' \0 ' ; ++pc) {
124
+ const auto c = *pc;
125
+ if (c == ' {' ) {
126
+ // Write arg name for everything except *args and **kwargs.
127
+ is_starred = *(pc + 1 ) == ' *' ;
128
+ if (is_starred) {
129
+ continue ;
130
+ }
131
+ // Separator for keyword-only arguments, placed before the kw
132
+ // arguments start (unless we are already putting an *args)
133
+ if (!func_rec->has_args && arg_index == func_rec->nargs_pos ) {
134
+ signature += " *, " ;
135
+ }
136
+ if (arg_index < func_rec->args .size () && func_rec->args [arg_index].name ) {
137
+ signature += func_rec->args [arg_index].name ;
138
+ } else if (arg_index == 0 && func_rec->is_method ) {
139
+ signature += " self" ;
140
+ } else {
141
+ signature += " arg" + std::to_string (arg_index - (func_rec->is_method ? 1 : 0 ));
142
+ }
143
+ signature += " : " ;
144
+ } else if (c == ' }' ) {
145
+ // Write default value if available.
146
+ if (!is_starred && arg_index < func_rec->args .size ()
147
+ && func_rec->args [arg_index].descr ) {
148
+ signature += " = " ;
149
+ signature += detail::replace_newlines_and_squash (func_rec->args [arg_index].descr );
150
+ }
151
+ // Separator for positional-only arguments (placed after the
152
+ // argument, rather than before like *
153
+ if (func_rec->nargs_pos_only > 0 && (arg_index + 1 ) == func_rec->nargs_pos_only ) {
154
+ signature += " , /" ;
155
+ }
156
+ if (!is_starred) {
157
+ arg_index++;
158
+ }
159
+ } else if (c == ' %' ) {
160
+ const std::type_info *t = types[type_index++];
161
+ if (!t) {
162
+ pybind11_fail (" Internal error while parsing type signature (1)" );
163
+ }
164
+ if (auto *tinfo = detail::get_type_info (*t)) {
165
+ handle th ((PyObject *) tinfo->type );
166
+ signature += th.attr (" __module__" ).cast <std::string>() + " ."
167
+ + th.attr (" __qualname__" ).cast <std::string>();
168
+ } else if (func_rec->is_new_style_constructor && arg_index == 0 ) {
169
+ // A new-style `__init__` takes `self` as `value_and_holder`.
170
+ // Rewrite it to the proper class type.
171
+ signature += func_rec->scope .attr (" __module__" ).cast <std::string>() + " ."
172
+ + func_rec->scope .attr (" __qualname__" ).cast <std::string>();
173
+ } else {
174
+ signature += detail::quote_cpp_type_name (detail::clean_type_id (t->name ()));
175
+ }
176
+ } else if (c == ' !' && special_chars.find (*(pc + 1 )) != std::string::npos) {
177
+ // typing::Literal escapes special characters with !
178
+ signature += *++pc;
179
+ } else if (c == ' @' ) {
180
+ // `@^ ... @!` and `@$ ... @!` are used to force arg/return value type (see
181
+ // typing::Callable/detail::arg_descr/detail::return_descr)
182
+ if (*(pc + 1 ) == ' ^' ) {
183
+ is_return_value.emplace (false );
184
+ ++pc;
185
+ continue ;
186
+ }
187
+ if (*(pc + 1 ) == ' $' ) {
188
+ is_return_value.emplace (true );
189
+ ++pc;
190
+ continue ;
191
+ }
192
+ if (*(pc + 1 ) == ' !' ) {
193
+ is_return_value.pop ();
194
+ ++pc;
195
+ continue ;
196
+ }
197
+ // Handle types that differ depending on whether they appear
198
+ // in an argument or a return value position (see io_name<text1, text2>).
199
+ // For named arguments (py::arg()) with noconvert set, return value type is used.
200
+ ++pc;
201
+ if (!is_return_value.top ()
202
+ && (is_annotation
203
+ || !(arg_index < func_rec->args .size ()
204
+ && !func_rec->args [arg_index].convert ))) {
205
+ while (*pc != ' \0 ' && *pc != ' @' ) {
206
+ signature += *pc++;
207
+ }
208
+ if (*pc == ' @' ) {
209
+ ++pc;
210
+ }
211
+ while (*pc != ' \0 ' && *pc != ' @' ) {
212
+ ++pc;
213
+ }
214
+ } else {
215
+ while (*pc != ' \0 ' && *pc != ' @' ) {
216
+ ++pc;
217
+ }
218
+ if (*pc == ' @' ) {
219
+ ++pc;
220
+ }
221
+ while (*pc != ' \0 ' && *pc != ' @' ) {
222
+ signature += *pc++;
223
+ }
224
+ }
225
+ } else {
226
+ if (c == ' -' && *(pc + 1 ) == ' >' ) {
227
+ is_return_value.emplace (true );
228
+ }
229
+ signature += c;
230
+ }
231
+ }
232
+ return signature;
233
+ }
234
+
107
235
#if defined(_MSC_VER)
108
236
# define PYBIND11_COMPAT_STRDUP _strdup
109
237
#else
@@ -439,124 +567,9 @@ class cpp_function : public function {
439
567
}
440
568
#endif
441
569
442
- /* Generate a proper function signature */
443
- std::string signature;
444
570
size_t type_index = 0 , arg_index = 0 ;
445
- bool is_starred = false ;
446
- // `is_return_value.top()` is true if we are currently inside the return type of the
447
- // signature. Using `@^`/`@$` we can force types to be arg/return types while `@!` pops
448
- // back to the previous state.
449
- std::stack<bool > is_return_value ({false });
450
- // The following characters have special meaning in the signature parsing. Literals
451
- // containing these are escaped with `!`.
452
- std::string special_chars (" !@%{}-" );
453
- for (const auto *pc = text; *pc != ' \0 ' ; ++pc) {
454
- const auto c = *pc;
455
-
456
- if (c == ' {' ) {
457
- // Write arg name for everything except *args and **kwargs.
458
- is_starred = *(pc + 1 ) == ' *' ;
459
- if (is_starred) {
460
- continue ;
461
- }
462
- // Separator for keyword-only arguments, placed before the kw
463
- // arguments start (unless we are already putting an *args)
464
- if (!rec->has_args && arg_index == rec->nargs_pos ) {
465
- signature += " *, " ;
466
- }
467
- if (arg_index < rec->args .size () && rec->args [arg_index].name ) {
468
- signature += rec->args [arg_index].name ;
469
- } else if (arg_index == 0 && rec->is_method ) {
470
- signature += " self" ;
471
- } else {
472
- signature += " arg" + std::to_string (arg_index - (rec->is_method ? 1 : 0 ));
473
- }
474
- signature += " : " ;
475
- } else if (c == ' }' ) {
476
- // Write default value if available.
477
- if (!is_starred && arg_index < rec->args .size () && rec->args [arg_index].descr ) {
478
- signature += " = " ;
479
- signature += detail::replace_newlines_and_squash (rec->args [arg_index].descr );
480
- }
481
- // Separator for positional-only arguments (placed after the
482
- // argument, rather than before like *
483
- if (rec->nargs_pos_only > 0 && (arg_index + 1 ) == rec->nargs_pos_only ) {
484
- signature += " , /" ;
485
- }
486
- if (!is_starred) {
487
- arg_index++;
488
- }
489
- } else if (c == ' %' ) {
490
- const std::type_info *t = types[type_index++];
491
- if (!t) {
492
- pybind11_fail (" Internal error while parsing type signature (1)" );
493
- }
494
- if (auto *tinfo = detail::get_type_info (*t)) {
495
- handle th ((PyObject *) tinfo->type );
496
- signature += th.attr (" __module__" ).cast <std::string>() + " ."
497
- + th.attr (" __qualname__" ).cast <std::string>();
498
- } else if (rec->is_new_style_constructor && arg_index == 0 ) {
499
- // A new-style `__init__` takes `self` as `value_and_holder`.
500
- // Rewrite it to the proper class type.
501
- signature += rec->scope .attr (" __module__" ).cast <std::string>() + " ."
502
- + rec->scope .attr (" __qualname__" ).cast <std::string>();
503
- } else {
504
- signature += detail::quote_cpp_type_name (detail::clean_type_id (t->name ()));
505
- }
506
- } else if (c == ' !' && special_chars.find (*(pc + 1 )) != std::string::npos) {
507
- // typing::Literal escapes special characters with !
508
- signature += *++pc;
509
- } else if (c == ' @' ) {
510
- // `@^ ... @!` and `@$ ... @!` are used to force arg/return value type (see
511
- // typing::Callable/detail::arg_descr/detail::return_descr)
512
- if (*(pc + 1 ) == ' ^' ) {
513
- is_return_value.emplace (false );
514
- ++pc;
515
- continue ;
516
- }
517
- if (*(pc + 1 ) == ' $' ) {
518
- is_return_value.emplace (true );
519
- ++pc;
520
- continue ;
521
- }
522
- if (*(pc + 1 ) == ' !' ) {
523
- is_return_value.pop ();
524
- ++pc;
525
- continue ;
526
- }
527
- // Handle types that differ depending on whether they appear
528
- // in an argument or a return value position (see io_name<text1, text2>).
529
- // For named arguments (py::arg()) with noconvert set, return value type is used.
530
- ++pc;
531
- if (!is_return_value.top ()
532
- && !(arg_index < rec->args .size () && !rec->args [arg_index].convert )) {
533
- while (*pc != ' \0 ' && *pc != ' @' ) {
534
- signature += *pc++;
535
- }
536
- if (*pc == ' @' ) {
537
- ++pc;
538
- }
539
- while (*pc != ' \0 ' && *pc != ' @' ) {
540
- ++pc;
541
- }
542
- } else {
543
- while (*pc != ' \0 ' && *pc != ' @' ) {
544
- ++pc;
545
- }
546
- if (*pc == ' @' ) {
547
- ++pc;
548
- }
549
- while (*pc != ' \0 ' && *pc != ' @' ) {
550
- signature += *pc++;
551
- }
552
- }
553
- } else {
554
- if (c == ' -' && *(pc + 1 ) == ' >' ) {
555
- is_return_value.emplace (true );
556
- }
557
- signature += c;
558
- }
559
- }
571
+ std::string signature
572
+ = detail::generate_function_signature (text, rec, types, type_index, arg_index);
560
573
561
574
if (arg_index != args - rec->has_args - rec->has_kwargs || types[type_index] != nullptr ) {
562
575
pybind11_fail (" Internal error while parsing type signature (2)" );
0 commit comments