Merging 'master' into 'wrap'
						commit
						2bfd480a0e
					
				|  | @ -299,7 +299,7 @@ class Class: | |||
|             # If the base class is a TemplatedType, | ||||
|             # we want the instantiated Typename | ||||
|             if isinstance(parent_class, TemplatedType): | ||||
|                 parent_class = parent_class.typename  # type: ignore | ||||
|                 pass  # Note: this must get handled in InstantiatedClass | ||||
| 
 | ||||
|             self.parent_class = parent_class | ||||
|         else: | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ Code to use the parsed results and convert it to a format | |||
| that Matlab's MEX compiler can use. | ||||
| """ | ||||
| 
 | ||||
| # pylint: disable=too-many-lines, no-self-use, too-many-arguments, too-many-branches, too-many-statements | ||||
| # pylint: disable=too-many-lines, no-self-use, too-many-arguments, too-many-branches, too-many-statements, consider-using-f-string, unspecified-encoding | ||||
| 
 | ||||
| import copy | ||||
| import os | ||||
|  | @ -17,6 +17,7 @@ import gtwrap.template_instantiator as instantiator | |||
| from gtwrap.interface_parser.function import ArgumentList | ||||
| from gtwrap.matlab_wrapper.mixins import CheckMixin, FormatMixin | ||||
| from gtwrap.matlab_wrapper.templates import WrapperTemplate | ||||
| from gtwrap.template_instantiator.classes import InstantiatedClass | ||||
| 
 | ||||
| 
 | ||||
| class MatlabWrapper(CheckMixin, FormatMixin): | ||||
|  | @ -28,6 +29,7 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|         top_module_namespace: C++ namespace for the top module (default '') | ||||
|         ignore_classes: A list of classes to ignore (default []) | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, | ||||
|                  module_name, | ||||
|                  top_module_namespace='', | ||||
|  | @ -94,16 +96,19 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|             self.classes_elems[instantiated_class] = 0 | ||||
|             self.classes.append(instantiated_class) | ||||
| 
 | ||||
|     def _update_wrapper_id(self, collector_function=None, id_diff=0): | ||||
|     def _update_wrapper_id(self, | ||||
|                            collector_function=None, | ||||
|                            id_diff=0, | ||||
|                            function_name: str = None): | ||||
|         """ | ||||
|         Get and define wrapper ids. | ||||
|         Generates the map of id -> collector function. | ||||
| 
 | ||||
|         Args: | ||||
|             collector_function: tuple storing info about the wrapper function | ||||
|                 (namespace, class instance, function type, function name, | ||||
|                 extra) | ||||
|                 (namespace, class instance, function name, function object) | ||||
|             id_diff: constant to add to the id in the map | ||||
|             function_name: Optional custom function_name. | ||||
| 
 | ||||
|         Returns: | ||||
|             the current wrapper id | ||||
|  | @ -112,6 +117,7 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|             is_instantiated_class = isinstance(collector_function[1], | ||||
|                                                instantiator.InstantiatedClass) | ||||
| 
 | ||||
|             if function_name is None: | ||||
|                 if is_instantiated_class: | ||||
|                     function_name = collector_function[0] + \ | ||||
|                                     collector_function[1].name + '_' + collector_function[2] | ||||
|  | @ -145,6 +151,7 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|         We create "overload" functions with fewer arguments, but since we have to "remember" what | ||||
|         the default arguments are for later, we make a backup. | ||||
|         """ | ||||
| 
 | ||||
|         def args_copy(args): | ||||
|             return ArgumentList([copy.copy(arg) for arg in args.list()]) | ||||
| 
 | ||||
|  | @ -332,19 +339,8 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|         return check_statement | ||||
| 
 | ||||
|     def _wrapper_unwrap_arguments(self, args, arg_id=0, constructor=False): | ||||
|         """Format the interface_parser.Arguments. | ||||
| 
 | ||||
|         Examples: | ||||
|             ((a), unsigned char a = unwrap< unsigned char >(in[1]);), | ||||
|             ((a), Test& t = *unwrap_shared_ptr< Test >(in[1], "ptr_Test");), | ||||
|             ((a), std::shared_ptr<Test> p1 = unwrap_shared_ptr< Test >(in[1], "ptr_Test");) | ||||
|         """ | ||||
|         body_args = '' | ||||
| 
 | ||||
|         for arg in args.list(): | ||||
|             ctype_camel = self._format_type_name(arg.ctype.typename, | ||||
|                                                  separator='') | ||||
|     def _unwrap_argument(self, arg, arg_id=0, constructor=False): | ||||
|         ctype_camel = self._format_type_name(arg.ctype.typename, separator='') | ||||
|         ctype_sep = self._format_type_name(arg.ctype.typename) | ||||
| 
 | ||||
|         if self.is_ref(arg.ctype):  # and not constructor: | ||||
|  | @ -361,7 +357,6 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|         elif (self.is_shared_ptr(arg.ctype) or self.can_be_pointer(arg.ctype)) and \ | ||||
|                 arg.ctype.typename.name not in self.ignore_namespace: | ||||
|                 call_type = arg.ctype.is_shared_ptr | ||||
| 
 | ||||
|             arg_type = "{std_boost}::shared_ptr<{ctype_sep}>".format( | ||||
|                 std_boost='boost' if constructor else 'boost', | ||||
|  | @ -374,6 +369,21 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|             unwrap = 'unwrap< {ctype} >(in[{id}]);'.format( | ||||
|                 ctype=arg.ctype.typename.name, id=arg_id) | ||||
| 
 | ||||
|         return arg_type, unwrap | ||||
| 
 | ||||
|     def _wrapper_unwrap_arguments(self, args, arg_id=0, constructor=False): | ||||
|         """Format the interface_parser.Arguments. | ||||
| 
 | ||||
|         Examples: | ||||
|             ((a), unsigned char a = unwrap< unsigned char >(in[1]);), | ||||
|             ((a), Test& t = *unwrap_shared_ptr< Test >(in[1], "ptr_Test");), | ||||
|             ((a), std::shared_ptr<Test> p1 = unwrap_shared_ptr< Test >(in[1], "ptr_Test");) | ||||
|         """ | ||||
|         body_args = '' | ||||
| 
 | ||||
|         for arg in args.list(): | ||||
|             arg_type, unwrap = self._unwrap_argument(arg, arg_id, constructor) | ||||
| 
 | ||||
|             body_args += textwrap.indent(textwrap.dedent('''\ | ||||
|                     {arg_type} {name} = {unwrap} | ||||
|                     '''.format(arg_type=arg_type, name=arg.name, | ||||
|  | @ -452,6 +462,7 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|         """ | ||||
|         class_name = instantiated_class.name | ||||
|         ctors = instantiated_class.ctors | ||||
|         properties = instantiated_class.properties | ||||
|         methods = instantiated_class.methods | ||||
|         static_methods = instantiated_class.static_methods | ||||
| 
 | ||||
|  | @ -469,6 +480,12 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|                                                        args=self._wrap_args( | ||||
|                                                            ctor.args)) | ||||
| 
 | ||||
|         if len(properties) != 0: | ||||
|             comment += '%\n' \ | ||||
|                        '%-------Properties-------\n' | ||||
|             for property in properties: | ||||
|                 comment += '%{}\n'.format(property.name) | ||||
| 
 | ||||
|         if len(methods) != 0: | ||||
|             comment += '%\n' \ | ||||
|                        '%-------Methods-------\n' | ||||
|  | @ -578,12 +595,13 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|         param_wrap += textwrap.indent(textwrap.dedent('''\ | ||||
|             else | ||||
|               error('Arguments do not match any overload of function {func_name}'); | ||||
|         ''').format(func_name=function_name), | ||||
|             end''').format(func_name=function_name), | ||||
|                                       prefix='      ') | ||||
| 
 | ||||
|         global_function = textwrap.indent(textwrap.dedent('''\ | ||||
|             function varargout = {m_method}(varargin) | ||||
|             {statements}      end | ||||
|             {statements} | ||||
|             end | ||||
|         ''').format(m_method=function_name, statements=param_wrap), | ||||
|                                           prefix='') | ||||
| 
 | ||||
|  | @ -693,13 +711,82 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|         return methods_wrap | ||||
| 
 | ||||
|     def wrap_class_properties(self, class_name): | ||||
|         """Generate properties of class.""" | ||||
|         return textwrap.dedent('''\ | ||||
|     def wrap_properties_block(self, class_name, inst_class): | ||||
|         """Generate Matlab properties block of the class. | ||||
| 
 | ||||
|         E.g. | ||||
|         ``` | ||||
|         properties | ||||
|               ptr_{} = 0 | ||||
|             ptr_gtsamISAM2Params = 0 | ||||
|             relinearizeSkip | ||||
|         end | ||||
|         ''').format(class_name) | ||||
|         ``` | ||||
| 
 | ||||
|         Args: | ||||
|             class_name: Class name with namespace to assign unique pointer. | ||||
|             inst_class: The instantiated class whose properties we want to wrap. | ||||
| 
 | ||||
|         Returns: | ||||
|             str: The `properties` block in a Matlab `classdef`. | ||||
|         """ | ||||
|         # Get the property names and make into newline separated block | ||||
|         class_pointer = "  ptr_{class_name} = 0".format(class_name=class_name) | ||||
| 
 | ||||
|         if len(inst_class.properties) > 0: | ||||
|             properties = '\n' + "".join( | ||||
|                 ["  {}".format(p.name) for p in inst_class.properties]) | ||||
|         else: | ||||
|             properties = '' | ||||
| 
 | ||||
|         properties = class_pointer + properties | ||||
|         properties_block = textwrap.dedent('''\ | ||||
|             properties | ||||
|             {properties} | ||||
|             end | ||||
|         ''').format(properties=properties) | ||||
|         return properties_block | ||||
| 
 | ||||
|     def wrap_class_properties(self, namespace_name: str, | ||||
|                               inst_class: InstantiatedClass): | ||||
|         """Generate wrappers for the setters & getters of class properties. | ||||
| 
 | ||||
|         Args: | ||||
|             inst_class: The instantiated class whose properties we wish to wrap. | ||||
|         """ | ||||
|         properties = [] | ||||
|         for property in inst_class.properties: | ||||
|             # These are the setters and getters in the .m file | ||||
|             function_name = namespace_name + inst_class.name + '_get_' + property.name | ||||
|             getter = """ | ||||
|             function varargout = get.{name}(this) | ||||
|                 {varargout} = {wrapper}({num}, this); | ||||
|                 this.{name} = {varargout}; | ||||
|             end | ||||
|             """.format( | ||||
|                 name=property.name, | ||||
|                 varargout='varargout{1}', | ||||
|                 wrapper=self._wrapper_name(), | ||||
|                 num=self._update_wrapper_id( | ||||
|                     (namespace_name, inst_class, property.name, property), | ||||
|                     function_name=function_name)) | ||||
|             properties.append(getter) | ||||
| 
 | ||||
|             # Setter doesn't need varargin since it needs just one input. | ||||
|             function_name = namespace_name + inst_class.name + '_set_' + property.name | ||||
|             setter = """ | ||||
|             function set.{name}(this, value) | ||||
|                 obj.{name} = value; | ||||
|                 {wrapper}({num}, this, value); | ||||
|             end | ||||
|             """.format( | ||||
|                 name=property.name, | ||||
|                 wrapper=self._wrapper_name(), | ||||
|                 num=self._update_wrapper_id( | ||||
|                     (namespace_name, inst_class, property.name, property), | ||||
|                     function_name=function_name)) | ||||
|             properties.append(setter) | ||||
| 
 | ||||
|         return properties | ||||
| 
 | ||||
|     def wrap_class_deconstructor(self, namespace_name, inst_class): | ||||
|         """Generate the delete function for the Matlab class.""" | ||||
|  | @ -921,7 +1008,9 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|         return method_text | ||||
| 
 | ||||
|     def wrap_instantiated_class(self, instantiated_class, namespace_name=''): | ||||
|     def wrap_instantiated_class(self, | ||||
|                                 instantiated_class, | ||||
|                                 namespace_name: str = ''): | ||||
|         """Generate comments and code for given class. | ||||
| 
 | ||||
|         Args: | ||||
|  | @ -955,8 +1044,8 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|         # Class properties | ||||
|         content_text += '  ' + reduce( | ||||
|             self._insert_spaces, | ||||
|             self.wrap_class_properties( | ||||
|                 namespace_file_name).splitlines()) + '\n' | ||||
|             self.wrap_properties_block(namespace_file_name, | ||||
|                                        instantiated_class).splitlines()) + '\n' | ||||
| 
 | ||||
|         # Class constructor | ||||
|         content_text += '  ' + reduce( | ||||
|  | @ -996,15 +1085,26 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|                     lambda x, y: x + '\n' + ('' if y == '' else '    ') + y, | ||||
|                     class_methods_wrapped) + '\n' | ||||
| 
 | ||||
|         # Class properties | ||||
|         if len(instantiated_class.properties) != 0: | ||||
|             property_accessors = self.wrap_class_properties( | ||||
|                 namespace_name, instantiated_class) | ||||
|             content_text += textwrap.indent(textwrap.dedent( | ||||
|                 "".join(property_accessors)), | ||||
|                                             prefix='    ') | ||||
| 
 | ||||
|         content_text += '  end'  # End the `methods` block | ||||
| 
 | ||||
|         # Static class methods | ||||
|         content_text += '  end\n\n  ' + reduce( | ||||
|         content_text += '\n\n  ' + reduce( | ||||
|             self._insert_spaces, | ||||
|             self.wrap_static_methods(namespace_name, instantiated_class, | ||||
|                                      serialize[0]).splitlines()) + '\n' | ||||
|                                      serialize[0]).splitlines()) + '\n' + \ | ||||
|         '  end\n' | ||||
| 
 | ||||
|         # Close the classdef | ||||
|         content_text += textwrap.dedent('''\ | ||||
|             end | ||||
|             end | ||||
|         ''') | ||||
| 
 | ||||
|         return file_name + '.m', content_text | ||||
|  | @ -1112,6 +1212,41 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|         return return_type_text | ||||
| 
 | ||||
|     def _collector_return(self, obj: str, ctype: parser.Type): | ||||
|         """Helper method to get the final statement before the return in the collector function.""" | ||||
|         expanded = '' | ||||
|         if self.is_shared_ptr(ctype) or self.is_ptr(ctype) or \ | ||||
|             self.can_be_pointer(ctype): | ||||
|             sep_method_name = partial(self._format_type_name, | ||||
|                                       ctype.typename, | ||||
|                                       include_namespace=True) | ||||
| 
 | ||||
|             if ctype.typename.name in self.ignore_namespace: | ||||
|                 expanded += self.wrap_collector_function_shared_return( | ||||
|                     ctype.typename, obj, 0, new_line=False) | ||||
| 
 | ||||
|             if ctype.is_shared_ptr or ctype.is_ptr: | ||||
|                 shared_obj = '{obj},"{method_name_sep}"'.format( | ||||
|                     obj=obj, method_name_sep=sep_method_name('.')) | ||||
|             else: | ||||
|                 method_name_sep_dot = sep_method_name('.') | ||||
|                 shared_obj_template = 'boost::make_shared<{method_name_sep_col}>({obj}),' \ | ||||
|                                         '"{method_name_sep_dot}"' | ||||
|                 shared_obj = shared_obj_template \ | ||||
|                     .format(method_name_sep_col=sep_method_name(), | ||||
|                             method_name_sep_dot=method_name_sep_dot, | ||||
|                             obj=obj) | ||||
| 
 | ||||
|             if ctype.typename.name not in self.ignore_namespace: | ||||
|                 expanded += textwrap.indent( | ||||
|                     'out[0] = wrap_shared_ptr({}, false);'.format(shared_obj), | ||||
|                     prefix='  ') | ||||
|         else: | ||||
|             expanded += '  out[0] = wrap< {} >({});'.format( | ||||
|                 ctype.typename.name, obj) | ||||
| 
 | ||||
|         return expanded | ||||
| 
 | ||||
|     def wrap_collector_function_return(self, method): | ||||
|         """ | ||||
|         Wrap the complete return type of the function. | ||||
|  | @ -1154,36 +1289,8 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|         if return_1_name != 'void': | ||||
|             if return_count == 1: | ||||
|                 if self.is_shared_ptr(return_1) or self.is_ptr(return_1) or \ | ||||
|                     self.can_be_pointer(return_1): | ||||
|                     sep_method_name = partial(self._format_type_name, | ||||
|                                               return_1.typename, | ||||
|                                               include_namespace=True) | ||||
|                 expanded += self._collector_return(obj, return_1) | ||||
| 
 | ||||
|                     if return_1.typename.name in self.ignore_namespace: | ||||
|                         expanded += self.wrap_collector_function_shared_return( | ||||
|                             return_1.typename, obj, 0, new_line=False) | ||||
| 
 | ||||
|                     if return_1.is_shared_ptr or return_1.is_ptr: | ||||
|                         shared_obj = '{obj},"{method_name_sep}"'.format( | ||||
|                             obj=obj, method_name_sep=sep_method_name('.')) | ||||
|                     else: | ||||
|                         method_name_sep_dot = sep_method_name('.') | ||||
|                         shared_obj_template = 'boost::make_shared<{method_name_sep_col}>({obj}),' \ | ||||
|                                               '"{method_name_sep_dot}"' | ||||
|                         shared_obj = shared_obj_template \ | ||||
|                             .format(method_name_sep_col=sep_method_name(), | ||||
|                                     method_name_sep_dot=method_name_sep_dot, | ||||
|                                     obj=obj) | ||||
| 
 | ||||
|                     if return_1.typename.name not in self.ignore_namespace: | ||||
|                         expanded += textwrap.indent( | ||||
|                             'out[0] = wrap_shared_ptr({}, false);'.format( | ||||
|                                 shared_obj), | ||||
|                             prefix='  ') | ||||
|                 else: | ||||
|                     expanded += '  out[0] = wrap< {} >({});'.format( | ||||
|                         return_1.typename.name, obj) | ||||
|             elif return_count == 2: | ||||
|                 return_2 = method.return_type.type2 | ||||
| 
 | ||||
|  | @ -1197,6 +1304,14 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|         return expanded | ||||
| 
 | ||||
|     def wrap_collector_property_return(self, class_property: parser.Variable): | ||||
|         """Get the last collector function statement before return for a property.""" | ||||
|         property_name = class_property.name | ||||
|         obj = 'obj->{}'.format(property_name) | ||||
|         property_type = class_property.ctype | ||||
| 
 | ||||
|         return self._collector_return(obj, property_type) | ||||
| 
 | ||||
|     def wrap_collector_function_upcast_from_void(self, class_name, func_id, | ||||
|                                                  cpp_name): | ||||
|         """ | ||||
|  | @ -1207,7 +1322,10 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|     def generate_collector_function(self, func_id): | ||||
|         """ | ||||
|         Generate the complete collector function. | ||||
|         Generate the complete collector function that goes into the wrapper.cpp file. | ||||
| 
 | ||||
|         A collector function is the Mex function used to interact between | ||||
|         the C++ object and the Matlab .m files. | ||||
|         """ | ||||
|         collector_func = self.wrapper_map.get(func_id) | ||||
| 
 | ||||
|  | @ -1228,6 +1346,7 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|             class_name_separated = collector_func[1].to_cpp() | ||||
|             is_method = isinstance(extra, parser.Method) | ||||
|             is_static_method = isinstance(extra, parser.StaticMethod) | ||||
|             is_property = isinstance(extra, parser.Variable) | ||||
| 
 | ||||
|             if collector_func[2] == 'collectorInsertAndMakeBase': | ||||
|                 body += textwrap.indent(textwrap.dedent('''\ | ||||
|  | @ -1246,6 +1365,7 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|                         *reinterpret_cast<SharedBase**>(mxGetData(out[0])) = new SharedBase(*self); | ||||
|                     ''').format(collector_func[1].parent_class), | ||||
|                                             prefix='  ') | ||||
| 
 | ||||
|             elif collector_func[2] == 'constructor': | ||||
|                 base = '' | ||||
|                 params, body_args = self._wrapper_unwrap_arguments( | ||||
|  | @ -1271,6 +1391,7 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|                                       params=params, | ||||
|                                       class_name=class_name, | ||||
|                                       base=base) | ||||
| 
 | ||||
|             elif collector_func[2] == 'deconstructor': | ||||
|                 body += textwrap.indent(textwrap.dedent('''\ | ||||
|                     typedef boost::shared_ptr<{class_name_sep}> Shared; | ||||
|  | @ -1285,16 +1406,19 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|                 ''').format(class_name_sep=class_name_separated, | ||||
|                             class_name=class_name), | ||||
|                                         prefix='  ') | ||||
| 
 | ||||
|             elif extra == 'serialize': | ||||
|                 body += self.wrap_collector_function_serialize( | ||||
|                     collector_func[1].name, | ||||
|                     full_name=collector_func[1].to_cpp(), | ||||
|                     namespace=collector_func[0]) | ||||
| 
 | ||||
|             elif extra == 'deserialize': | ||||
|                 body += self.wrap_collector_function_deserialize( | ||||
|                     collector_func[1].name, | ||||
|                     full_name=collector_func[1].to_cpp(), | ||||
|                     namespace=collector_func[0]) | ||||
| 
 | ||||
|             elif is_method or is_static_method: | ||||
|                 method_name = '' | ||||
| 
 | ||||
|  | @ -1303,12 +1427,9 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
| 
 | ||||
|                 method_name += extra.name | ||||
| 
 | ||||
|                 # return_type = extra.return_type | ||||
|                 # return_count = self._return_count(return_type) | ||||
| 
 | ||||
|                 return_body = self.wrap_collector_function_return(extra) | ||||
|                 params, body_args = self._wrapper_unwrap_arguments( | ||||
|                 _, body_args = self._wrapper_unwrap_arguments( | ||||
|                     extra.args, arg_id=1 if is_method else 0) | ||||
|                 return_body = self.wrap_collector_function_return(extra) | ||||
| 
 | ||||
|                 shared_obj = '' | ||||
| 
 | ||||
|  | @ -1330,6 +1451,57 @@ class MatlabWrapper(CheckMixin, FormatMixin): | |||
|                     body_args=body_args, | ||||
|                     return_body=return_body) | ||||
| 
 | ||||
|             elif is_property: | ||||
|                 shared_obj = '  auto obj = unwrap_shared_ptr<{class_name_sep}>' \ | ||||
|                             '(in[0], "ptr_{class_name}");\n'.format( | ||||
|                                 class_name_sep=class_name_separated, | ||||
|                                 class_name=class_name) | ||||
| 
 | ||||
|                 # Unpack the property from mxArray | ||||
|                 property_type, unwrap = self._unwrap_argument(extra, arg_id=1) | ||||
|                 unpack_property = textwrap.indent(textwrap.dedent('''\ | ||||
|                     {arg_type} {name} = {unwrap} | ||||
|                     '''.format(arg_type=property_type, | ||||
|                                name=extra.name, | ||||
|                                unwrap=unwrap)), | ||||
|                                                   prefix='  ') | ||||
| 
 | ||||
|                 # Getter | ||||
|                 if "_get_" in method_name: | ||||
|                     return_body = self.wrap_collector_property_return(extra) | ||||
| 
 | ||||
|                     getter = '  checkArguments("{property_name}",nargout,nargin{min1},' \ | ||||
|                             '{num_args});\n' \ | ||||
|                             '{shared_obj}' \ | ||||
|                             '{return_body}\n'.format( | ||||
|                         property_name=extra.name, | ||||
|                         min1='-1', | ||||
|                         num_args=0, | ||||
|                         shared_obj=shared_obj, | ||||
|                         return_body=return_body) | ||||
| 
 | ||||
|                     body += getter | ||||
| 
 | ||||
|                 # Setter | ||||
|                 if "_set_" in method_name: | ||||
|                     is_ptr_type = self.can_be_pointer(extra.ctype) | ||||
|                     return_body = '  obj->{0} = {1}{0};'.format( | ||||
|                         extra.name, '*' if is_ptr_type else '') | ||||
| 
 | ||||
|                     setter = '  checkArguments("{property_name}",nargout,nargin{min1},' \ | ||||
|                             '{num_args});\n' \ | ||||
|                             '{shared_obj}' \ | ||||
|                             '{unpack_property}' \ | ||||
|                             '{return_body}\n'.format( | ||||
|                         property_name=extra.name, | ||||
|                         min1='-1', | ||||
|                         num_args=1, | ||||
|                         shared_obj=shared_obj, | ||||
|                         unpack_property=unpack_property, | ||||
|                         return_body=return_body) | ||||
| 
 | ||||
|                     body += setter | ||||
| 
 | ||||
|             body += '}\n' | ||||
| 
 | ||||
|             if extra not in ['serialize', 'deserialize']: | ||||
|  |  | |||
|  | @ -5,7 +5,8 @@ from gtwrap.template_instantiator.constructor import InstantiatedConstructor | |||
| from gtwrap.template_instantiator.helpers import (InstantiationHelper, | ||||
|                                                   instantiate_args_list, | ||||
|                                                   instantiate_name, | ||||
|                                                   instantiate_return_type) | ||||
|                                                   instantiate_return_type, | ||||
|                                                   instantiate_type) | ||||
| from gtwrap.template_instantiator.method import (InstantiatedMethod, | ||||
|                                                  InstantiatedStaticMethod) | ||||
| 
 | ||||
|  | @ -14,6 +15,7 @@ class InstantiatedClass(parser.Class): | |||
|     """ | ||||
|     Instantiate the class defined in the interface file. | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, original: parser.Class, instantiations=(), new_name=''): | ||||
|         """ | ||||
|         Template <T, U> | ||||
|  | @ -24,7 +26,6 @@ class InstantiatedClass(parser.Class): | |||
| 
 | ||||
|         self.template = None | ||||
|         self.is_virtual = original.is_virtual | ||||
|         self.parent_class = original.parent_class | ||||
|         self.parent = original.parent | ||||
| 
 | ||||
|         # If the class is templated, check if the number of provided instantiations | ||||
|  | @ -42,7 +43,8 @@ class InstantiatedClass(parser.Class): | |||
|         # This will allow the `This` keyword to be used in both templated and non-templated classes. | ||||
|         typenames = self.original.template.typenames if self.original.template else [] | ||||
| 
 | ||||
|         # Instantiate the constructors, static methods, properties, respectively. | ||||
|         # Instantiate the parent class, constructors, static methods, properties, respectively. | ||||
|         self.parent_class = self.instantiate_parent_class(typenames) | ||||
|         self.ctors = self.instantiate_ctors(typenames) | ||||
|         self.static_methods = self.instantiate_static_methods(typenames) | ||||
|         self.properties = self.instantiate_properties(typenames) | ||||
|  | @ -83,6 +85,23 @@ class InstantiatedClass(parser.Class): | |||
|                operators="\n".join([repr(op) for op in self.operators]) | ||||
|             ) | ||||
| 
 | ||||
|     def instantiate_parent_class(self, typenames): | ||||
|         """ | ||||
|         Instantiate the inherited parent names. | ||||
| 
 | ||||
|         Args: | ||||
|             typenames: List of template types to instantiate. | ||||
| 
 | ||||
|         Return: List of constructors instantiated with provided template args. | ||||
|         """ | ||||
| 
 | ||||
|         if isinstance(self.original.parent_class, parser.type.TemplatedType): | ||||
|             return instantiate_type( | ||||
|                 self.original.parent_class, typenames, self.instantiations, | ||||
|                 parser.Typename(self.namespaces())).typename | ||||
|         else: | ||||
|             return self.original.parent_class | ||||
| 
 | ||||
|     def instantiate_ctors(self, typenames): | ||||
|         """ | ||||
|         Instantiate the class constructors. | ||||
|  | @ -178,12 +197,18 @@ class InstantiatedClass(parser.Class): | |||
| 
 | ||||
|         Return: List of properties instantiated with provided template args. | ||||
|         """ | ||||
|         instantiated_properties = instantiate_args_list( | ||||
|         instantiated_ = instantiate_args_list( | ||||
|             self.original.properties, | ||||
|             typenames, | ||||
|             self.instantiations, | ||||
|             self.cpp_typename(), | ||||
|         ) | ||||
|         # Convert to type Variable | ||||
|         instantiated_properties = [ | ||||
|             parser.Variable(ctype=[arg.ctype], | ||||
|                             name=arg.name, | ||||
|                             default=arg.default) for arg in instantiated_ | ||||
|         ] | ||||
|         return instantiated_properties | ||||
| 
 | ||||
|     def cpp_typename(self): | ||||
|  |  | |||
|  | @ -55,7 +55,8 @@ class InstantiatedGlobalFunction(parser.GlobalFunction): | |||
|         """Generate the C++ code for wrapping.""" | ||||
|         if self.original.template: | ||||
|             instantiated_names = [ | ||||
|                 inst.instantiated_name() for inst in self.instantiations | ||||
|                 "::".join(inst.namespaces + [inst.instantiated_name()]) | ||||
|                 for inst in self.instantiations | ||||
|             ] | ||||
|             ret = "{}<{}>".format(self.original.name, | ||||
|                                   ",".join(instantiated_names)) | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| version: 1.0.{build} | ||||
| image: | ||||
| - Visual Studio 2015 | ||||
| - Visual Studio 2017 | ||||
| test: off | ||||
| skip_branch_with_pr: true | ||||
| build: | ||||
|  | @ -11,11 +11,9 @@ environment: | |||
|   matrix: | ||||
|   - PYTHON: 36 | ||||
|     CONFIG: Debug | ||||
|   - PYTHON: 27 | ||||
|     CONFIG: Debug | ||||
| install: | ||||
| - ps: | | ||||
|     $env:CMAKE_GENERATOR = "Visual Studio 14 2015" | ||||
|     $env:CMAKE_GENERATOR = "Visual Studio 15 2017" | ||||
|     if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } | ||||
|     $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" | ||||
|     python -W ignore -m pip install --upgrade pip wheel | ||||
|  |  | |||
|  | @ -3,17 +3,36 @@ | |||
| # clang-format --style=llvm --dump-config | ||||
| BasedOnStyle: LLVM | ||||
| AccessModifierOffset: -4 | ||||
| AllowShortLambdasOnASingleLine: true | ||||
| AlwaysBreakTemplateDeclarations: Yes | ||||
| BinPackArguments: false | ||||
| BinPackParameters: false | ||||
| BreakBeforeBinaryOperators: All | ||||
| BreakConstructorInitializers: BeforeColon | ||||
| ColumnLimit: 99 | ||||
| CommentPragmas: 'NOLINT:.*|^ IWYU pragma:' | ||||
| IncludeBlocks: Regroup | ||||
| IndentCaseLabels: true | ||||
| IndentPPDirectives: AfterHash | ||||
| IndentWidth: 4 | ||||
| Language: Cpp | ||||
| SpaceAfterCStyleCast: true | ||||
| Standard: Cpp11 | ||||
| StatementMacros: ['PyObject_HEAD'] | ||||
| TabWidth: 4 | ||||
| IncludeCategories: | ||||
|   - Regex:           '<pybind11/.*' | ||||
|     Priority:        -1 | ||||
|   - Regex:           'pybind11.h"$' | ||||
|     Priority:        1 | ||||
|   - Regex:           '^".*/?detail/' | ||||
|     Priority:        1 | ||||
|     SortPriority:    2 | ||||
|   - Regex:           '^"' | ||||
|     Priority:        1 | ||||
|     SortPriority:    3 | ||||
|   - Regex:           '<[[:alnum:]._]+>' | ||||
|     Priority:        4 | ||||
|   - Regex:           '.*' | ||||
|     Priority:        5 | ||||
| ... | ||||
|  |  | |||
|  | @ -1,12 +1,17 @@ | |||
| FormatStyle: file | ||||
| 
 | ||||
| Checks: ' | ||||
| Checks: | | ||||
|   *bugprone*, | ||||
| cppcoreguidelines-init-variables, | ||||
| cppcoreguidelines-slicing, | ||||
|   *performance*, | ||||
|   clang-analyzer-optin.cplusplus.VirtualCall, | ||||
|   clang-analyzer-optin.performance.Padding, | ||||
|   cppcoreguidelines-init-variables, | ||||
|   cppcoreguidelines-prefer-member-initializer, | ||||
|   cppcoreguidelines-pro-type-static-cast-downcast, | ||||
|   cppcoreguidelines-slicing, | ||||
|   google-explicit-constructor, | ||||
|   llvm-namespace-comment, | ||||
|   misc-definitions-in-headers, | ||||
|   misc-misplaced-const, | ||||
|   misc-non-copyable-objects, | ||||
|   misc-static-assert, | ||||
|  | @ -14,6 +19,7 @@ misc-throw-by-value-catch-by-reference, | |||
|   misc-uniqueptr-reset-release, | ||||
|   misc-unused-parameters, | ||||
|   modernize-avoid-bind, | ||||
|   modernize-loop-convert, | ||||
|   modernize-make-shared, | ||||
|   modernize-redundant-void-arg, | ||||
|   modernize-replace-auto-ptr, | ||||
|  | @ -22,23 +28,26 @@ modernize-replace-random-shuffle, | |||
|   modernize-shrink-to-fit, | ||||
|   modernize-use-auto, | ||||
|   modernize-use-bool-literals, | ||||
|   modernize-use-default-member-init, | ||||
|   modernize-use-emplace, | ||||
|   modernize-use-equals-default, | ||||
|   modernize-use-equals-delete, | ||||
| modernize-use-default-member-init, | ||||
|   modernize-use-noexcept, | ||||
| modernize-use-emplace, | ||||
|   modernize-use-nullptr, | ||||
|   modernize-use-override, | ||||
|   modernize-use-using, | ||||
| *performance*, | ||||
|   readability-avoid-const-params-in-decls, | ||||
|   readability-braces-around-statements, | ||||
|   readability-const-return-type, | ||||
|   readability-container-size-empty, | ||||
|   readability-delete-null-pointer, | ||||
|   readability-else-after-return, | ||||
|   readability-implicit-bool-conversion, | ||||
|   readability-inconsistent-declaration-parameter-name, | ||||
|   readability-make-member-function-const, | ||||
|   readability-misplaced-array-index, | ||||
|   readability-non-const-parameter, | ||||
|   readability-qualified-auto, | ||||
|   readability-redundant-function-ptr-dereference, | ||||
|   readability-redundant-smartptr-get, | ||||
|   readability-redundant-string-cstr, | ||||
|  | @ -48,19 +57,19 @@ readability-static-definition-in-anonymous-namespace, | |||
|   readability-string-compare, | ||||
|   readability-suspicious-call-argument, | ||||
|   readability-uniqueptr-delete-release, | ||||
|   -bugprone-easily-swappable-parameters, | ||||
|   -bugprone-exception-escape, | ||||
|   -bugprone-reserved-identifier, | ||||
|   -bugprone-unused-raii, | ||||
| ' | ||||
| 
 | ||||
| CheckOptions: | ||||
| - key:             performance-for-range-copy.WarnOnAllAutoCopies | ||||
|   value:           true | ||||
| - key:             performance-inefficient-string-concatenation.StrictMode | ||||
|   value:           true | ||||
| - key:             performance-unnecessary-value-param.AllowedTypes | ||||
|   value:           'exception_ptr$;' | ||||
| - key:             readability-implicit-bool-conversion.AllowPointerConditions | ||||
|   value:           true | ||||
| 
 | ||||
| HeaderFilterRegex: 'pybind11/.*h' | ||||
| 
 | ||||
| WarningsAsErrors: '*' | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| docs/*.svg binary | ||||
|  | @ -93,11 +93,10 @@ cmake --build build -j4 | |||
| 
 | ||||
| Tips: | ||||
| 
 | ||||
| * You can use `virtualenv` (from PyPI) instead of `venv` (which is Python 3 | ||||
|   only). | ||||
| * You can use `virtualenv` (faster, from PyPI) instead of `venv`. | ||||
| * You can select any name for your environment folder; if it contains "env" it | ||||
|   will be ignored by git. | ||||
| * If you don’t have CMake 3.14+, just add “cmake” to the pip install command. | ||||
| * If you don't have CMake 3.14+, just add "cmake" to the pip install command. | ||||
| * You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+ | ||||
| * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. | ||||
|   FindPython uses `-DPython_ROOT_DIR=/path/to` or | ||||
|  | @ -105,7 +104,7 @@ Tips: | |||
| 
 | ||||
| ### Configuration options | ||||
| 
 | ||||
| In CMake, configuration options are given with “-D”. Options are stored in the | ||||
| In CMake, configuration options are given with "-D". Options are stored in the | ||||
| build directory, in the `CMakeCache.txt` file, so they are remembered for each | ||||
| build directory. Two selections are special - the generator, given with `-G`, | ||||
| and the compiler, which is selected based on environment variables `CXX` and | ||||
|  | @ -115,7 +114,7 @@ after the initial run. | |||
| The valid options are: | ||||
| 
 | ||||
| * `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo | ||||
| * `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+’s FindPython instead of the | ||||
| * `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+'s FindPython instead of the | ||||
|   classic, deprecated, custom FindPythonLibs | ||||
| * `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests) | ||||
| * `-DBUILD_TESTING=ON`: Enable the tests | ||||
|  | @ -236,12 +235,14 @@ directory inside your pybind11 git clone. Files will be modified in place, | |||
| so you can use git to monitor the changes. | ||||
| 
 | ||||
| ```bash | ||||
| docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:12 | ||||
| docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:13 | ||||
| apt-get update && apt-get install -y python3-dev python3-pytest | ||||
| cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-fix" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17 | ||||
| cmake --build build -j 2 -- --keep-going | ||||
| cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17 | ||||
| cmake --build build -j 2 | ||||
| ``` | ||||
| 
 | ||||
| You can add `--fix` to the options list if you want. | ||||
| 
 | ||||
| ### Include what you use | ||||
| 
 | ||||
| To run include what you use, install (`brew install include-what-you-use` on | ||||
|  | @ -257,7 +258,7 @@ The report is sent to stderr; you can pipe it into a file if you wish. | |||
| ### Build recipes | ||||
| 
 | ||||
| This builds with the Intel compiler (assuming it is in your path, along with a | ||||
| recent CMake and Python 3): | ||||
| recent CMake and Python): | ||||
| 
 | ||||
| ```bash | ||||
| python3 -m venv venv | ||||
|  |  | |||
|  | @ -5,12 +5,3 @@ updates: | |||
|     directory: "/" | ||||
|     schedule: | ||||
|       interval: "daily" | ||||
|     ignore: | ||||
|       # Official actions have moving tags like v1 | ||||
|       # that are used, so they don't need updates here | ||||
|       - dependency-name: "actions/checkout" | ||||
|       - dependency-name: "actions/setup-python" | ||||
|       - dependency-name: "actions/cache" | ||||
|       - dependency-name: "actions/upload-artifact" | ||||
|       - dependency-name: "actions/download-artifact" | ||||
|       - dependency-name: "actions/labeler" | ||||
|  |  | |||
|  | @ -0,0 +1,32 @@ | |||
| { | ||||
|   "problemMatcher": [ | ||||
|     { | ||||
|       "severity": "warning", | ||||
|       "pattern": [ | ||||
|         { | ||||
|           "regexp": "^([^:]+):(\\d+):(\\d+): ([A-DF-Z]\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", | ||||
|           "file": 1, | ||||
|           "line": 2, | ||||
|           "column": 3, | ||||
|           "code": 4, | ||||
|           "message": 5 | ||||
|         } | ||||
|       ], | ||||
|       "owner": "pylint-warning" | ||||
|     }, | ||||
|     { | ||||
|       "severity": "error", | ||||
|       "pattern": [ | ||||
|         { | ||||
|           "regexp": "^([^:]+):(\\d+):(\\d+): (E\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", | ||||
|           "file": 1, | ||||
|           "line": 2, | ||||
|           "column": 3, | ||||
|           "code": 4, | ||||
|           "message": 5 | ||||
|         } | ||||
|       ], | ||||
|       "owner": "pylint-error" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|  | @ -15,6 +15,8 @@ concurrency: | |||
| 
 | ||||
| env: | ||||
|   PIP_ONLY_BINARY: numpy | ||||
|   FORCE_COLOR: 3 | ||||
|   PYTEST_TIMEOUT: 300 | ||||
| 
 | ||||
| jobs: | ||||
|   # This is the "main" test suite, which tests a large number of different | ||||
|  | @ -25,13 +27,13 @@ jobs: | |||
|       matrix: | ||||
|         runs-on: [ubuntu-latest, windows-2022, macos-latest] | ||||
|         python: | ||||
|         - '2.7' | ||||
|         - '3.5' | ||||
|         - '3.6' | ||||
|         - '3.9' | ||||
|         - '3.10' | ||||
|         - 'pypy-3.7-v7.3.7' | ||||
|         - 'pypy-3.8-v7.3.7' | ||||
|         - '3.11-dev' | ||||
|         - '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,26 +47,26 @@ jobs: | |||
|             args: > | ||||
|               -DPYBIND11_FINDPYTHON=ON | ||||
|               -DCMAKE_CXX_FLAGS="-D_=1" | ||||
|           - runs-on: windows-latest | ||||
|           - runs-on: ubuntu-latest | ||||
|             python: 'pypy-3.8' | ||||
|             args: > | ||||
|               -DPYBIND11_FINDPYTHON=ON | ||||
|           - runs-on: windows-2019 | ||||
|             python: '3.6' | ||||
|             args: > | ||||
|               -DPYBIND11_FINDPYTHON=ON | ||||
|           - runs-on: macos-latest | ||||
|             python: 'pypy-2.7' | ||||
|           # Inject a couple Windows 2019 runs | ||||
|           - runs-on: windows-2019 | ||||
|             python: '3.9' | ||||
|           - runs-on: windows-2019 | ||||
|             python: '2.7' | ||||
| 
 | ||||
|     name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" | ||||
|     runs-on: ${{ matrix.runs-on }} | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup Python ${{ matrix.python }} | ||||
|       uses: actions/setup-python@v2 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: ${{ matrix.python }} | ||||
| 
 | ||||
|  | @ -82,7 +84,7 @@ jobs: | |||
| 
 | ||||
|     - name: Cache wheels | ||||
|       if: runner.os == 'macOS' | ||||
|       uses: actions/cache@v2 | ||||
|       uses: actions/cache@v3 | ||||
|       with: | ||||
|         # This path is specific to macOS - we really only need it for PyPy NumPy wheels | ||||
|         # See https://github.com/actions/cache/blob/master/examples.md#python---pip | ||||
|  | @ -168,27 +170,11 @@ jobs: | |||
|     - name: Interface test | ||||
|       run: cmake --build build2 --target test_cmake_build | ||||
| 
 | ||||
|     # Eventually Microsoft might have an action for setting up | ||||
|     # MSVC, but for now, this action works: | ||||
|     - name: Prepare compiler environment for Windows 🐍 2.7 | ||||
|       if: matrix.python == 2.7 && runner.os == 'Windows' | ||||
|       uses: ilammy/msvc-dev-cmd@v1.10.0 | ||||
|       with: | ||||
|         arch: x64 | ||||
| 
 | ||||
|     # This makes two environment variables available in the following step(s) | ||||
|     - name: Set Windows 🐍 2.7 environment variables | ||||
|       if: matrix.python == 2.7 && runner.os == 'Windows' | ||||
|       shell: bash | ||||
|       run: | | ||||
|         echo "DISTUTILS_USE_SDK=1" >> $GITHUB_ENV | ||||
|         echo "MSSdk=1" >> $GITHUB_ENV | ||||
| 
 | ||||
|     # This makes sure the setup_helpers module can build packages using | ||||
|     # setuptools | ||||
|     - name: Setuptools helpers test | ||||
|       run: pytest tests/extra_setuptools | ||||
|       if: "!(matrix.python == '3.5' && matrix.runs-on == 'windows-2022')" | ||||
|       if: "!(matrix.runs-on == 'windows-2022')" | ||||
| 
 | ||||
| 
 | ||||
|   deadsnakes: | ||||
|  | @ -200,14 +186,14 @@ jobs: | |||
|         - python-version: "3.9" | ||||
|           python-debug: true | ||||
|           valgrind: true | ||||
|       # - python-version: "3.11-dev" | ||||
|       #   python-debug: false | ||||
|         - python-version: "3.11-dev" | ||||
|           python-debug: false | ||||
| 
 | ||||
|     name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup Python ${{ matrix.python-version }} (deadsnakes) | ||||
|       uses: deadsnakes/action@v2.1.1 | ||||
|  | @ -220,7 +206,7 @@ jobs: | |||
| 
 | ||||
|     - name: Valgrind cache | ||||
|       if: matrix.valgrind | ||||
|       uses: actions/cache@v2 | ||||
|       uses: actions/cache@v3 | ||||
|       id: cache-valgrind | ||||
|       with: | ||||
|         path: valgrind | ||||
|  | @ -295,12 +281,14 @@ jobs: | |||
|             std: 20 | ||||
|           - clang: 10 | ||||
|             std: 17 | ||||
|           - clang: 14 | ||||
|             std: 20 | ||||
| 
 | ||||
|     name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64" | ||||
|     container: "silkeh/clang:${{ matrix.clang }}" | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Add wget and python3 | ||||
|       run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev | ||||
|  | @ -330,11 +318,11 @@ jobs: | |||
|   # Testing NVCC; forces sources to behave like .cu files | ||||
|   cuda: | ||||
|     runs-on: ubuntu-latest | ||||
|     name: "🐍 3.8 • CUDA 11 • Ubuntu 20.04" | ||||
|     container: nvidia/cuda:11.0-devel-ubuntu20.04 | ||||
|     name: "🐍 3.8 • CUDA 11.2 • Ubuntu 20.04" | ||||
|     container: nvidia/cuda:11.2.2-devel-ubuntu20.04 | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     # tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND | ||||
|     - name: Install 🐍 3 | ||||
|  | @ -358,7 +346,7 @@ jobs: | |||
| #    container: centos:8 | ||||
| # | ||||
| #    steps: | ||||
| #    - uses: actions/checkout@v2 | ||||
| #    - uses: actions/checkout@v3 | ||||
| # | ||||
| #    - 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 | ||||
|  | @ -397,17 +385,17 @@ jobs: | |||
|   # Testing on CentOS 7 + PGI compilers, which seems to require more workarounds | ||||
|   centos-nvhpc7: | ||||
|     runs-on: ubuntu-latest | ||||
|     name: "🐍 3 • CentOS7 / PGI 20.9 • x64" | ||||
|     name: "🐍 3 • CentOS7 / PGI 22.3 • x64" | ||||
|     container: centos:7 | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Add Python 3 and a few requirements | ||||
|       run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 | ||||
|       run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 yum-utils | ||||
| 
 | ||||
|     - name: Install NVidia HPC SDK | ||||
|       run:  yum -y install https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-20-9-20.9-1.x86_64.rpm https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-2020-20.9-1.x86_64.rpm | ||||
|       run: yum-config-manager --add-repo https://developer.download.nvidia.com/hpc-sdk/rhel/nvhpc.repo && yum -y install nvhpc-22.3 | ||||
| 
 | ||||
|     # On CentOS 7, we have to filter a few tests (compiler internal error) | ||||
|     # and allow deeper template recursion (not needed on CentOS 8 with a newer | ||||
|  | @ -417,7 +405,7 @@ jobs: | |||
|       shell: bash | ||||
|       run: | | ||||
|         source /etc/profile.d/modules.sh | ||||
|         module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.9 | ||||
|         module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/22.3 | ||||
|         cmake3 -S . -B build -DDOWNLOAD_CATCH=ON \ | ||||
|                             -DCMAKE_CXX_STANDARD=11 \ | ||||
|                             -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \ | ||||
|  | @ -462,7 +450,7 @@ jobs: | |||
|     container: "gcc:${{ matrix.gcc }}" | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Add Python 3 | ||||
|       run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev | ||||
|  | @ -504,7 +492,7 @@ jobs: | |||
|     name: "🐍 3 • ICC latest • x64" | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Add apt repo | ||||
|       run: | | ||||
|  | @ -599,19 +587,25 @@ jobs: | |||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         centos: | ||||
|           - centos7  # GCC 4.8 | ||||
|           - stream8 | ||||
|         container: | ||||
|           - "centos:7"  # GCC 4.8 | ||||
|           - "almalinux:8" | ||||
|           - "almalinux:9" | ||||
| 
 | ||||
|     name: "🐍 3 • CentOS ${{ matrix.centos }} • x64" | ||||
|     container: "quay.io/centos/centos:${{ matrix.centos }}" | ||||
|     name: "🐍 3 • ${{ matrix.container }} • x64" | ||||
|     container: "${{ matrix.container }}" | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Add Python 3 | ||||
|     - name: Add Python 3 (RHEL 7) | ||||
|       if: matrix.container == 'centos:7' | ||||
|       run: yum update -y && yum install -y python3-devel gcc-c++ make git | ||||
| 
 | ||||
|     - name: Add Python 3 (RHEL 8+) | ||||
|       if: matrix.container != 'centos:7' | ||||
|       run: dnf update -y && dnf install -y python3-devel gcc-c++ make git | ||||
| 
 | ||||
|     - name: Update pip | ||||
|       run: python3 -m pip install --upgrade pip | ||||
| 
 | ||||
|  | @ -645,18 +639,18 @@ jobs: | |||
| 
 | ||||
|   # This tests an "install" with the CMake tools | ||||
|   install-classic: | ||||
|     name: "🐍 3.5 • Debian • x86 •  Install" | ||||
|     name: "🐍 3.7 • Debian • x86 •  Install" | ||||
|     runs-on: ubuntu-latest | ||||
|     container: i386/debian:stretch | ||||
|     container: i386/debian:buster | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - uses: actions/checkout@v1  # Required to run inside docker | ||||
| 
 | ||||
|     - name: Install requirements | ||||
|       run: | | ||||
|         apt-get update | ||||
|         apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip | ||||
|         pip3 install "pytest==3.1.*" | ||||
|         pip3 install "pytest==6.*" | ||||
| 
 | ||||
|     - name: Configure for install | ||||
|       run: > | ||||
|  | @ -687,15 +681,17 @@ 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 | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - uses: actions/setup-python@v2 | ||||
|     - uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: "3.x" | ||||
| 
 | ||||
|     - name: Install Doxygen | ||||
|       run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04 | ||||
|  | @ -725,27 +721,25 @@ jobs: | |||
|       fail-fast: false | ||||
|       matrix: | ||||
|         python: | ||||
|         - 3.5 | ||||
|         - 3.6 | ||||
|         - 3.7 | ||||
|         - 3.8 | ||||
|         - 3.9 | ||||
|         - pypy-3.6 | ||||
| 
 | ||||
|         include: | ||||
|           - python: 3.9 | ||||
|             args: -DCMAKE_CXX_STANDARD=20 -DDOWNLOAD_EIGEN=OFF | ||||
|             args: -DCMAKE_CXX_STANDARD=20 | ||||
|           - python: 3.8 | ||||
|             args: -DCMAKE_CXX_STANDARD=17 | ||||
| 
 | ||||
|     name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}" | ||||
|     runs-on: windows-latest | ||||
|     runs-on: windows-2019 | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup Python ${{ matrix.python }} | ||||
|       uses: actions/setup-python@v2 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: ${{ matrix.python }} | ||||
|         architecture: x86 | ||||
|  | @ -777,25 +771,31 @@ jobs: | |||
|     - name: Python tests | ||||
|       run: cmake --build build -t pytest | ||||
| 
 | ||||
|   win32-msvc2015: | ||||
|     name: "🐍 ${{ matrix.python }} • MSVC 2015 • x64" | ||||
|     runs-on: windows-latest | ||||
|   win32-debug: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         python: | ||||
|           - 2.7 | ||||
|           - 3.6 | ||||
|           - 3.7 | ||||
|           # todo: check/cpptest does not support 3.8+ yet | ||||
|         - 3.8 | ||||
|         - 3.9 | ||||
| 
 | ||||
|         include: | ||||
|           - 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 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup 🐍 ${{ matrix.python }} | ||||
|       uses: actions/setup-python@v2 | ||||
|     - name: Setup Python ${{ matrix.python }} | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: ${{ matrix.python }} | ||||
|         architecture: x86 | ||||
| 
 | ||||
|     - name: Update CMake | ||||
|       uses: jwlawson/actions-setup-cmake@v1.12 | ||||
|  | @ -803,82 +803,73 @@ jobs: | |||
|     - name: Prepare MSVC | ||||
|       uses: ilammy/msvc-dev-cmd@v1.10.0 | ||||
|       with: | ||||
|         toolset: 14.0 | ||||
|         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 14 2015" -A x64 | ||||
|         -G "Visual Studio 16 2019" -A Win32 | ||||
|         -DCMAKE_BUILD_TYPE=Debug | ||||
|         -DPYBIND11_WERROR=ON | ||||
|         -DDOWNLOAD_CATCH=ON | ||||
|         -DDOWNLOAD_EIGEN=ON | ||||
|         ${{ matrix.args }} | ||||
|     - name: Build C++11 | ||||
|       run: cmake --build build --config Debug -j 2 | ||||
| 
 | ||||
|     - name: Build C++14 | ||||
|       run: cmake --build build -j 2 | ||||
| 
 | ||||
|     - name: Run all checks | ||||
|       run: cmake --build build -t check | ||||
|     - name: Python tests | ||||
|       run: cmake --build build --config Debug -t pytest | ||||
| 
 | ||||
| 
 | ||||
|   win32-msvc2017: | ||||
|     name: "🐍 ${{ matrix.python }} • MSVC 2017 • x64" | ||||
|     runs-on: windows-2016 | ||||
|   windows-2022: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         python: | ||||
|           - 2.7 | ||||
|           - 3.5 | ||||
|           - 3.7 | ||||
|         std: | ||||
|           - 14 | ||||
|         - 3.9 | ||||
| 
 | ||||
|         include: | ||||
|           - python: 2.7 | ||||
|             std: 17 | ||||
|             args: > | ||||
|               -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" | ||||
|           - python: 3.7 | ||||
|             std: 17 | ||||
|             args: > | ||||
|               -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" | ||||
|     name: "🐍 ${{ matrix.python }} • MSVC 2022 C++20 • x64" | ||||
|     runs-on: windows-2022 | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup 🐍 ${{ matrix.python }} | ||||
|       uses: actions/setup-python@v2 | ||||
|     - name: Setup Python ${{ matrix.python }} | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: ${{ matrix.python }} | ||||
| 
 | ||||
|     - name: Prepare env | ||||
|       run: | | ||||
|         python3 -m pip install -r tests/requirements.txt | ||||
| 
 | ||||
|     - name: Update CMake | ||||
|       uses: jwlawson/actions-setup-cmake@v1.12 | ||||
| 
 | ||||
|     - name: Prepare env | ||||
|       run: | | ||||
|         python -m pip install -r tests/requirements.txt | ||||
| 
 | ||||
|     # First build - C++11 mode and inplace | ||||
|     - name: Configure | ||||
|     - name: Configure C++20 | ||||
|       run: > | ||||
|         cmake -S . -B build | ||||
|         -G "Visual Studio 15 2017" -A x64 | ||||
|         -DPYBIND11_WERROR=ON | ||||
|         -DDOWNLOAD_CATCH=ON | ||||
|         -DDOWNLOAD_EIGEN=ON | ||||
|         -DCMAKE_CXX_STANDARD=${{ matrix.std }} | ||||
|         ${{ matrix.args }} | ||||
|         -DCMAKE_CXX_STANDARD=20 | ||||
| 
 | ||||
|     - name: Build ${{ matrix.std }} | ||||
|     - name: Build C++20 | ||||
|       run: cmake --build build -j 2 | ||||
| 
 | ||||
|     - name: Run all checks | ||||
|       run: cmake --build build -t check | ||||
|     - name: Python tests | ||||
|       run: cmake --build build --target pytest | ||||
| 
 | ||||
|     - name: C++20 tests | ||||
|       run: cmake --build build --target cpptest -j 2 | ||||
| 
 | ||||
|     - name: Interface test C++20 | ||||
|       run: cmake --build build --target test_cmake_build | ||||
| 
 | ||||
|   mingw: | ||||
|     name: "🐍 3 • windows-latest • ${{ matrix.sys }}" | ||||
|  | @ -909,12 +900,12 @@ jobs: | |||
|           mingw-w64-${{matrix.env}}-boost | ||||
|           mingw-w64-${{matrix.env}}-catch | ||||
| 
 | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Configure C++11 | ||||
|       # LTO leads to many undefined reference like | ||||
|       # `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&) | ||||
|       run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -S . -B build | ||||
|       run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DDOWNLOAD_CATCH=ON -S . -B build | ||||
| 
 | ||||
|     - name: Build C++11 | ||||
|       run: cmake --build build -j 2 | ||||
|  | @ -932,7 +923,7 @@ jobs: | |||
|       run: git clean -fdx | ||||
| 
 | ||||
|     - name: Configure C++14 | ||||
|       run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -S . -B build2 | ||||
|       run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DDOWNLOAD_CATCH=ON -S . -B build2 | ||||
| 
 | ||||
|     - name: Build C++14 | ||||
|       run: cmake --build build2 -j 2 | ||||
|  | @ -950,7 +941,7 @@ jobs: | |||
|       run: git clean -fdx | ||||
| 
 | ||||
|     - name: Configure C++17 | ||||
|       run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -S . -B build3 | ||||
|       run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DDOWNLOAD_CATCH=ON -S . -B build3 | ||||
| 
 | ||||
|     - name: Build C++17 | ||||
|       run: cmake --build build3 -j 2 | ||||
|  |  | |||
|  | @ -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,22 +29,18 @@ 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 }} | ||||
|     runs-on: ${{ matrix.runs-on }} | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup Python 3.7 | ||||
|       uses: actions/setup-python@v2 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: 3.7 | ||||
|         architecture: ${{ matrix.arch }} | ||||
|  |  | |||
|  | @ -12,14 +12,21 @@ on: | |||
|     - stable | ||||
|     - "v*" | ||||
| 
 | ||||
| env: | ||||
|   FORCE_COLOR: 3 | ||||
| 
 | ||||
| jobs: | ||||
|   pre-commit: | ||||
|     name: Format | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/setup-python@v2 | ||||
|     - uses: pre-commit/action@v2.0.3 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: "3.x" | ||||
|     - name: Add matchers | ||||
|       run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" | ||||
|     - uses: pre-commit/action@v3.0.0 | ||||
|       with: | ||||
|         # Slow hooks are marked with manual - slow is okay here, run them too | ||||
|         extra_args: --hook-stage manual --all-files | ||||
|  | @ -29,9 +36,9 @@ jobs: | |||
|     # in .github/CONTRIBUTING.md and update as needed. | ||||
|     name: Clang-Tidy | ||||
|     runs-on: ubuntu-latest | ||||
|     container: silkeh/clang:12 | ||||
|     container: silkeh/clang:13 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Install requirements | ||||
|       run: apt-get update && apt-get install -y python3-dev python3-pytest | ||||
|  | @ -39,7 +46,7 @@ jobs: | |||
|     - name: Configure | ||||
|       run: > | ||||
|         cmake -S . -B build | ||||
|         -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy)" | ||||
|         -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color;--warnings-as-errors=*" | ||||
|         -DDOWNLOAD_EIGEN=ON | ||||
|         -DDOWNLOAD_CATCH=ON | ||||
|         -DCMAKE_CXX_STANDARD=17 | ||||
|  |  | |||
|  | @ -17,19 +17,19 @@ env: | |||
| 
 | ||||
| jobs: | ||||
|   # This builds the sdists and wheels and makes sure the files are exactly as | ||||
|   # expected. Using Windows and Python 2.7, since that is often the most | ||||
|   # expected. Using Windows and Python 3.6, since that is often the most | ||||
|   # challenging matrix element. | ||||
|   test-packaging: | ||||
|     name: 🐍 2.7 • 📦 tests • windows-latest | ||||
|     name: 🐍 3.6 • 📦 tests • windows-latest | ||||
|     runs-on: windows-latest | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup 🐍 2.7 | ||||
|       uses: actions/setup-python@v2 | ||||
|     - name: Setup 🐍 3.6 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: 2.7 | ||||
|         python-version: 3.6 | ||||
| 
 | ||||
|     - name: Prepare env | ||||
|       run: | | ||||
|  | @ -46,10 +46,10 @@ jobs: | |||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup 🐍 3.8 | ||||
|       uses: actions/setup-python@v2 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: 3.8 | ||||
| 
 | ||||
|  | @ -69,13 +69,13 @@ jobs: | |||
|       run: twine check dist/* | ||||
| 
 | ||||
|     - name: Save standard package | ||||
|       uses: actions/upload-artifact@v2 | ||||
|       uses: actions/upload-artifact@v3 | ||||
|       with: | ||||
|         name: standard | ||||
|         path: dist/pybind11-* | ||||
| 
 | ||||
|     - name: Save global package | ||||
|       uses: actions/upload-artifact@v2 | ||||
|       uses: actions/upload-artifact@v3 | ||||
|       with: | ||||
|         name: global | ||||
|         path: dist/pybind11_global-* | ||||
|  | @ -90,10 +90,12 @@ jobs: | |||
|     needs: [packaging] | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/setup-python@v2 | ||||
|     - uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: "3.x" | ||||
| 
 | ||||
|     # Downloads all to directories matching the artifact names | ||||
|     - uses: actions/download-artifact@v2 | ||||
|     - uses: actions/download-artifact@v3 | ||||
| 
 | ||||
|     - name: Publish standard package | ||||
|       uses: pypa/gh-action-pypi-publish@v1.5.0 | ||||
|  |  | |||
|  | @ -14,15 +14,15 @@ env: | |||
| 
 | ||||
| jobs: | ||||
|   standard: | ||||
|     name: "🐍 3.11 dev • ubuntu-latest • x64" | ||||
|     name: "🐍 3.11 latest internals • ubuntu-latest • x64" | ||||
|     runs-on: ubuntu-latest | ||||
|     if: "contains(github.event.pull_request.labels.*.name, 'python dev')" | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
| 
 | ||||
|     - name: Setup Python 3.11 | ||||
|       uses: actions/setup-python@v2 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: "3.11-dev" | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| repos: | ||||
| # Standard hooks | ||||
| - repo: https://github.com/pre-commit/pre-commit-hooks | ||||
|   rev: v4.1.0 | ||||
|   rev: "v4.3.0" | ||||
|   hooks: | ||||
|   - id: check-added-large-files | ||||
|   - id: check-case-conflict | ||||
|  | @ -29,73 +29,92 @@ repos: | |||
|   - id: mixed-line-ending | ||||
|   - id: requirements-txt-fixer | ||||
|   - id: trailing-whitespace | ||||
|   - id: fix-encoding-pragma | ||||
|     exclude: ^noxfile.py$ | ||||
| 
 | ||||
| # Upgrade old Python syntax | ||||
| - repo: https://github.com/asottile/pyupgrade | ||||
|   rev: v2.31.0 | ||||
|   rev: "v2.37.1" | ||||
|   hooks: | ||||
|   - id: pyupgrade | ||||
|     args: [--py36-plus] | ||||
| 
 | ||||
| # Nicely sort includes | ||||
| - repo: https://github.com/PyCQA/isort | ||||
|   rev: 5.10.1 | ||||
|   rev: "5.10.1" | ||||
|   hooks: | ||||
|   - id: isort | ||||
| 
 | ||||
| # Black, the code formatter, natively supports pre-commit | ||||
| - repo: https://github.com/psf/black | ||||
|   rev: 21.12b0 # Keep in sync with blacken-docs | ||||
|   rev: "22.6.0" # Keep in sync with blacken-docs | ||||
|   hooks: | ||||
|   - id: black | ||||
| 
 | ||||
| # Also code format the docs | ||||
| - repo: https://github.com/asottile/blacken-docs | ||||
|   rev: v1.12.0 | ||||
|   rev: "v1.12.1" | ||||
|   hooks: | ||||
|   - id: blacken-docs | ||||
|     additional_dependencies: | ||||
|     - black==21.12b0 # keep in sync with black hook | ||||
|     - black==22.6.0 # keep in sync with black hook | ||||
| 
 | ||||
| # Changes tabs to spaces | ||||
| - repo: https://github.com/Lucas-C/pre-commit-hooks | ||||
|   rev: v1.1.10 | ||||
|   rev: "v1.3.0" | ||||
|   hooks: | ||||
|   - id: remove-tabs | ||||
| 
 | ||||
| - repo: https://github.com/sirosen/texthooks | ||||
|   rev: "0.3.1" | ||||
|   hooks: | ||||
|   - id: fix-ligatures | ||||
|   - id: fix-smartquotes | ||||
| 
 | ||||
| # Autoremoves unused imports | ||||
| - repo: https://github.com/hadialqattan/pycln | ||||
|   rev: v1.1.0 | ||||
|   rev: "v2.0.1" | ||||
|   hooks: | ||||
|   - id: pycln | ||||
|     stages: [manual] | ||||
| 
 | ||||
| # Checking for common mistakes | ||||
| - repo: https://github.com/pre-commit/pygrep-hooks | ||||
|   rev: v1.9.0 | ||||
|   rev: "v1.9.0" | ||||
|   hooks: | ||||
|   - id: python-check-blanket-noqa | ||||
|   - id: python-check-blanket-type-ignore | ||||
|   - id: python-no-log-warn | ||||
|   - id: python-use-type-annotations | ||||
|   - id: rst-backticks | ||||
|   - id: rst-directive-colons | ||||
|   - id: rst-inline-touching-normal | ||||
| 
 | ||||
| # Flake8 also supports pre-commit natively (same author) | ||||
| - repo: https://github.com/PyCQA/flake8 | ||||
|   rev: 4.0.1 | ||||
| # Automatically remove noqa that are not used | ||||
| - repo: https://github.com/asottile/yesqa | ||||
|   rev: "v1.3.0" | ||||
|   hooks: | ||||
|   - id: flake8 | ||||
|   - id: yesqa | ||||
|     additional_dependencies: &flake8_dependencies | ||||
|       - flake8-bugbear | ||||
|       - pep8-naming | ||||
|     exclude: ^(docs/.*|tools/.*)$ | ||||
| 
 | ||||
| - repo: https://github.com/asottile/yesqa | ||||
|   rev: v1.3.0 | ||||
| # Flake8 also supports pre-commit natively (same author) | ||||
| - repo: https://github.com/PyCQA/flake8 | ||||
|   rev: "4.0.1" | ||||
|   hooks: | ||||
|   - id: yesqa | ||||
|   - id: flake8 | ||||
|     exclude: ^(docs/.*|tools/.*)$ | ||||
|     additional_dependencies: *flake8_dependencies | ||||
| 
 | ||||
| # PyLint has native support - not always usable, but works for us | ||||
| - repo: https://github.com/PyCQA/pylint | ||||
|   rev: "v2.14.4" | ||||
|   hooks: | ||||
|   - id: pylint | ||||
|     files: ^pybind11 | ||||
| 
 | ||||
| # CMake formatting | ||||
| - repo: https://github.com/cheshirekow/cmake-format-precommit | ||||
|   rev: v0.6.13 | ||||
|   rev: "v0.6.13" | ||||
|   hooks: | ||||
|   - id: cmake-format | ||||
|     additional_dependencies: [pyyaml] | ||||
|  | @ -104,48 +123,48 @@ repos: | |||
| 
 | ||||
| # Check static types with mypy | ||||
| - repo: https://github.com/pre-commit/mirrors-mypy | ||||
|   rev: v0.931 | ||||
|   rev: "v0.961" | ||||
|   hooks: | ||||
|   - id: mypy | ||||
|     # Running per-file misbehaves a bit, so just run on all files, it's fast | ||||
|     pass_filenames: false | ||||
|     additional_dependencies: [typed_ast] | ||||
|     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 | ||||
|     stages: [manual] | ||||
|     additional_dependencies: [cmake, ninja] | ||||
| 
 | ||||
| # Check for spelling | ||||
| - repo: https://github.com/codespell-project/codespell | ||||
|   rev: v2.1.0 | ||||
|   rev: "v2.1.0" | ||||
|   hooks: | ||||
|   - id: codespell | ||||
|     exclude: ".supp$" | ||||
|     args: ["-L", "nd,ot,thist"] | ||||
| 
 | ||||
| # Check for common shell mistakes | ||||
| - repo: https://github.com/shellcheck-py/shellcheck-py | ||||
|   rev: v0.8.0.3 | ||||
|   rev: "v0.8.0.4" | ||||
|   hooks: | ||||
|   - id: shellcheck | ||||
| 
 | ||||
| # The original pybind11 checks for a few C++ style items | ||||
| # Disallow some common capitalization mistakes | ||||
| - repo: local | ||||
|   hooks: | ||||
|   - id: disallow-caps | ||||
|     name: Disallow improper capitalization | ||||
|     language: pygrep | ||||
|     entry: PyBind|Numpy|Cmake|CCache|PyTest | ||||
|     exclude: .pre-commit-config.yaml | ||||
|     exclude: ^\.pre-commit-config.yaml$ | ||||
| 
 | ||||
| - repo: local | ||||
| # Clang format the codebase automatically | ||||
| - repo: https://github.com/pre-commit/mirrors-clang-format | ||||
|   rev: "v14.0.6" | ||||
|   hooks: | ||||
|   - id: check-style | ||||
|     name: Classic check-style | ||||
|     language: system | ||||
|     types: | ||||
|     - c++ | ||||
|     entry: ./tools/check-style.sh | ||||
|   - id: clang-format | ||||
|     types_or: [c++, c, cuda] | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| recursive-include pybind11/include/pybind11 *.h | ||||
| recursive-include pybind11 *.py | ||||
| recursive-include pybind11 py.typed | ||||
| recursive-include pybind11 *.pyi | ||||
| include pybind11/share/cmake/pybind11/*.cmake | ||||
| include LICENSE README.rst pyproject.toml setup.py setup.cfg | ||||
|  |  | |||
|  | @ -32,9 +32,9 @@ this heavy machinery has become an excessively large and unnecessary | |||
| dependency. | ||||
| 
 | ||||
| Think of this library as a tiny self-contained version of Boost.Python | ||||
| with everything stripped away that isn’t relevant for binding | ||||
| with everything stripped away that isn't relevant for binding | ||||
| generation. Without comments, the core header files only require ~4K | ||||
| lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++ | ||||
| lines of code and depend on Python (3.6+, or PyPy) and the C++ | ||||
| standard library. This compact implementation was possible thanks to | ||||
| some of the new C++11 language features (specifically: tuples, lambda | ||||
| functions and variadic templates). Since its creation, this library has | ||||
|  | @ -78,8 +78,8 @@ Goodies | |||
| In addition to the core functionality, pybind11 provides some extra | ||||
| goodies: | ||||
| 
 | ||||
| - Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an | ||||
|   implementation-agnostic interface. | ||||
| - Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic | ||||
|   interface (pybind11 2.9 was the last version to support Python 2 and 3.5). | ||||
| 
 | ||||
| - It is possible to bind C++11 lambda functions with captured | ||||
|   variables. The lambda capture data is stored inside the resulting | ||||
|  | @ -88,8 +88,8 @@ goodies: | |||
| - pybind11 uses C++11 move constructors and move assignment operators | ||||
|   whenever possible to efficiently transfer custom data types. | ||||
| 
 | ||||
| - It’s easy to expose the internal storage of custom data types through | ||||
|   Pythons’ buffer protocols. This is handy e.g. for fast conversion | ||||
| - It's easy to expose the internal storage of custom data types through | ||||
|   Pythons' buffer protocols. This is handy e.g. for fast conversion | ||||
|   between C++ matrix classes like Eigen and NumPy without expensive | ||||
|   copy operations. | ||||
| 
 | ||||
|  | @ -119,10 +119,10 @@ goodies: | |||
| Supported compilers | ||||
| ------------------- | ||||
| 
 | ||||
| 1. Clang/LLVM 3.3 or newer (for Apple Xcode’s clang, this is 5.0.0 or | ||||
| 1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or | ||||
|    newer) | ||||
| 2. GCC 4.8 or newer | ||||
| 3. Microsoft Visual Studio 2015 Update 3 or newer | ||||
| 3. Microsoft Visual Studio 2017 or newer | ||||
| 4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI) | ||||
| 5. Cygwin/GCC (previously tested on 2.5.1) | ||||
| 6. NVCC (CUDA 11.0 tested in CI) | ||||
|  |  | |||
|  | @ -18,5 +18,4 @@ ALIASES               += "endrst=\endverbatim" | |||
| QUIET                  = YES | ||||
| WARNINGS               = YES | ||||
| WARN_IF_UNDOCUMENTED   = NO | ||||
| PREDEFINED             = PY_MAJOR_VERSION=3 \ | ||||
|                          PYBIND11_NOINLINE | ||||
| PREDEFINED             = PYBIND11_NOINLINE | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| .highlight .go { | ||||
|   color: #707070; | ||||
| } | ||||
|  | @ -1,11 +0,0 @@ | |||
| .wy-table-responsive table td, | ||||
| .wy-table-responsive table th { | ||||
|     white-space: initial !important; | ||||
| } | ||||
| .rst-content table.docutils td { | ||||
|     vertical-align: top !important; | ||||
| } | ||||
| div[class^='highlight'] pre { | ||||
|     white-space: pre; | ||||
|     white-space: pre-wrap; | ||||
| } | ||||
|  | @ -167,5 +167,4 @@ as arguments and return values, refer to the section on binding :ref:`classes`. | |||
| +------------------------------------+---------------------------+-----------------------------------+ | ||||
| 
 | ||||
| .. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and | ||||
|    ``os.PathLike`` is converted to ``std::filesystem::path``, but this requires | ||||
|    Python 3.6 (for ``__fspath__`` support). | ||||
|    ``os.PathLike`` is converted to ``std::filesystem::path``. | ||||
|  |  | |||
|  | @ -87,8 +87,6 @@ included to tell pybind11 how to visit the variant. | |||
| 
 | ||||
|     pybind11 only supports the modern implementation of ``boost::variant`` | ||||
|     which makes use of variadic templates. This requires Boost 1.56 or newer. | ||||
|     Additionally, on Windows, MSVC 2017 is required because ``boost::variant`` | ||||
|     falls back to the old non-variadic implementation on MSVC 2015. | ||||
| 
 | ||||
| .. _opaque: | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,14 +1,6 @@ | |||
| Strings, bytes and Unicode conversions | ||||
| ###################################### | ||||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|     This section discusses string handling in terms of Python 3 strings. For | ||||
|     Python 2.7, replace all occurrences of ``str`` with ``unicode`` and | ||||
|     ``bytes`` with ``str``.  Python 2.7 users may find it best to use ``from | ||||
|     __future__ import unicode_literals`` to avoid unintentionally using ``str`` | ||||
|     instead of ``unicode``. | ||||
| 
 | ||||
| Passing Python strings to C++ | ||||
| ============================= | ||||
| 
 | ||||
|  | @ -58,9 +50,9 @@ Passing bytes to C++ | |||
| -------------------- | ||||
| 
 | ||||
| A Python ``bytes`` object will be passed to C++ functions that accept | ||||
| ``std::string`` or ``char*`` *without* conversion.  On Python 3, in order to | ||||
| make a function *only* accept ``bytes`` (and not ``str``), declare it as taking | ||||
| a ``py::bytes`` argument. | ||||
| ``std::string`` or ``char*`` *without* conversion.  In order to make a function | ||||
| *only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes`` | ||||
| argument. | ||||
| 
 | ||||
| 
 | ||||
| Returning C++ strings to Python | ||||
|  | @ -204,11 +196,6 @@ decoded to Python ``str``. | |||
|         } | ||||
|     ); | ||||
| 
 | ||||
| .. warning:: | ||||
| 
 | ||||
|     Wide character strings may not work as described on Python 2.7 or Python | ||||
|     3.3 compiled with ``--enable-unicode=ucs2``. | ||||
| 
 | ||||
| Strings in multibyte encodings such as Shift-JIS must transcoded to a | ||||
| UTF-8/16/32 before being returned to Python. | ||||
| 
 | ||||
|  |  | |||
|  | @ -133,14 +133,14 @@ a virtual method call. | |||
|     >>> from example import * | ||||
|     >>> d = Dog() | ||||
|     >>> call_go(d) | ||||
|     u'woof! woof! woof! ' | ||||
|     'woof! woof! woof! ' | ||||
|     >>> class Cat(Animal): | ||||
|     ...     def go(self, n_times): | ||||
|     ...         return "meow! " * n_times | ||||
|     ... | ||||
|     >>> c = Cat() | ||||
|     >>> call_go(c) | ||||
|     u'meow! meow! meow! ' | ||||
|     'meow! meow! meow! ' | ||||
| 
 | ||||
| If you are defining a custom constructor in a derived Python class, you *must* | ||||
| ensure that you explicitly call the bound C++ constructor using ``__init__``, | ||||
|  | @ -813,26 +813,21 @@ An instance can now be pickled as follows: | |||
| 
 | ||||
| .. code-block:: python | ||||
| 
 | ||||
|     try: | ||||
|         import cPickle as pickle  # Use cPickle on Python 2.7 | ||||
|     except ImportError: | ||||
|     import pickle | ||||
| 
 | ||||
|     p = Pickleable("test_value") | ||||
|     p.setExtra(15) | ||||
|     data = pickle.dumps(p, 2) | ||||
|     data = pickle.dumps(p) | ||||
| 
 | ||||
| 
 | ||||
| .. note:: | ||||
|     Note that only the cPickle module is supported on Python 2.7. | ||||
| 
 | ||||
|     The second argument to ``dumps`` is also crucial: it selects the pickle | ||||
|     protocol version 2, since the older version 1 is not supported. Newer | ||||
|     versions are also fine—for instance, specify ``-1`` to always use the | ||||
|     latest available version. Beware: failure to follow these instructions | ||||
|     will cause important pybind11 memory allocation routines to be skipped | ||||
|     during unpickling, which will likely lead to memory corruption and/or | ||||
|     segmentation faults. | ||||
|     If given, the second argument to ``dumps`` must be 2 or larger - 0 and 1 are | ||||
|     not supported. Newer versions are also fine; for instance, specify ``-1`` to | ||||
|     always use the latest available version. Beware: failure to follow these | ||||
|     instructions will cause important pybind11 memory allocation routines to be | ||||
|     skipped during unpickling, which will likely lead to memory corruption | ||||
|     and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and | ||||
|     version 4 for Python 3.8+. | ||||
| 
 | ||||
| .. seealso:: | ||||
| 
 | ||||
|  | @ -849,11 +844,9 @@ Python normally uses references in assignments. Sometimes a real copy is needed | |||
| to prevent changing all copies. The ``copy`` module [#f5]_ provides these | ||||
| capabilities. | ||||
| 
 | ||||
| On Python 3, a class with pickle support is automatically also (deep)copy | ||||
| A class with pickle support is automatically also (deep)copy | ||||
| compatible. However, performance can be improved by adding custom | ||||
| ``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods | ||||
| are mandatory for (deep)copy compatibility, because pybind11 only supports | ||||
| cPickle. | ||||
| ``__copy__`` and ``__deepcopy__`` methods. | ||||
| 
 | ||||
| For simple classes (deep)copy can be enabled by using the copy constructor, | ||||
| which should look as follows: | ||||
|  | @ -1125,13 +1118,6 @@ described trampoline: | |||
|     py::class_<A, Trampoline>(m, "A") // <-- `Trampoline` here | ||||
|         .def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`! | ||||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|     MSVC 2015 has a compiler bug (fixed in version 2017) which | ||||
|     requires a more explicit function binding in the form of | ||||
|     ``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));`` | ||||
|     where ``int (A::*)() const`` is the type of ``A::foo``. | ||||
| 
 | ||||
| Binding final classes | ||||
| ===================== | ||||
| 
 | ||||
|  |  | |||
|  | @ -328,8 +328,8 @@ an invalid state. | |||
| Chaining exceptions ('raise from') | ||||
| ================================== | ||||
| 
 | ||||
| In Python 3.3 a mechanism for indicating that exceptions were caused by other | ||||
| exceptions was introduced: | ||||
| Python has a mechanism for indicating that exceptions were caused by other | ||||
| exceptions: | ||||
| 
 | ||||
| .. code-block:: py | ||||
| 
 | ||||
|  | @ -340,7 +340,7 @@ exceptions was introduced: | |||
| 
 | ||||
| To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It | ||||
| sets the current python error indicator, so to continue propagating the exception | ||||
| you should ``throw py::error_already_set()`` (Python 3 only). | ||||
| you should ``throw py::error_already_set()``. | ||||
| 
 | ||||
| .. code-block:: cpp | ||||
| 
 | ||||
|  |  | |||
|  | @ -372,7 +372,7 @@ like so: | |||
| Keyword-only arguments | ||||
| ====================== | ||||
| 
 | ||||
| Python 3 introduced keyword-only arguments by specifying an unnamed ``*`` | ||||
| Python implements keyword-only arguments by specifying an unnamed ``*`` | ||||
| argument in a function definition: | ||||
| 
 | ||||
| .. code-block:: python | ||||
|  | @ -395,19 +395,18 @@ argument annotations when registering the function: | |||
|     m.def("f", [](int a, int b) { /* ... */ }, | ||||
|           py::arg("a"), py::kw_only(), py::arg("b")); | ||||
| 
 | ||||
| Note that you currently cannot combine this with a ``py::args`` argument.  This | ||||
| feature does *not* require Python 3 to work. | ||||
| 
 | ||||
| .. versionadded:: 2.6 | ||||
| 
 | ||||
| As of pybind11 2.9, a ``py::args`` argument implies that any following arguments | ||||
| are keyword-only, as if ``py::kw_only()`` had been specified in the same | ||||
| relative location of the argument list as the ``py::args`` argument.  The | ||||
| ``py::kw_only()`` may be included to be explicit about this, but is not | ||||
| required.  (Prior to 2.9 ``py::args`` may only occur at the end of the argument | ||||
| list, or immediately before a ``py::kwargs`` argument at the end). | ||||
| A ``py::args`` argument implies that any following arguments are keyword-only, | ||||
| as if ``py::kw_only()`` had been specified in the same relative location of the | ||||
| argument list as the ``py::args`` argument.  The ``py::kw_only()`` may be | ||||
| included to be explicit about this, but is not required. | ||||
| 
 | ||||
| .. versionchanged:: 2.9 | ||||
|    This can now be combined with ``py::args``. Before, ``py::args`` could only | ||||
|    occur at the end of the argument list, or immediately before a ``py::kwargs`` | ||||
|    argument at the end. | ||||
| 
 | ||||
| .. versionadded:: 2.9 | ||||
| 
 | ||||
| Positional-only arguments | ||||
| ========================= | ||||
|  |  | |||
|  | @ -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<Scalar>::format()) | ||||
|                 throw std::runtime_error("Incompatible format: expected a double array!"); | ||||
| 
 | ||||
|  | @ -395,11 +395,9 @@ uses of ``py::array``: | |||
| Ellipsis | ||||
| ======== | ||||
| 
 | ||||
| Python 3 provides a convenient ``...`` ellipsis notation that is often used to | ||||
| Python provides a convenient ``...`` ellipsis notation that is often used to | ||||
| slice multidimensional arrays. For instance, the following snippet extracts the | ||||
| middle dimensions of a tensor with the first and last index set to zero. | ||||
| In Python 2, the syntactic sugar ``...`` is not available, but the singleton | ||||
| ``Ellipsis`` (of type ``ellipsis``) can still be used directly. | ||||
| 
 | ||||
| .. code-block:: python | ||||
| 
 | ||||
|  | @ -414,8 +412,6 @@ operation on the C++ side: | |||
|    py::array a = /* A NumPy array */; | ||||
|    py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; | ||||
| 
 | ||||
| .. versionchanged:: 2.6 | ||||
|    ``py::ellipsis()`` is now also available in Python 2. | ||||
| 
 | ||||
| Memory view | ||||
| =========== | ||||
|  | @ -455,9 +451,5 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer: | |||
|         ); | ||||
|     }) | ||||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|     ``memoryview::from_memory`` is not available in Python 2. | ||||
| 
 | ||||
| .. versionchanged:: 2.6 | ||||
|     ``memoryview::from_memory`` added. | ||||
|  |  | |||
|  | @ -32,8 +32,7 @@ The last line will both compile and run the tests. | |||
| Windows | ||||
| ------- | ||||
| 
 | ||||
| On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies | ||||
| on various C++11 language features that break older versions of Visual Studio. | ||||
| On Windows, only **Visual Studio 2017** and newer are supported. | ||||
| 
 | ||||
| .. Note:: | ||||
| 
 | ||||
|  | @ -166,12 +165,12 @@ load and execute the example: | |||
| .. code-block:: pycon | ||||
| 
 | ||||
|     $ python | ||||
|     Python 2.7.10 (default, Aug 22 2015, 20:33:39) | ||||
|     [GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin | ||||
|     Python 3.9.10 (main, Jan 15 2022, 11:48:04) | ||||
|     [Clang 13.0.0 (clang-1300.0.29.3)] on darwin | ||||
|     Type "help", "copyright", "credits" or "license" for more information. | ||||
|     >>> import example | ||||
|     >>> example.add(1, 2) | ||||
|     3L | ||||
|     3 | ||||
|     >>> | ||||
| 
 | ||||
| .. _keyword_args: | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import datetime as dt | ||||
| import os | ||||
| import random | ||||
|  | @ -12,20 +11,20 @@ def generate_dummy_code_pybind11(nclasses=10): | |||
|     bindings = "" | ||||
| 
 | ||||
|     for cl in range(nclasses): | ||||
|         decl += "class cl%03i;\n" % cl | ||||
|         decl += f"class cl{cl:03};\n" | ||||
|     decl += "\n" | ||||
| 
 | ||||
|     for cl in range(nclasses): | ||||
|         decl += "class cl%03i {\n" % cl | ||||
|         decl += f"class {cl:03} {{\n" | ||||
|         decl += "public:\n" | ||||
|         bindings += '    py::class_<cl%03i>(m, "cl%03i")\n' % (cl, cl) | ||||
|         bindings += f'    py::class_<cl{cl:03}>(m, "cl{cl:03}")\n' | ||||
|         for fn in range(nfns): | ||||
|             ret = random.randint(0, nclasses - 1) | ||||
|             params = [random.randint(0, nclasses - 1) for i in range(nargs)] | ||||
|             decl += "    cl%03i *fn_%03i(" % (ret, fn) | ||||
|             decl += ", ".join("cl%03i *" % p for p in params) | ||||
|             decl += f"    cl{ret:03} *fn_{fn:03}(" | ||||
|             decl += ", ".join(f"cl{p:03} *" for p in params) | ||||
|             decl += ");\n" | ||||
|             bindings += '        .def("fn_%03i", &cl%03i::fn_%03i)\n' % (fn, cl, fn) | ||||
|             bindings += f'        .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n' | ||||
|         decl += "};\n\n" | ||||
|         bindings += "        ;\n" | ||||
| 
 | ||||
|  | @ -43,23 +42,20 @@ def generate_dummy_code_boost(nclasses=10): | |||
|     bindings = "" | ||||
| 
 | ||||
|     for cl in range(nclasses): | ||||
|         decl += "class cl%03i;\n" % cl | ||||
|         decl += f"class cl{cl:03};\n" | ||||
|     decl += "\n" | ||||
| 
 | ||||
|     for cl in range(nclasses): | ||||
|         decl += "class cl%03i {\n" % cl | ||||
|         decl += "public:\n" | ||||
|         bindings += '    py::class_<cl%03i>("cl%03i")\n' % (cl, cl) | ||||
|         bindings += f'    py::class_<cl{cl:03}>("cl{cl:03}")\n' | ||||
|         for fn in range(nfns): | ||||
|             ret = random.randint(0, nclasses - 1) | ||||
|             params = [random.randint(0, nclasses - 1) for i in range(nargs)] | ||||
|             decl += "    cl%03i *fn_%03i(" % (ret, fn) | ||||
|             decl += ", ".join("cl%03i *" % p for p in params) | ||||
|             decl += f"    cl{ret:03} *fn_{fn:03}(" | ||||
|             decl += ", ".join(f"cl{p:03} *" for p in params) | ||||
|             decl += ");\n" | ||||
|             bindings += ( | ||||
|                 '        .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy<py::manage_new_object>())\n' | ||||
|                 % (fn, cl, fn) | ||||
|             ) | ||||
|             bindings += f'        .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy<py::manage_new_object>())\n' | ||||
|         decl += "};\n\n" | ||||
|         bindings += "        ;\n" | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,6 +6,272 @@ Changelog | |||
| Starting with version 1.8.0, pybind11 releases use a `semantic versioning | ||||
| <http://semver.org>`_ policy. | ||||
| 
 | ||||
| Changes will be added here periodically from the "Suggested changelog entry" | ||||
| block in pull request descriptions. | ||||
| 
 | ||||
| Version 2.10.0 (Jul 15, 2022) | ||||
| ----------------------------- | ||||
| 
 | ||||
| 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. Initial support added for Python 3.11. | ||||
| 
 | ||||
| New features: | ||||
| 
 | ||||
| * ``py::anyset`` & ``py::frozenset`` were added, with copying (cast) to | ||||
|   ``std::set`` (similar to ``set``). | ||||
|   `#3901 <https://github.com/pybind/pybind11/pull/3901>`_ | ||||
| 
 | ||||
| * Support bytearray casting to string. | ||||
|   `#3707 <https://github.com/pybind/pybind11/pull/3707>`_ | ||||
| 
 | ||||
| * ``type_caster<std::monostate>`` 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 <https://github.com/pybind/pybind11/pull/3818>`_ | ||||
| 
 | ||||
| * ``pybind11::capsule::set_name`` added to mutate the name of the capsule instance. | ||||
|   `#3866 <https://github.com/pybind/pybind11/pull/3866>`_ | ||||
| 
 | ||||
| * NumPy: dtype constructor from type number added, accessors corresponding to | ||||
|   Python API ``dtype.num``, ``dtype.byteorder``, ``dtype.flags`` and | ||||
|   ``dtype.alignment`` added. | ||||
|   `#3868 <https://github.com/pybind/pybind11/pull/3868>`_ | ||||
| 
 | ||||
| 
 | ||||
| Changes: | ||||
| 
 | ||||
| * Python 3.6 is now the minimum supported version. | ||||
|   `#3688 <https://github.com/pybind/pybind11/pull/3688>`_ | ||||
|   `#3719 <https://github.com/pybind/pybind11/pull/3719>`_ | ||||
| 
 | ||||
| * The minimum version for MSVC is now 2017. | ||||
|   `#3722 <https://github.com/pybind/pybind11/pull/3722>`_ | ||||
| 
 | ||||
| * Fix issues with CPython 3.11 betas and add to supported test matrix. | ||||
|   `#3923 <https://github.com/pybind/pybind11/pull/3923>`_ | ||||
| 
 | ||||
| * ``error_already_set`` is now safer and more performant, especially for | ||||
|   exceptions with long tracebacks, by delaying computation. | ||||
|   `#1895 <https://github.com/pybind/pybind11/pull/1895>`_ | ||||
| 
 | ||||
| * Improve exception handling in python ``str`` bindings. | ||||
|   `#3826 <https://github.com/pybind/pybind11/pull/3826>`_ | ||||
| 
 | ||||
| * The bindings for capsules now have more consistent exception handling. | ||||
|   `#3825 <https://github.com/pybind/pybind11/pull/3825>`_ | ||||
| 
 | ||||
| * ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can now be | ||||
|   used to define classes in namespaces other than pybind11. | ||||
|   `#3797 <https://github.com/pybind/pybind11/pull/3797>`_ | ||||
| 
 | ||||
| * Error printing code now uses ``PYBIND11_DETAILED_ERROR_MESSAGES`` instead of | ||||
|   requiring ``NDEBUG``, allowing use with release builds if desired. | ||||
|   `#3913 <https://github.com/pybind/pybind11/pull/3913>`_ | ||||
| 
 | ||||
| * Implicit conversion of the literal ``0`` to ``pybind11::handle`` is now disabled. | ||||
|   `#4008 <https://github.com/pybind/pybind11/pull/4008>`_ | ||||
| 
 | ||||
| 
 | ||||
| Bug fixes: | ||||
| 
 | ||||
| * Fix exception handling when ``pybind11::weakref()`` fails. | ||||
|   `#3739 <https://github.com/pybind/pybind11/pull/3739>`_ | ||||
| 
 | ||||
| * ``module_::def_submodule`` was missing proper error handling. This is fixed now. | ||||
|   `#3973 <https://github.com/pybind/pybind11/pull/3973>`_ | ||||
| 
 | ||||
| * The behavior or ``error_already_set`` was made safer and the highly opaque | ||||
|   "Unknown internal error occurred" message was replaced with a more helpful | ||||
|   message. | ||||
|   `#3982 <https://github.com/pybind/pybind11/pull/3982>`_ | ||||
| 
 | ||||
| * ``error_already_set::what()`` now handles non-normalized exceptions correctly. | ||||
|   `#3971 <https://github.com/pybind/pybind11/pull/3971>`_ | ||||
| 
 | ||||
| * Support older C++ compilers where filesystem is not yet part of the standard | ||||
|   library and is instead included in ``std::experimental::filesystem``. | ||||
|   `#3840 <https://github.com/pybind/pybind11/pull/3840>`_ | ||||
| 
 | ||||
| * Fix ``-Wfree-nonheap-object`` warnings produced by GCC by avoiding returning | ||||
|   pointers to static objects with ``return_value_policy::take_ownership``. | ||||
|   `#3946 <https://github.com/pybind/pybind11/pull/3946>`_ | ||||
| 
 | ||||
| * Fix cast from pytype rvalue to another pytype. | ||||
|   `#3949 <https://github.com/pybind/pybind11/pull/3949>`_ | ||||
| 
 | ||||
| * Ensure proper behavior when garbage collecting classes with dynamic attributes in Python >=3.9. | ||||
|   `#4051 <https://github.com/pybind/pybind11/pull/4051>`_ | ||||
| 
 | ||||
| * A couple long-standing ``PYBIND11_NAMESPACE`` | ||||
|   ``__attribute__((visibility("hidden")))`` inconsistencies are now fixed | ||||
|   (affects only unusual environments). | ||||
|   `#4043 <https://github.com/pybind/pybind11/pull/4043>`_ | ||||
| 
 | ||||
| * ``pybind11::detail::get_internals()`` is now resilient to in-flight Python | ||||
|   exceptions. | ||||
|   `#3981 <https://github.com/pybind/pybind11/pull/3981>`_ | ||||
| 
 | ||||
| * Arrays with a dimension of size 0 are now properly converted to dynamic Eigen | ||||
|   matrices (more common in NumPy 1.23). | ||||
|   `#4038 <https://github.com/pybind/pybind11/pull/4038>`_ | ||||
| 
 | ||||
| * Avoid catching unrelated errors when importing NumPy. | ||||
|   `#3974 <https://github.com/pybind/pybind11/pull/3974>`_ | ||||
| 
 | ||||
| Performance and style: | ||||
| 
 | ||||
| * Added an accessor overload of ``(object &&key)`` to reference steal the | ||||
|   object when using python types as keys. This prevents unnecessary reference | ||||
|   count overhead for attr, dictionary, tuple, and sequence look ups. Added | ||||
|   additional regression tests. Fixed a performance bug the caused accessor | ||||
|   assignments to potentially perform unnecessary copies. | ||||
|   `#3970 <https://github.com/pybind/pybind11/pull/3970>`_ | ||||
| 
 | ||||
| * Perfect forward all args of ``make_iterator``. | ||||
|   `#3980 <https://github.com/pybind/pybind11/pull/3980>`_ | ||||
| 
 | ||||
| * Avoid potential bug in pycapsule destructor by adding an ``error_guard`` to | ||||
|   one of the dtors. | ||||
|   `#3958 <https://github.com/pybind/pybind11/pull/3958>`_ | ||||
| 
 | ||||
| * Optimize dictionary access in ``strip_padding`` for numpy. | ||||
|   `#3994 <https://github.com/pybind/pybind11/pull/3994>`_ | ||||
| 
 | ||||
| * ``stl_bind.h`` bindings now take slice args as a const-ref. | ||||
|   `#3852 <https://github.com/pybind/pybind11/pull/3852>`_ | ||||
| 
 | ||||
| * Made slice constructor more consistent, and improve performance of some | ||||
|   casters by allowing reference stealing. | ||||
|   `#3845 <https://github.com/pybind/pybind11/pull/3845>`_ | ||||
| 
 | ||||
| * Change numpy dtype from_args method to use const ref. | ||||
|   `#3878 <https://github.com/pybind/pybind11/pull/3878>`_ | ||||
| 
 | ||||
| * Follow rule of three to ensure ``PyErr_Restore`` is called only once. | ||||
|   `#3872 <https://github.com/pybind/pybind11/pull/3872>`_ | ||||
| 
 | ||||
| * Added missing perfect forwarding for ``make_iterator`` functions. | ||||
|   `#3860 <https://github.com/pybind/pybind11/pull/3860>`_ | ||||
| 
 | ||||
| * Optimize c++ to python function casting by using the rvalue caster. | ||||
|   `#3966 <https://github.com/pybind/pybind11/pull/3966>`_ | ||||
| 
 | ||||
| * Optimize Eigen sparse matrix casting by removing unnecessary temporary. | ||||
|   `#4064 <https://github.com/pybind/pybind11/pull/4064>`_ | ||||
| 
 | ||||
| * Avoid potential implicit copy/assignment constructors causing double free in | ||||
|   ``strdup_gaurd``. | ||||
|   `#3905 <https://github.com/pybind/pybind11/pull/3905>`_ | ||||
| 
 | ||||
| * Enable clang-tidy checks ``misc-definitions-in-headers``, | ||||
|   ``modernize-loop-convert``, and ``modernize-use-nullptr``. | ||||
|   `#3881 <https://github.com/pybind/pybind11/pull/3881>`_ | ||||
|   `#3988 <https://github.com/pybind/pybind11/pull/3988>`_ | ||||
| 
 | ||||
| 
 | ||||
| Build system improvements: | ||||
| 
 | ||||
| * CMake: Fix file extension on Windows with cp36 and cp37 using FindPython. | ||||
|   `#3919 <https://github.com/pybind/pybind11/pull/3919>`_ | ||||
| 
 | ||||
| * CMake: Support multiple Python targets (such as on vcpkg). | ||||
|   `#3948 <https://github.com/pybind/pybind11/pull/3948>`_ | ||||
| 
 | ||||
| * CMake: Fix issue with NVCC on Windows. | ||||
|   `#3947 <https://github.com/pybind/pybind11/pull/3947>`_ | ||||
| 
 | ||||
| * CMake: Drop the bitness check on cross compiles (like targeting WebAssembly | ||||
|   via Emscripten). | ||||
|   `#3959 <https://github.com/pybind/pybind11/pull/3959>`_ | ||||
| 
 | ||||
| * Add MSVC builds in debug mode to CI. | ||||
|   `#3784 <https://github.com/pybind/pybind11/pull/3784>`_ | ||||
| 
 | ||||
| * MSVC 2022 C++20 coverage was added to GitHub Actions, including Eigen. | ||||
|   `#3732 <https://github.com/pybind/pybind11/pull/3732>`_, | ||||
|   `#3741 <https://github.com/pybind/pybind11/pull/3741>`_ | ||||
| 
 | ||||
| 
 | ||||
| Backend and tidying up: | ||||
| 
 | ||||
| * New theme for the documentation. | ||||
|   `#3109 <https://github.com/pybind/pybind11/pull/3109>`_ | ||||
| 
 | ||||
| * Remove idioms in code comments.  Use more inclusive language. | ||||
|   `#3809 <https://github.com/pybind/pybind11/pull/3809>`_ | ||||
| 
 | ||||
| * ``#include <iostream>`` was removed from the ``pybind11/stl.h`` header. Your | ||||
|   project may break if it has a transitive dependency on this include. The fix | ||||
|   is to "Include What You Use". | ||||
|   `#3928 <https://github.com/pybind/pybind11/pull/3928>`_ | ||||
| 
 | ||||
| * Avoid ``setup.py <command>`` usage in internal tests. | ||||
|   `#3734 <https://github.com/pybind/pybind11/pull/3734>`_ | ||||
| 
 | ||||
| 
 | ||||
| Version 2.9.2 (Mar 29, 2022) | ||||
| ---------------------------- | ||||
| 
 | ||||
| Changes: | ||||
| 
 | ||||
| * Enum now has an ``__index__`` method on Python <3.8 too. | ||||
|   `#3700 <https://github.com/pybind/pybind11/pull/3700>`_ | ||||
| 
 | ||||
| * Local internals are now cleared after finalizing the interpreter. | ||||
|   `#3744 <https://github.com/pybind/pybind11/pull/3744>`_ | ||||
| 
 | ||||
| Bug fixes: | ||||
| 
 | ||||
| * Better support for Python 3.11 alphas. | ||||
|   `#3694 <https://github.com/pybind/pybind11/pull/3694>`_ | ||||
| 
 | ||||
| * ``PYBIND11_TYPE_CASTER`` now uses fully qualified symbols, so it can be used | ||||
|   outside of ``pybind11::detail``. | ||||
|   `#3758 <https://github.com/pybind/pybind11/pull/3758>`_ | ||||
| 
 | ||||
| * Some fixes for PyPy 3.9. | ||||
|   `#3768 <https://github.com/pybind/pybind11/pull/3768>`_ | ||||
| 
 | ||||
| * Fixed a potential memleak in PyPy in ``get_type_override``. | ||||
|   `#3774 <https://github.com/pybind/pybind11/pull/3774>`_ | ||||
| 
 | ||||
| * Fix usage of ``VISIBILITY_INLINES_HIDDEN``. | ||||
|   `#3721 <https://github.com/pybind/pybind11/pull/3721>`_ | ||||
| 
 | ||||
| 
 | ||||
| Build system improvements: | ||||
| 
 | ||||
| * Uses ``sysconfig`` module to determine installation locations on Python >= | ||||
|   3.10, instead of ``distutils`` which has been deprecated. | ||||
|   `#3764 <https://github.com/pybind/pybind11/pull/3764>`_ | ||||
| 
 | ||||
| * Support Catch 2.13.5+ (supporting GLIBC 2.34+). | ||||
|   `#3679 <https://github.com/pybind/pybind11/pull/3679>`_ | ||||
| 
 | ||||
| * Fix test failures with numpy 1.22 by ignoring whitespace when comparing | ||||
|   ``str()`` of dtypes. | ||||
|   `#3682 <https://github.com/pybind/pybind11/pull/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 <https://github.com/pybind/pybind11/pull/3702>`_, | ||||
|   `#3699 <https://github.com/pybind/pybind11/pull/3699>`_, | ||||
|   `#3716 <https://github.com/pybind/pybind11/pull/3716>`_, | ||||
|   `#3709 <https://github.com/pybind/pybind11/pull/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 <https://github.com/pybind/pybind11/pull/3713>`_ | ||||
| 
 | ||||
| 
 | ||||
| Version 2.9.1 (Feb 2, 2022) | ||||
| --------------------------- | ||||
| 
 | ||||
|  | @ -794,7 +1060,7 @@ Packaging / building improvements: | |||
|   `#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and | ||||
|   `#2370 <https://github.com/pybind/pybind11/pull/2370>`_ | ||||
| 
 | ||||
|   * Full integration with CMake’s C++ standard system and compile features | ||||
|   * Full integration with CMake's C++ standard system and compile features | ||||
|     replaces ``PYBIND11_CPP_STANDARD``. | ||||
| 
 | ||||
|   * Generated config file is now portable to different Python/compiler/CMake | ||||
|  |  | |||
|  | @ -48,10 +48,10 @@ interactive Python session demonstrating this example is shown below: | |||
|     >>> print(p) | ||||
|     <example.Pet object at 0x10cd98060> | ||||
|     >>> p.getName() | ||||
|     u'Molly' | ||||
|     'Molly' | ||||
|     >>> p.setName("Charly") | ||||
|     >>> p.getName() | ||||
|     u'Charly' | ||||
|     'Charly' | ||||
| 
 | ||||
| .. seealso:: | ||||
| 
 | ||||
|  | @ -124,10 +124,10 @@ This makes it possible to write | |||
| 
 | ||||
|     >>> p = example.Pet("Molly") | ||||
|     >>> p.name | ||||
|     u'Molly' | ||||
|     'Molly' | ||||
|     >>> p.name = "Charly" | ||||
|     >>> p.name | ||||
|     u'Charly' | ||||
|     'Charly' | ||||
| 
 | ||||
| Now suppose that ``Pet::name`` was a private internal variable | ||||
| that can only be accessed via setters and getters. | ||||
|  | @ -282,9 +282,9 @@ expose fields and methods of both types: | |||
| 
 | ||||
|     >>> p = example.Dog("Molly") | ||||
|     >>> p.name | ||||
|     u'Molly' | ||||
|     'Molly' | ||||
|     >>> p.bark() | ||||
|     u'woof!' | ||||
|     'woof!' | ||||
| 
 | ||||
| The C++ classes defined above are regular non-polymorphic types with an | ||||
| inheritance relationship. This is reflected in Python: | ||||
|  | @ -332,7 +332,7 @@ will automatically recognize this: | |||
|     >>> type(p) | ||||
|     PolymorphicDog  # automatically downcast | ||||
|     >>> p.bark() | ||||
|     u'woof!' | ||||
|     'woof!' | ||||
| 
 | ||||
| Given a pointer to a polymorphic base, pybind11 performs automatic downcasting | ||||
| to the actual derived type. Note that this goes beyond the usual situation in | ||||
|  | @ -434,8 +434,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth | |||
|         .def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age") | ||||
|         .def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name"); | ||||
| 
 | ||||
| .. [#cpp14] A compiler which supports the ``-std=c++14`` flag | ||||
|             or Visual Studio 2015 Update 2 and newer. | ||||
| .. [#cpp14] A compiler which supports the ``-std=c++14`` flag. | ||||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|  | @ -483,7 +482,7 @@ The binding code for this example looks as follows: | |||
|         .value("Cat", Pet::Kind::Cat) | ||||
|         .export_values(); | ||||
| 
 | ||||
|     py::class_<Pet::Attributes> attributes(pet, "Attributes") | ||||
|     py::class_<Pet::Attributes>(pet, "Attributes") | ||||
|         .def(py::init<>()) | ||||
|         .def_readwrite("age", &Pet::Attributes::age); | ||||
| 
 | ||||
|  |  | |||
|  | @ -417,10 +417,10 @@ existing targets instead: | |||
| 
 | ||||
| .. code-block:: cmake | ||||
| 
 | ||||
|     cmake_minimum_required(VERSION 3.15...3.19) | ||||
|     cmake_minimum_required(VERSION 3.15...3.22) | ||||
|     project(example LANGUAGES CXX) | ||||
| 
 | ||||
|     find_package(Python COMPONENTS Interpreter Development REQUIRED) | ||||
|     find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED) | ||||
|     find_package(pybind11 CONFIG REQUIRED) | ||||
|     # or add_subdirectory(pybind11) | ||||
| 
 | ||||
|  | @ -433,9 +433,8 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``. | |||
| 
 | ||||
| .. warning:: | ||||
| 
 | ||||
|     If you use FindPython2 and FindPython3 to dual-target Python, use the | ||||
|     individual targets listed below, and avoid targets that directly include | ||||
|     Python parts. | ||||
|     If you use FindPython to multi-target Python versions, use the individual | ||||
|     targets listed below, and avoid targets that directly include Python parts. | ||||
| 
 | ||||
| There are `many ways to hint or force a discovery of a specific Python | ||||
| installation <https://cmake.org/cmake/help/latest/module/FindPython.html>`_), | ||||
|  | @ -462,11 +461,8 @@ available in all modes. The targets provided are: | |||
|    ``pybind11::headers`` | ||||
|      Just the pybind11 headers and minimum compile requirements | ||||
| 
 | ||||
|    ``pybind11::python2_no_register`` | ||||
|      Quiets the warning/error when mixing C++14 or higher and Python 2 | ||||
| 
 | ||||
|    ``pybind11::pybind11`` | ||||
|      Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only) | ||||
|      Python headers + ``pybind11::headers`` | ||||
| 
 | ||||
|    ``pybind11::python_link_helper`` | ||||
|      Just the "linking" part of pybind11:module | ||||
|  | @ -475,7 +471,7 @@ available in all modes. The targets provided are: | |||
|      Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` | ||||
| 
 | ||||
|    ``pybind11::embed`` | ||||
|      Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Embed`` (FindPython) or Python libs | ||||
|      Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs | ||||
| 
 | ||||
|    ``pybind11::lto`` / ``pybind11::thin_lto`` | ||||
|      An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization. | ||||
|  | @ -509,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) | ||||
|     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") | ||||
|  | @ -577,21 +576,12 @@ On Linux, you can compile an example such as the one given in | |||
| 
 | ||||
|     $ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) | ||||
| 
 | ||||
| The flags given here assume that you're using Python 3. For Python 2, just | ||||
| change the executable appropriately (to ``python`` or ``python2``). | ||||
| 
 | ||||
| The ``python3 -m pybind11 --includes`` command fetches the include paths for | ||||
| both pybind11 and Python headers. This assumes that pybind11 has been installed | ||||
| using ``pip`` or ``conda``. If it hasn't, you can also manually specify | ||||
| ``-I <path-to-pybind11>/include`` together with the Python includes path | ||||
| ``python3-config --includes``. | ||||
| 
 | ||||
| Note that Python 2.7 modules don't use a special suffix, so you should simply | ||||
| use ``example.so`` instead of ``example$(python3-config --extension-suffix)``. | ||||
| Besides, the ``--extension-suffix`` option may or may not be available, depending | ||||
| on the distribution; in the latter case, the module extension can be manually | ||||
| set to ``.so``. | ||||
| 
 | ||||
| On macOS: the build command is almost the same but it also requires passing | ||||
| the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when | ||||
| building the module: | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # pybind11 documentation build configuration file, created by | ||||
| # sphinx-quickstart on Sun Oct 11 19:23:48 2015. | ||||
|  | @ -36,6 +35,7 @@ DIR = Path(__file__).parent.resolve() | |||
| # ones. | ||||
| extensions = [ | ||||
|     "breathe", | ||||
|     "sphinx_copybutton", | ||||
|     "sphinxcontrib.rsvgconverter", | ||||
|     "sphinxcontrib.moderncmakedomain", | ||||
| ] | ||||
|  | @ -126,23 +126,7 @@ todo_include_todos = False | |||
| # The theme to use for HTML and HTML Help pages.  See the documentation for | ||||
| # a list of builtin themes. | ||||
| 
 | ||||
| on_rtd = os.environ.get("READTHEDOCS", None) == "True" | ||||
| 
 | ||||
| if not on_rtd:  # only import and set the theme if we're building docs locally | ||||
|     import sphinx_rtd_theme | ||||
| 
 | ||||
|     html_theme = "sphinx_rtd_theme" | ||||
|     html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] | ||||
| 
 | ||||
|     html_context = {"css_files": ["_static/theme_overrides.css"]} | ||||
| else: | ||||
|     html_context = { | ||||
|         "css_files": [ | ||||
|             "//media.readthedocs.org/css/sphinx_rtd_theme.css", | ||||
|             "//media.readthedocs.org/css/readthedocs-doc-embed.css", | ||||
|             "_static/theme_overrides.css", | ||||
|         ] | ||||
|     } | ||||
| html_theme = "furo" | ||||
| 
 | ||||
| # Theme options are theme-specific and customize the look and feel of a theme | ||||
| # further.  For a list of options available for each theme, see the | ||||
|  | @ -173,6 +157,10 @@ else: | |||
| # so a file named "default.css" will overwrite the builtin "default.css". | ||||
| html_static_path = ["_static"] | ||||
| 
 | ||||
| html_css_files = [ | ||||
|     "css/custom.css", | ||||
| ] | ||||
| 
 | ||||
| # Add any extra paths that contain custom files (such as robots.txt or | ||||
| # .htaccess) here, relative to this directory. These files are copied | ||||
| # directly to the root of the documentation. | ||||
|  | @ -345,9 +333,9 @@ def generate_doxygen_xml(app): | |||
|         subprocess.call(["doxygen", "--version"]) | ||||
|         retcode = subprocess.call(["doxygen"], cwd=app.confdir) | ||||
|         if retcode < 0: | ||||
|             sys.stderr.write("doxygen error code: {}\n".format(-retcode)) | ||||
|             sys.stderr.write(f"doxygen error code: {-retcode}\n") | ||||
|     except OSError as e: | ||||
|         sys.stderr.write("doxygen execution failed: {}\n".format(e)) | ||||
|         sys.stderr.write(f"doxygen execution failed: {e}\n") | ||||
| 
 | ||||
| 
 | ||||
| def prepare(app): | ||||
|  |  | |||
|  | @ -8,9 +8,7 @@ Frequently asked questions | |||
| filename of the extension library (without suffixes such as ``.so``). | ||||
| 
 | ||||
| 2. If the above did not fix the issue, you are likely using an incompatible | ||||
| version of Python (for instance, the extension library was compiled against | ||||
| Python 2, while the interpreter is running on top of some version of Python | ||||
| 3, or vice versa). | ||||
| version of Python that does not match what you compiled with. | ||||
| 
 | ||||
| "Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" | ||||
| ======================================================================== | ||||
|  | @ -147,7 +145,7 @@ using C++14 template metaprogramming. | |||
| 
 | ||||
| .. _`faq:hidden_visibility`: | ||||
| 
 | ||||
| "‘SomeClass’ declared with greater visibility than the type of its field ‘SomeClass::member’ [-Wattributes]" | ||||
| "'SomeClass' declared with greater visibility than the type of its field 'SomeClass::member' [-Wattributes]" | ||||
| ============================================================================================================ | ||||
| 
 | ||||
| This error typically indicates that you are compiling without the required | ||||
|  | @ -222,20 +220,6 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids | |||
| potential serious issues when loading multiple modules and is required for | ||||
| proper pybind operation.  See the previous FAQ entry for more details. | ||||
| 
 | ||||
| Working with ancient Visual Studio 2008 builds on Windows | ||||
| ========================================================= | ||||
| 
 | ||||
| The official Windows distributions of Python are compiled using truly | ||||
| ancient versions of Visual Studio that lack good C++11 support. Some users | ||||
| implicitly assume that it would be impossible to load a plugin built with | ||||
| Visual Studio 2015 into a Python distribution that was compiled using Visual | ||||
| Studio 2008. However, no such issue exists: it's perfectly legitimate to | ||||
| interface DLLs that are built with different compilers and/or C libraries. | ||||
| Common gotchas to watch out for involve not ``free()``-ing memory region | ||||
| that that were ``malloc()``-ed in another shared library, using data | ||||
| structures with incompatible ABIs, and so on. pybind11 is very careful not | ||||
| to make these types of mistakes. | ||||
| 
 | ||||
| How can I properly handle Ctrl-C in long-running functions? | ||||
| =========================================================== | ||||
| 
 | ||||
|  | @ -289,27 +273,7 @@ Conflicts can arise, however, when using pybind11 in a project that *also* uses | |||
| the CMake Python detection in a system with several Python versions installed. | ||||
| 
 | ||||
| This difference may cause inconsistencies and errors if *both* mechanisms are | ||||
| used in the same project. Consider the following CMake code executed in a | ||||
| system with Python 2.7 and 3.x installed: | ||||
| 
 | ||||
| .. code-block:: cmake | ||||
| 
 | ||||
|     find_package(PythonInterp) | ||||
|     find_package(PythonLibs) | ||||
|     find_package(pybind11) | ||||
| 
 | ||||
| It will detect Python 2.7 and pybind11 will pick it as well. | ||||
| 
 | ||||
| In contrast this code: | ||||
| 
 | ||||
| .. code-block:: cmake | ||||
| 
 | ||||
|     find_package(pybind11) | ||||
|     find_package(PythonInterp) | ||||
|     find_package(PythonLibs) | ||||
| 
 | ||||
| will detect Python 3.x for pybind11 and may crash on | ||||
| ``find_package(PythonLibs)`` afterwards. | ||||
| used in the same project. | ||||
| 
 | ||||
| There are three possible solutions: | ||||
| 
 | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 60 KiB | 
|  | @ -1,5 +1,6 @@ | |||
| breathe==4.31.0 | ||||
| sphinx==3.5.4 | ||||
| sphinx_rtd_theme==1.0.0 | ||||
| sphinxcontrib-moderncmakedomain==3.19 | ||||
| sphinxcontrib-svg2pdfconverter==1.1.1 | ||||
| breathe==4.34.0 | ||||
| furo==2022.6.21 | ||||
| sphinx==5.0.2 | ||||
| sphinx-copybutton==0.5.0 | ||||
| sphinxcontrib-moderncmakedomain==3.21.4 | ||||
| sphinxcontrib-svg2pdfconverter==1.2.0 | ||||
|  |  | |||
|  | @ -524,7 +524,7 @@ include a declaration of the form: | |||
| 
 | ||||
|     PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>) | ||||
| 
 | ||||
| Continuing to do so won’t cause an error or even a deprecation warning, | ||||
| Continuing to do so won't cause an error or even a deprecation warning, | ||||
| but it's completely redundant. | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "detail/common.h" | ||||
| #include "cast.h" | ||||
| 
 | ||||
| #include <functional> | ||||
|  | @ -20,7 +21,8 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | |||
| /// @{
 | ||||
| 
 | ||||
| /// Annotation for methods
 | ||||
| struct is_method { handle class_; | ||||
| struct is_method { | ||||
|     handle class_; | ||||
|     explicit is_method(const handle &c) : class_(c) {} | ||||
| }; | ||||
| 
 | ||||
|  | @ -31,34 +33,41 @@ struct is_operator { }; | |||
| struct is_final {}; | ||||
| 
 | ||||
| /// Annotation for parent scope
 | ||||
| struct scope { handle value; | ||||
| struct scope { | ||||
|     handle value; | ||||
|     explicit scope(const handle &s) : value(s) {} | ||||
| }; | ||||
| 
 | ||||
| /// Annotation for documentation
 | ||||
| struct doc { const char *value; | ||||
| struct doc { | ||||
|     const char *value; | ||||
|     explicit doc(const char *value) : value(value) {} | ||||
| }; | ||||
| 
 | ||||
| /// Annotation for function names
 | ||||
| struct name { const char *value; | ||||
| struct name { | ||||
|     const char *value; | ||||
|     explicit name(const char *value) : value(value) {} | ||||
| }; | ||||
| 
 | ||||
| /// Annotation indicating that a function is an overload associated with a given "sibling"
 | ||||
| struct sibling { handle value; | ||||
| struct sibling { | ||||
|     handle value; | ||||
|     explicit sibling(const handle &value) : value(value.ptr()) {} | ||||
| }; | ||||
| 
 | ||||
| /// Annotation indicating that a class derives from another given type
 | ||||
| template <typename T> struct base { | ||||
| template <typename T> | ||||
| struct base { | ||||
| 
 | ||||
|     PYBIND11_DEPRECATED("base<T>() was deprecated in favor of specifying 'T' as a template argument to class_") | ||||
|     base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
 | ||||
|     PYBIND11_DEPRECATED( | ||||
|         "base<T>() was deprecated in favor of specifying 'T' as a template argument to class_") | ||||
|     base() = default; | ||||
| }; | ||||
| 
 | ||||
| /// Keep patient alive while nurse lives
 | ||||
| template <size_t Nurse, size_t Patient> struct keep_alive { }; | ||||
| template <size_t Nurse, size_t Patient> | ||||
| struct keep_alive {}; | ||||
| 
 | ||||
| /// Annotation indicating that a class is involved in a multiple inheritance relationship
 | ||||
| struct multiple_inheritance {}; | ||||
|  | @ -74,8 +83,7 @@ struct metaclass { | |||
|     handle value; | ||||
| 
 | ||||
|     PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") | ||||
|     // NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
 | ||||
|     metaclass() {} | ||||
|     metaclass() = default; | ||||
| 
 | ||||
|     /// Override pybind11's default metaclass
 | ||||
|     explicit metaclass(handle value) : value(value) {} | ||||
|  | @ -99,7 +107,8 @@ struct custom_type_setup { | |||
| }; | ||||
| 
 | ||||
| /// Annotation that marks a class as local to the module:
 | ||||
| struct module_local { const bool value; | ||||
| struct module_local { | ||||
|     const bool value; | ||||
|     constexpr explicit module_local(bool v = true) : value(v) {} | ||||
| }; | ||||
| 
 | ||||
|  | @ -127,9 +136,13 @@ struct prepend { }; | |||
|             return foo(args...); // forwarded arguments
 | ||||
|         }); | ||||
|  \endrst */ | ||||
| template <typename... Ts> struct call_guard; | ||||
| template <typename... Ts> | ||||
| struct call_guard; | ||||
| 
 | ||||
| template <> struct call_guard<> { using type = detail::void_type; }; | ||||
| template <> | ||||
| struct call_guard<> { | ||||
|     using type = detail::void_type; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct call_guard<T> { | ||||
|  | @ -154,7 +167,8 @@ PYBIND11_NAMESPACE_BEGIN(detail) | |||
| enum op_id : int; | ||||
| enum op_type : int; | ||||
| struct undefined_t; | ||||
| template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_; | ||||
| template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> | ||||
| struct op_; | ||||
| void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); | ||||
| 
 | ||||
| /// Internal data structure which holds metadata about a keyword argument
 | ||||
|  | @ -169,12 +183,13 @@ struct argument_record { | |||
|         : name(name), descr(descr), value(value), convert(convert), none(none) {} | ||||
| }; | ||||
| 
 | ||||
| /// Internal data structure which holds metadata about a bound function (signature, overloads, etc.)
 | ||||
| /// Internal data structure which holds metadata about a bound function (signature, overloads,
 | ||||
| /// etc.)
 | ||||
| struct function_record { | ||||
|     function_record() | ||||
|         : is_constructor(false), is_new_style_constructor(false), is_stateless(false), | ||||
|           is_operator(false), is_method(false), has_args(false), | ||||
|           has_kwargs(false), prepend(false) { } | ||||
|           is_operator(false), is_method(false), has_args(false), has_kwargs(false), | ||||
|           prepend(false) {} | ||||
| 
 | ||||
|     /// Function name
 | ||||
|     char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ | ||||
|  | @ -311,35 +326,38 @@ struct type_record { | |||
|     bool is_final : 1; | ||||
| 
 | ||||
|     PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { | ||||
|         auto base_info = detail::get_type_info(base, false); | ||||
|         auto *base_info = detail::get_type_info(base, false); | ||||
|         if (!base_info) { | ||||
|             std::string tname(base.name()); | ||||
|             detail::clean_type_id(tname); | ||||
|             pybind11_fail("generic_type: type \"" + std::string(name) + | ||||
|                           "\" referenced unknown base type \"" + tname + "\""); | ||||
|             pybind11_fail("generic_type: type \"" + std::string(name) | ||||
|                           + "\" referenced unknown base type \"" + tname + "\""); | ||||
|         } | ||||
| 
 | ||||
|         if (default_holder != base_info->default_holder) { | ||||
|             std::string tname(base.name()); | ||||
|             detail::clean_type_id(tname); | ||||
|             pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + | ||||
|                     (default_holder ? "does not have" : "has") + | ||||
|                     " a non-default holder type while its base \"" + tname + "\" " + | ||||
|                     (base_info->default_holder ? "does not" : "does")); | ||||
|             pybind11_fail("generic_type: type \"" + std::string(name) + "\" " | ||||
|                           + (default_holder ? "does not have" : "has") | ||||
|                           + " a non-default holder type while its base \"" + tname + "\" " | ||||
|                           + (base_info->default_holder ? "does not" : "does")); | ||||
|         } | ||||
| 
 | ||||
|         bases.append((PyObject *) base_info->type); | ||||
| 
 | ||||
|         if (base_info->type->tp_dictoffset != 0) | ||||
|             dynamic_attr = true; | ||||
| #if PY_VERSION_HEX < 0x030B0000 | ||||
|         dynamic_attr |= base_info->type->tp_dictoffset != 0; | ||||
| #else | ||||
|         dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; | ||||
| #endif | ||||
| 
 | ||||
|         if (caster) | ||||
|         if (caster) { | ||||
|             base_info->implicit_casts.emplace_back(type, caster); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| inline function_call::function_call(const function_record &f, handle p) : | ||||
|         func(f), parent(p) { | ||||
| inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { | ||||
|     args.reserve(f.nargs); | ||||
|     args_convert.reserve(f.nargs); | ||||
| } | ||||
|  | @ -353,9 +371,11 @@ struct is_new_style_constructor { }; | |||
|  * fields in the type_record and function_record data structures or executed at | ||||
|  * runtime to deal with custom call policies (e.g. keep_alive). | ||||
|  */ | ||||
| template <typename T, typename SFINAE = void> struct process_attribute; | ||||
| template <typename T, typename SFINAE = void> | ||||
| struct process_attribute; | ||||
| 
 | ||||
| template <typename T> struct process_attribute_default { | ||||
| template <typename T> | ||||
| struct process_attribute_default { | ||||
|     /// Default implementation: do nothing
 | ||||
|     static void init(const T &, function_record *) {} | ||||
|     static void init(const T &, type_record *) {} | ||||
|  | @ -364,63 +384,84 @@ template <typename T> struct process_attribute_default { | |||
| }; | ||||
| 
 | ||||
| /// Process an attribute specifying the function's name
 | ||||
| template <> struct process_attribute<name> : process_attribute_default<name> { | ||||
| template <> | ||||
| struct process_attribute<name> : process_attribute_default<name> { | ||||
|     static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); } | ||||
| }; | ||||
| 
 | ||||
| /// Process an attribute specifying the function's docstring
 | ||||
| template <> struct process_attribute<doc> : process_attribute_default<doc> { | ||||
| template <> | ||||
| struct process_attribute<doc> : process_attribute_default<doc> { | ||||
|     static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); } | ||||
| }; | ||||
| 
 | ||||
| /// Process an attribute specifying the function's docstring (provided as a C-style string)
 | ||||
| template <> struct process_attribute<const char *> : process_attribute_default<const char *> { | ||||
| template <> | ||||
| struct process_attribute<const char *> : process_attribute_default<const char *> { | ||||
|     static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); } | ||||
|     static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); } | ||||
| }; | ||||
| template <> struct process_attribute<char *> : process_attribute<const char *> { }; | ||||
| template <> | ||||
| struct process_attribute<char *> : process_attribute<const char *> {}; | ||||
| 
 | ||||
| /// Process an attribute indicating the function's return value policy
 | ||||
| template <> struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> { | ||||
| template <> | ||||
| struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> { | ||||
|     static void init(const return_value_policy &p, function_record *r) { r->policy = p; } | ||||
| }; | ||||
| 
 | ||||
| /// Process an attribute which indicates that this is an overloaded function associated with a given sibling
 | ||||
| template <> struct process_attribute<sibling> : process_attribute_default<sibling> { | ||||
| /// Process an attribute which indicates that this is an overloaded function associated with a
 | ||||
| /// given sibling
 | ||||
| template <> | ||||
| struct process_attribute<sibling> : process_attribute_default<sibling> { | ||||
|     static void init(const sibling &s, function_record *r) { r->sibling = s.value; } | ||||
| }; | ||||
| 
 | ||||
| /// Process an attribute which indicates that this function is a method
 | ||||
| template <> struct process_attribute<is_method> : process_attribute_default<is_method> { | ||||
|     static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } | ||||
| template <> | ||||
| struct process_attribute<is_method> : process_attribute_default<is_method> { | ||||
|     static void init(const is_method &s, function_record *r) { | ||||
|         r->is_method = true; | ||||
|         r->scope = s.class_; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// Process an attribute which indicates the parent scope of a method
 | ||||
| template <> struct process_attribute<scope> : process_attribute_default<scope> { | ||||
| template <> | ||||
| struct process_attribute<scope> : process_attribute_default<scope> { | ||||
|     static void init(const scope &s, function_record *r) { r->scope = s.value; } | ||||
| }; | ||||
| 
 | ||||
| /// Process an attribute which indicates that this function is an operator
 | ||||
| template <> struct process_attribute<is_operator> : process_attribute_default<is_operator> { | ||||
| template <> | ||||
| struct process_attribute<is_operator> : process_attribute_default<is_operator> { | ||||
|     static void init(const is_operator &, function_record *r) { r->is_operator = true; } | ||||
| }; | ||||
| 
 | ||||
| template <> struct process_attribute<is_new_style_constructor> : process_attribute_default<is_new_style_constructor> { | ||||
|     static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } | ||||
| template <> | ||||
| struct process_attribute<is_new_style_constructor> | ||||
|     : process_attribute_default<is_new_style_constructor> { | ||||
|     static void init(const is_new_style_constructor &, function_record *r) { | ||||
|         r->is_new_style_constructor = true; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| inline void check_kw_only_arg(const arg &a, function_record *r) { | ||||
|     if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) | ||||
|         pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or args() argument"); | ||||
|     if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { | ||||
|         pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " | ||||
|                       "args() argument"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline void append_self_arg_if_needed(function_record *r) { | ||||
|     if (r->is_method && r->args.empty()) | ||||
|     if (r->is_method && r->args.empty()) { | ||||
|         r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Process a keyword argument attribute (*without* a default value)
 | ||||
| template <> struct process_attribute<arg> : process_attribute_default<arg> { | ||||
| template <> | ||||
| struct process_attribute<arg> : process_attribute_default<arg> { | ||||
|     static void init(const arg &a, function_record *r) { | ||||
|         append_self_arg_if_needed(r); | ||||
|         r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); | ||||
|  | @ -430,30 +471,38 @@ template <> struct process_attribute<arg> : process_attribute_default<arg> { | |||
| }; | ||||
| 
 | ||||
| /// Process a keyword argument attribute (*with* a default value)
 | ||||
| template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> { | ||||
| template <> | ||||
| struct process_attribute<arg_v> : process_attribute_default<arg_v> { | ||||
|     static void init(const arg_v &a, function_record *r) { | ||||
|         if (r->is_method && r->args.empty()) | ||||
|             r->args.emplace_back("self", /*descr=*/ nullptr, /*parent=*/ handle(), /*convert=*/ true, /*none=*/ false); | ||||
|         if (r->is_method && r->args.empty()) { | ||||
|             r->args.emplace_back( | ||||
|                 "self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); | ||||
|         } | ||||
| 
 | ||||
|         if (!a.value) { | ||||
| #if !defined(NDEBUG) | ||||
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|             std::string descr("'"); | ||||
|             if (a.name) descr += std::string(a.name) + ": "; | ||||
|             if (a.name) { | ||||
|                 descr += std::string(a.name) + ": "; | ||||
|             } | ||||
|             descr += a.type + "'"; | ||||
|             if (r->is_method) { | ||||
|                 if (r->name) | ||||
|                     descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; | ||||
|                 else | ||||
|                 if (r->name) { | ||||
|                     descr += " in method '" + (std::string) str(r->scope) + "." | ||||
|                              + (std::string) r->name + "'"; | ||||
|                 } else { | ||||
|                     descr += " in method of '" + (std::string) str(r->scope) + "'"; | ||||
|                 } | ||||
|             } else if (r->name) { | ||||
|                 descr += " in function '" + (std::string) r->name + "'"; | ||||
|             } | ||||
|             pybind11_fail("arg(): could not convert default argument " | ||||
|                           + descr + " into a Python object (type not registered yet?)"); | ||||
|             pybind11_fail("arg(): could not convert default argument " + descr | ||||
|                           + " into a Python object (type not registered yet?)"); | ||||
| #else | ||||
|             pybind11_fail("arg(): could not convert default argument " | ||||
|                           "into a Python object (type not registered yet?). " | ||||
|                           "Compile in debug mode for more information."); | ||||
|                           "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " | ||||
|                           "more information."); | ||||
| #endif | ||||
|         } | ||||
|         r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); | ||||
|  | @ -463,29 +512,36 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> { | |||
| }; | ||||
| 
 | ||||
| /// Process a keyword-only-arguments-follow pseudo argument
 | ||||
| template <> struct process_attribute<kw_only> : process_attribute_default<kw_only> { | ||||
| template <> | ||||
| struct process_attribute<kw_only> : process_attribute_default<kw_only> { | ||||
|     static void init(const kw_only &, function_record *r) { | ||||
|         append_self_arg_if_needed(r); | ||||
|         if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) | ||||
|             pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative argument location (or omit kw_only() entirely)"); | ||||
|         if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) { | ||||
|             pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " | ||||
|                           "argument location (or omit kw_only() entirely)"); | ||||
|         } | ||||
|         r->nargs_pos = static_cast<std::uint16_t>(r->args.size()); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// Process a positional-only-argument maker
 | ||||
| template <> struct process_attribute<pos_only> : process_attribute_default<pos_only> { | ||||
| template <> | ||||
| struct process_attribute<pos_only> : process_attribute_default<pos_only> { | ||||
|     static void init(const pos_only &, function_record *r) { | ||||
|         append_self_arg_if_needed(r); | ||||
|         r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size()); | ||||
|         if (r->nargs_pos_only > r->nargs_pos) | ||||
|         if (r->nargs_pos_only > r->nargs_pos) { | ||||
|             pybind11_fail("pos_only(): cannot follow a py::args() argument"); | ||||
|         } | ||||
|         // It also can't follow a kw_only, but a static_assert in pybind11.h checks that
 | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// Process a parent class attribute.  Single inheritance only (class_ itself already guarantees that)
 | ||||
| /// Process a parent class attribute.  Single inheritance only (class_ itself already guarantees
 | ||||
| /// that)
 | ||||
| template <typename T> | ||||
| struct process_attribute<T, enable_if_t<is_pyobject<T>::value>> : process_attribute_default<handle> { | ||||
| struct process_attribute<T, enable_if_t<is_pyobject<T>::value>> | ||||
|     : process_attribute_default<handle> { | ||||
|     static void init(const handle &h, type_record *r) { r->bases.append(h); } | ||||
| }; | ||||
| 
 | ||||
|  | @ -498,7 +554,9 @@ struct process_attribute<base<T>> : process_attribute_default<base<T>> { | |||
| /// Process a multiple inheritance attribute
 | ||||
| template <> | ||||
| struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> { | ||||
|     static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } | ||||
|     static void init(const multiple_inheritance &, type_record *r) { | ||||
|         r->multiple_inheritance = true; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template <> | ||||
|  | @ -551,19 +609,26 @@ struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_gua | |||
|  * pre-call handler if both Nurse, Patient != 0 and use the post-call handler | ||||
|  * otherwise | ||||
|  */ | ||||
| template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurse, Patient>> : public process_attribute_default<keep_alive<Nurse, Patient>> { | ||||
| template <size_t Nurse, size_t Patient> | ||||
| struct process_attribute<keep_alive<Nurse, Patient>> | ||||
|     : public process_attribute_default<keep_alive<Nurse, Patient>> { | ||||
|     template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0> | ||||
|     static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } | ||||
|     static void precall(function_call &call) { | ||||
|         keep_alive_impl(Nurse, Patient, call, handle()); | ||||
|     } | ||||
|     template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0> | ||||
|     static void postcall(function_call &, handle) {} | ||||
|     template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0> | ||||
|     static void precall(function_call &) {} | ||||
|     template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0> | ||||
|     static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } | ||||
|     static void postcall(function_call &call, handle ret) { | ||||
|         keep_alive_impl(Nurse, Patient, call, ret); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// Recursively iterate over variadic template arguments
 | ||||
| template <typename... Args> struct process_attributes { | ||||
| template <typename... Args> | ||||
| struct process_attributes { | ||||
|     static void init(const Args &...args, function_record *r) { | ||||
|         PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); | ||||
|         PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); | ||||
|  |  | |||
|  | @ -19,9 +19,11 @@ PYBIND11_NAMESPACE_BEGIN(detail) | |||
| inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { | ||||
|     auto ndim = shape.size(); | ||||
|     std::vector<ssize_t> strides(ndim, itemsize); | ||||
|     if (ndim > 0) | ||||
|         for (size_t i = ndim - 1; i > 0; --i) | ||||
|     if (ndim > 0) { | ||||
|         for (size_t i = ndim - 1; i > 0; --i) { | ||||
|             strides[i - 1] = strides[i] * shape[i]; | ||||
|         } | ||||
|     } | ||||
|     return strides; | ||||
| } | ||||
| 
 | ||||
|  | @ -29,8 +31,9 @@ inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t | |||
| inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { | ||||
|     auto ndim = shape.size(); | ||||
|     std::vector<ssize_t> strides(ndim, itemsize); | ||||
|     for (size_t i = 1; i < ndim; ++i) | ||||
|     for (size_t i = 1; i < ndim; ++i) { | ||||
|         strides[i] = strides[i - 1] * shape[i - 1]; | ||||
|     } | ||||
|     return strides; | ||||
| } | ||||
| 
 | ||||
|  | @ -41,29 +44,52 @@ struct buffer_info { | |||
|     void *ptr = nullptr;          // Pointer to the underlying storage
 | ||||
|     ssize_t itemsize = 0;         // Size of individual items in bytes
 | ||||
|     ssize_t size = 0;             // Total number of entries
 | ||||
|     std::string format;           // For homogeneous buffers, this should be set to format_descriptor<T>::format()
 | ||||
|     std::string format;           // For homogeneous buffers, this should be set to
 | ||||
|                                   // format_descriptor<T>::format()
 | ||||
|     ssize_t ndim = 0;             // Number of dimensions
 | ||||
|     std::vector<ssize_t> shape;   // Shape of the tensor (1 entry per dimension)
 | ||||
|     std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
 | ||||
|     std::vector<ssize_t> strides; // Number of bytes between adjacent entries
 | ||||
|                                   // (for each per dimension)
 | ||||
|     bool readonly = false;        // flag to indicate if the underlying storage may be written to
 | ||||
| 
 | ||||
|     buffer_info() = default; | ||||
| 
 | ||||
|     buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, | ||||
|                 detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false) | ||||
|     buffer_info(void *ptr, | ||||
|                 ssize_t itemsize, | ||||
|                 const std::string &format, | ||||
|                 ssize_t ndim, | ||||
|                 detail::any_container<ssize_t> shape_in, | ||||
|                 detail::any_container<ssize_t> strides_in, | ||||
|                 bool readonly = false) | ||||
|         : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), | ||||
|           shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { | ||||
|         if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) | ||||
|         if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { | ||||
|             pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); | ||||
|         for (size_t i = 0; i < (size_t) ndim; ++i) | ||||
|         } | ||||
|         for (size_t i = 0; i < (size_t) ndim; ++i) { | ||||
|             size *= shape[i]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> | ||||
|     buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false) | ||||
|     : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } | ||||
|     buffer_info(T *ptr, | ||||
|                 detail::any_container<ssize_t> shape_in, | ||||
|                 detail::any_container<ssize_t> strides_in, | ||||
|                 bool readonly = false) | ||||
|         : buffer_info(private_ctr_tag(), | ||||
|                       ptr, | ||||
|                       sizeof(T), | ||||
|                       format_descriptor<T>::format(), | ||||
|                       static_cast<ssize_t>(shape_in->size()), | ||||
|                       std::move(shape_in), | ||||
|                       std::move(strides_in), | ||||
|                       readonly) {} | ||||
| 
 | ||||
|     buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) | ||||
|     buffer_info(void *ptr, | ||||
|                 ssize_t itemsize, | ||||
|                 const std::string &format, | ||||
|                 ssize_t size, | ||||
|                 bool readonly = false) | ||||
|         : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} | ||||
| 
 | ||||
|     template <typename T> | ||||
|  | @ -72,10 +98,15 @@ struct buffer_info { | |||
| 
 | ||||
|     template <typename T> | ||||
|     buffer_info(const T *ptr, ssize_t size, bool readonly = true) | ||||
|     : buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) { } | ||||
|         : buffer_info( | ||||
|             const_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {} | ||||
| 
 | ||||
|     explicit buffer_info(Py_buffer *view, bool ownview = true) | ||||
|     : buffer_info(view->buf, view->itemsize, view->format, view->ndim, | ||||
|         : buffer_info( | ||||
|             view->buf, | ||||
|             view->itemsize, | ||||
|             view->format, | ||||
|             view->ndim, | ||||
|             {view->shape, view->shape + view->ndim}, | ||||
|             /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
 | ||||
|              * ignore this flag and return a view with NULL strides. | ||||
|  | @ -84,7 +115,9 @@ struct buffer_info { | |||
|                 ? std::vector<ssize_t>(view->strides, view->strides + view->ndim) | ||||
|                 : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), | ||||
|             (view->readonly != 0)) { | ||||
|         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
 | ||||
|         this->m_view = view; | ||||
|         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
 | ||||
|         this->ownview = ownview; | ||||
|     } | ||||
| 
 | ||||
|  | @ -108,17 +141,28 @@ struct buffer_info { | |||
|     } | ||||
| 
 | ||||
|     ~buffer_info() { | ||||
|         if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; } | ||||
|         if (m_view && ownview) { | ||||
|             PyBuffer_Release(m_view); | ||||
|             delete m_view; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Py_buffer *view() const { return m_view; } | ||||
|     Py_buffer *&view() { return m_view; } | ||||
| 
 | ||||
| private: | ||||
|     struct private_ctr_tag {}; | ||||
| 
 | ||||
|     buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, | ||||
|                 detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly) | ||||
|     : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } | ||||
|     buffer_info(private_ctr_tag, | ||||
|                 void *ptr, | ||||
|                 ssize_t itemsize, | ||||
|                 const std::string &format, | ||||
|                 ssize_t ndim, | ||||
|                 detail::any_container<ssize_t> &&shape_in, | ||||
|                 detail::any_container<ssize_t> &&strides_in, | ||||
|                 bool readonly) | ||||
|         : buffer_info( | ||||
|             ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} | ||||
| 
 | ||||
|     Py_buffer *m_view = nullptr; | ||||
|     bool ownview = false; | ||||
|  | @ -126,17 +170,22 @@ private: | |||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| template <typename T, typename SFINAE = void> struct compare_buffer_info { | ||||
| template <typename T, typename SFINAE = void> | ||||
| struct compare_buffer_info { | ||||
|     static bool compare(const buffer_info &b) { | ||||
|         return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template <typename T> struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> { | ||||
| template <typename T> | ||||
| struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> { | ||||
|     static bool compare(const buffer_info &b) { | ||||
|         return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value || | ||||
|             ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) || | ||||
|             ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n"))); | ||||
|         return (size_t) b.itemsize == sizeof(T) | ||||
|                && (b.format == format_descriptor<T>::value | ||||
|                    || ((sizeof(T) == sizeof(long)) | ||||
|                        && b.format == (std::is_unsigned<T>::value ? "L" : "l")) | ||||
|                    || ((sizeof(T) == sizeof(size_t)) | ||||
|                        && b.format == (std::is_unsigned<T>::value ? "N" : "n"))); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -15,38 +15,32 @@ | |||
| #include <chrono> | ||||
| #include <cmath> | ||||
| #include <ctime> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include <datetime.h> | ||||
| 
 | ||||
| // Backport the PyDateTime_DELTA functions from Python3.3 if required
 | ||||
| #ifndef PyDateTime_DELTA_GET_DAYS | ||||
| #define PyDateTime_DELTA_GET_DAYS(o)         (((PyDateTime_Delta*)o)->days) | ||||
| #endif | ||||
| #ifndef PyDateTime_DELTA_GET_SECONDS | ||||
| #define PyDateTime_DELTA_GET_SECONDS(o)      (((PyDateTime_Delta*)o)->seconds) | ||||
| #endif | ||||
| #ifndef PyDateTime_DELTA_GET_MICROSECONDS | ||||
| #define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) | ||||
| #endif | ||||
| #include <mutex> | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| template <typename type> class duration_caster { | ||||
| template <typename type> | ||||
| class duration_caster { | ||||
| public: | ||||
|     using rep = typename type::rep; | ||||
|     using period = typename type::period; | ||||
| 
 | ||||
|     using days = std::chrono::duration<int_least32_t, std::ratio<86400>>; // signed 25 bits required by the standard.
 | ||||
|     // signed 25 bits required by the standard.
 | ||||
|     using days = std::chrono::duration<int_least32_t, std::ratio<86400>>; | ||||
| 
 | ||||
|     bool load(handle src, bool) { | ||||
|         using namespace std::chrono; | ||||
| 
 | ||||
|         // Lazy initialise the PyDateTime import
 | ||||
|         if (!PyDateTimeAPI) { PyDateTime_IMPORT; } | ||||
|         if (!PyDateTimeAPI) { | ||||
|             PyDateTime_IMPORT; | ||||
|         } | ||||
| 
 | ||||
|         if (!src) return false; | ||||
|         if (!src) { | ||||
|             return false; | ||||
|         } | ||||
|         // If invoked with datetime.delta object
 | ||||
|         if (PyDelta_Check(src.ptr())) { | ||||
|             value = type(duration_cast<duration<rep, period>>( | ||||
|  | @ -57,19 +51,23 @@ public: | |||
|         } | ||||
|         // If invoked with a float we assume it is seconds and convert
 | ||||
|         if (PyFloat_Check(src.ptr())) { | ||||
|             value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr())))); | ||||
|             value = type(duration_cast<duration<rep, period>>( | ||||
|                 duration<double>(PyFloat_AsDouble(src.ptr())))); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // If this is a duration just return it back
 | ||||
|     static const std::chrono::duration<rep, period>& get_duration(const std::chrono::duration<rep, period> &src) { | ||||
|     static const std::chrono::duration<rep, period> & | ||||
|     get_duration(const std::chrono::duration<rep, period> &src) { | ||||
|         return src; | ||||
|     } | ||||
| 
 | ||||
|     // If this is a time_point get the time_since_epoch
 | ||||
|     template <typename Clock> static std::chrono::duration<rep, period> get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) { | ||||
|     template <typename Clock> | ||||
|     static std::chrono::duration<rep, period> | ||||
|     get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) { | ||||
|         return src.time_since_epoch(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -81,9 +79,12 @@ public: | |||
|         auto d = get_duration(src); | ||||
| 
 | ||||
|         // Lazy initialise the PyDateTime import
 | ||||
|         if (!PyDateTimeAPI) { PyDateTime_IMPORT; } | ||||
|         if (!PyDateTimeAPI) { | ||||
|             PyDateTime_IMPORT; | ||||
|         } | ||||
| 
 | ||||
|         // Declare these special duration types so the conversions happen with the correct primitive types (int)
 | ||||
|         // Declare these special duration types so the conversions happen with the correct
 | ||||
|         // primitive types (int)
 | ||||
|         using dd_t = duration<int, std::ratio<86400>>; | ||||
|         using ss_t = duration<int, std::ratio<1>>; | ||||
|         using us_t = duration<int, std::micro>; | ||||
|  | @ -115,16 +116,21 @@ inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) { | |||
| } | ||||
| 
 | ||||
| // This is for casting times on the system clock into datetime.datetime instances
 | ||||
| template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> { | ||||
| template <typename Duration> | ||||
| class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> { | ||||
| public: | ||||
|     using type = std::chrono::time_point<std::chrono::system_clock, Duration>; | ||||
|     bool load(handle src, bool) { | ||||
|         using namespace std::chrono; | ||||
| 
 | ||||
|         // Lazy initialise the PyDateTime import
 | ||||
|         if (!PyDateTimeAPI) { PyDateTime_IMPORT; } | ||||
|         if (!PyDateTimeAPI) { | ||||
|             PyDateTime_IMPORT; | ||||
|         } | ||||
| 
 | ||||
|         if (!src) return false; | ||||
|         if (!src) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         std::tm cal; | ||||
|         microseconds msecs; | ||||
|  | @ -156,35 +162,43 @@ public: | |||
|             cal.tm_year = 70; // earliest available date for Python's datetime
 | ||||
|             cal.tm_isdst = -1; | ||||
|             msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr())); | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
|         else return false; | ||||
| 
 | ||||
|         value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) { | ||||
|     static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, | ||||
|                        return_value_policy /* policy */, | ||||
|                        handle /* parent */) { | ||||
|         using namespace std::chrono; | ||||
| 
 | ||||
|         // Lazy initialise the PyDateTime import
 | ||||
|         if (!PyDateTimeAPI) { PyDateTime_IMPORT; } | ||||
|         if (!PyDateTimeAPI) { | ||||
|             PyDateTime_IMPORT; | ||||
|         } | ||||
| 
 | ||||
|         // Get out microseconds, and make sure they are positive, to avoid bug in eastern hemisphere time zones
 | ||||
|         // (cfr. https://github.com/pybind/pybind11/issues/2417)
 | ||||
|         // Get out microseconds, and make sure they are positive, to avoid bug in eastern
 | ||||
|         // hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
 | ||||
|         using us_t = duration<int, std::micro>; | ||||
|         auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1)); | ||||
|         if (us.count() < 0) | ||||
|         if (us.count() < 0) { | ||||
|             us += seconds(1); | ||||
|         } | ||||
| 
 | ||||
|         // Subtract microseconds BEFORE `system_clock::to_time_t`, because:
 | ||||
|         // > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated.
 | ||||
|         // (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
 | ||||
|         std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us)); | ||||
|         // > If std::time_t has lower precision, it is implementation-defined whether the value is
 | ||||
|         // rounded or truncated. (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
 | ||||
|         std::time_t tt | ||||
|             = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us)); | ||||
| 
 | ||||
|         std::tm localtime; | ||||
|         std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime); | ||||
|         if (!localtime_ptr) | ||||
|         if (!localtime_ptr) { | ||||
|             throw cast_error("Unable to represent system_clock in local time"); | ||||
|         } | ||||
|         return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, | ||||
|                                           localtime.tm_mon + 1, | ||||
|                                           localtime.tm_mday, | ||||
|  | @ -199,13 +213,13 @@ public: | |||
| // Other clocks that are not the system clock are not measured as datetime.datetime objects
 | ||||
| // since they are not measured on calendar time. So instead we just make them timedeltas
 | ||||
| // Or if they have passed us a time as a float we convert that
 | ||||
| template <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>> | ||||
| : public duration_caster<std::chrono::time_point<Clock, Duration>> { | ||||
| }; | ||||
| template <typename Clock, typename Duration> | ||||
| class type_caster<std::chrono::time_point<Clock, Duration>> | ||||
|     : public duration_caster<std::chrono::time_point<Clock, Duration>> {}; | ||||
| 
 | ||||
| template <typename Rep, typename Period> class type_caster<std::chrono::duration<Rep, Period>> | ||||
| : public duration_caster<std::chrono::duration<Rep, Period>> { | ||||
| }; | ||||
| template <typename Rep, typename Period> | ||||
| class type_caster<std::chrono::duration<Rep, Period>> | ||||
|     : public duration_caster<std::chrono::duration<Rep, Period>> {}; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "pybind11.h" | ||||
| 
 | ||||
| #include <complex> | ||||
| 
 | ||||
| /// glibc defines I as a macro which breaks things, e.g., boost template names
 | ||||
|  | @ -19,7 +20,8 @@ | |||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| 
 | ||||
| template <typename T> struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> { | ||||
| template <typename T> | ||||
| struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> { | ||||
|     static constexpr const char c = format_descriptor<T>::c; | ||||
|     static constexpr const char value[3] = {'Z', c, '\0'}; | ||||
|     static std::string format() { return std::string(value); } | ||||
|  | @ -27,25 +29,31 @@ template <typename T> struct format_descriptor<std::complex<T>, detail::enable_i | |||
| 
 | ||||
| #ifndef PYBIND11_CPP17 | ||||
| 
 | ||||
| template <typename T> constexpr const char format_descriptor< | ||||
|     std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>>::value[3]; | ||||
| template <typename T> | ||||
| constexpr const char | ||||
|     format_descriptor<std::complex<T>, | ||||
|                       detail::enable_if_t<std::is_floating_point<T>::value>>::value[3]; | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| template <typename T> struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> { | ||||
| template <typename T> | ||||
| struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> { | ||||
|     static constexpr bool value = true; | ||||
|     static constexpr int index = is_fmt_numeric<T>::index + 3; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> class type_caster<std::complex<T>> { | ||||
| template <typename T> | ||||
| class type_caster<std::complex<T>> { | ||||
| public: | ||||
|     bool load(handle src, bool convert) { | ||||
|         if (!src) | ||||
|         if (!src) { | ||||
|             return false; | ||||
|         if (!convert && !PyComplex_Check(src.ptr())) | ||||
|         } | ||||
|         if (!convert && !PyComplex_Check(src.ptr())) { | ||||
|             return false; | ||||
|         } | ||||
|         Py_complex result = PyComplex_AsCComplex(src.ptr()); | ||||
|         if (result.real == -1.0 && PyErr_Occurred()) { | ||||
|             PyErr_Clear(); | ||||
|  | @ -55,7 +63,8 @@ public: | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     static handle cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) { | ||||
|     static handle | ||||
|     cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) { | ||||
|         return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,13 +15,14 @@ | |||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| #if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION) | ||||
| #if !defined(PYPY_VERSION) | ||||
| #    define PYBIND11_BUILTIN_QUALNAME | ||||
| #    define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) | ||||
| #else | ||||
| // In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type
 | ||||
| // signatures; in 3.3+ this macro expands to nothing:
 | ||||
| #  define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) | ||||
| // In PyPy, we still set __qualname__ so that we can produce reliable function type
 | ||||
| // signatures; in CPython this macro expands to nothing:
 | ||||
| #    define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)                                             \ | ||||
|         setattr((PyObject *) obj, "__qualname__", nameobj) | ||||
| #endif | ||||
| 
 | ||||
| inline std::string get_fully_qualified_tp_name(PyTypeObject *type) { | ||||
|  | @ -65,24 +66,26 @@ inline PyTypeObject *make_static_property_type() { | |||
|        issue no Python C API calls which could potentially invoke the | ||||
|        garbage collector (the GC will call type_traverse(), which will in | ||||
|        turn find the newly constructed type in an invalid state) */ | ||||
|     auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); | ||||
|     if (!heap_type) | ||||
|     auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); | ||||
|     if (!heap_type) { | ||||
|         pybind11_fail("make_static_property_type(): error allocating type!"); | ||||
|     } | ||||
| 
 | ||||
|     heap_type->ht_name = name_obj.inc_ref().ptr(); | ||||
| #    ifdef PYBIND11_BUILTIN_QUALNAME | ||||
|     heap_type->ht_qualname = name_obj.inc_ref().ptr(); | ||||
| #    endif | ||||
| 
 | ||||
|     auto type = &heap_type->ht_type; | ||||
|     auto *type = &heap_type->ht_type; | ||||
|     type->tp_name = name; | ||||
|     type->tp_base = type_incref(&PyProperty_Type); | ||||
|     type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; | ||||
|     type->tp_descr_get = pybind11_static_get; | ||||
|     type->tp_descr_set = pybind11_static_set; | ||||
| 
 | ||||
|     if (PyType_Ready(type) < 0) | ||||
|     if (PyType_Ready(type) < 0) { | ||||
|         pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); | ||||
|     } | ||||
| 
 | ||||
|     setattr((PyObject *) type, "__module__", str("pybind11_builtins")); | ||||
|     PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); | ||||
|  | @ -105,8 +108,10 @@ inline PyTypeObject *make_static_property_type() { | |||
|     def __set__(self, obj, value): | ||||
|         cls = obj if isinstance(obj, type) else type(obj) | ||||
|         property.__set__(self, cls, value) | ||||
|         )", Py_file_input, d.ptr(), d.ptr() | ||||
|     ); | ||||
| )", | ||||
|                                     Py_file_input, | ||||
|                                     d.ptr(), | ||||
|                                     d.ptr()); | ||||
|     if (result == nullptr) | ||||
|         throw error_already_set(); | ||||
|     Py_DECREF(result); | ||||
|  | @ -128,7 +133,7 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb | |||
|     //   1. `Type.static_prop = value`             --> descr_set: `Type.static_prop.__set__(value)`
 | ||||
|     //   2. `Type.static_prop = other_static_prop` --> setattro:  replace existing `static_prop`
 | ||||
|     //   3. `Type.regular_attribute = value`       --> setattro:  regular attribute assignment
 | ||||
|     const auto static_prop = (PyObject *) get_internals().static_property_type; | ||||
|     auto *const static_prop = (PyObject *) get_internals().static_property_type; | ||||
|     const auto call_descr_set = (descr != nullptr) && (value != nullptr) | ||||
|                                 && (PyObject_IsInstance(descr, static_prop) != 0) | ||||
|                                 && (PyObject_IsInstance(value, static_prop) == 0); | ||||
|  | @ -150,7 +155,6 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
| /**
 | ||||
|  * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing | ||||
|  * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, | ||||
|  | @ -165,7 +169,6 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name | |||
|     } | ||||
|     return PyType_Type.tp_getattro(obj, name); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /// metaclass `__call__` function that is used to create all pybind11 objects.
 | ||||
| extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { | ||||
|  | @ -177,12 +180,13 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P | |||
|     } | ||||
| 
 | ||||
|     // This must be a pybind11 instance
 | ||||
|     auto instance = reinterpret_cast<detail::instance *>(self); | ||||
|     auto *instance = reinterpret_cast<detail::instance *>(self); | ||||
| 
 | ||||
|     // Ensure that the base __init__ function(s) were called
 | ||||
|     for (const auto &vh : values_and_holders(instance)) { | ||||
|         if (!vh.holder_constructed()) { | ||||
|             PyErr_Format(PyExc_TypeError, "%.200s.__init__() must be called when overriding __init__", | ||||
|             PyErr_Format(PyExc_TypeError, | ||||
|                          "%.200s.__init__() must be called when overriding __init__", | ||||
|                          get_fully_qualified_tp_name(vh.type->type).c_str()); | ||||
|             Py_DECREF(self); | ||||
|             return nullptr; | ||||
|  | @ -201,28 +205,29 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) { | |||
|     // 1) be found in internals.registered_types_py
 | ||||
|     // 2) have exactly one associated `detail::type_info`
 | ||||
|     auto found_type = internals.registered_types_py.find(type); | ||||
|     if (found_type != internals.registered_types_py.end() && | ||||
|         found_type->second.size() == 1 && | ||||
|         found_type->second[0]->type == type) { | ||||
|     if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1 | ||||
|         && found_type->second[0]->type == type) { | ||||
| 
 | ||||
|         auto *tinfo = found_type->second[0]; | ||||
|         auto tindex = std::type_index(*tinfo->cpptype); | ||||
|         internals.direct_conversions.erase(tindex); | ||||
| 
 | ||||
|         if (tinfo->module_local) | ||||
|         if (tinfo->module_local) { | ||||
|             get_local_internals().registered_types_cpp.erase(tindex); | ||||
|         else | ||||
|         } else { | ||||
|             internals.registered_types_cpp.erase(tindex); | ||||
|         } | ||||
|         internals.registered_types_py.erase(tinfo->type); | ||||
| 
 | ||||
|         // Actually just `std::erase_if`, but that's only available in C++20
 | ||||
|         auto &cache = internals.inactive_override_cache; | ||||
|         for (auto it = cache.begin(), last = cache.end(); it != last;) { | ||||
|             if (it->first == (PyObject *) tinfo->type) | ||||
|             if (it->first == (PyObject *) tinfo->type) { | ||||
|                 it = cache.erase(it); | ||||
|             else | ||||
|             } else { | ||||
|                 ++it; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         delete tinfo; | ||||
|     } | ||||
|  | @ -241,16 +246,17 @@ inline PyTypeObject* make_default_metaclass() { | |||
|        issue no Python C API calls which could potentially invoke the | ||||
|        garbage collector (the GC will call type_traverse(), which will in | ||||
|        turn find the newly constructed type in an invalid state) */ | ||||
|     auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); | ||||
|     if (!heap_type) | ||||
|     auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); | ||||
|     if (!heap_type) { | ||||
|         pybind11_fail("make_default_metaclass(): error allocating metaclass!"); | ||||
|     } | ||||
| 
 | ||||
|     heap_type->ht_name = name_obj.inc_ref().ptr(); | ||||
| #ifdef PYBIND11_BUILTIN_QUALNAME | ||||
|     heap_type->ht_qualname = name_obj.inc_ref().ptr(); | ||||
| #endif | ||||
| 
 | ||||
|     auto type = &heap_type->ht_type; | ||||
|     auto *type = &heap_type->ht_type; | ||||
|     type->tp_name = name; | ||||
|     type->tp_base = type_incref(&PyType_Type); | ||||
|     type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; | ||||
|  | @ -258,14 +264,13 @@ inline PyTypeObject* make_default_metaclass() { | |||
|     type->tp_call = pybind11_meta_call; | ||||
| 
 | ||||
|     type->tp_setattro = pybind11_meta_setattro; | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|     type->tp_getattro = pybind11_meta_getattro; | ||||
| #endif | ||||
| 
 | ||||
|     type->tp_dealloc = pybind11_meta_dealloc; | ||||
| 
 | ||||
|     if (PyType_Ready(type) < 0) | ||||
|     if (PyType_Ready(type) < 0) { | ||||
|         pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); | ||||
|     } | ||||
| 
 | ||||
|     setattr((PyObject *) type, "__module__", str("pybind11_builtins")); | ||||
|     PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); | ||||
|  | @ -275,16 +280,20 @@ inline PyTypeObject* make_default_metaclass() { | |||
| 
 | ||||
| /// For multiple inheritance types we need to recursively register/deregister base pointers for any
 | ||||
| /// base classes with pointers that are difference from the instance value pointer so that we can
 | ||||
| /// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs.
 | ||||
| inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, | ||||
| /// correctly recognize an offset base class pointer. This calls a function with any offset base
 | ||||
| /// ptrs.
 | ||||
| inline void traverse_offset_bases(void *valueptr, | ||||
|                                   const detail::type_info *tinfo, | ||||
|                                   instance *self, | ||||
|                                   bool (*f)(void * /*parentptr*/, instance * /*self*/)) { | ||||
|     for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) { | ||||
|         if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { | ||||
|         if (auto *parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { | ||||
|             for (auto &c : parent_tinfo->implicit_casts) { | ||||
|                 if (c.first == tinfo->cpptype) { | ||||
|                     auto *parentptr = c.second(valueptr); | ||||
|                     if (parentptr != valueptr) | ||||
|                     if (parentptr != valueptr) { | ||||
|                         f(parentptr, self); | ||||
|                     } | ||||
|                     traverse_offset_bases(parentptr, parent_tinfo, self, f); | ||||
|                     break; | ||||
|                 } | ||||
|  | @ -311,31 +320,33 @@ inline bool deregister_instance_impl(void *ptr, instance *self) { | |||
| 
 | ||||
| inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { | ||||
|     register_instance_impl(valptr, self); | ||||
|     if (!tinfo->simple_ancestors) | ||||
|     if (!tinfo->simple_ancestors) { | ||||
|         traverse_offset_bases(valptr, tinfo, self, register_instance_impl); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { | ||||
|     bool ret = deregister_instance_impl(valptr, self); | ||||
|     if (!tinfo->simple_ancestors) | ||||
|     if (!tinfo->simple_ancestors) { | ||||
|         traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| /// Instance creation function for all pybind11 types. It allocates the internal instance layout for
 | ||||
| /// holding C++ objects and holders.  Allocation is done lazily (the first time the instance is cast
 | ||||
| /// to a reference or pointer), and initialization is done by an `__init__` function.
 | ||||
| /// Instance creation function for all pybind11 types. It allocates the internal instance layout
 | ||||
| /// for holding C++ objects and holders.  Allocation is done lazily (the first time the instance is
 | ||||
| /// cast to a reference or pointer), and initialization is done by an `__init__` function.
 | ||||
| inline PyObject *make_new_instance(PyTypeObject *type) { | ||||
| #if defined(PYPY_VERSION) | ||||
|     // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
 | ||||
|     // object is a plain Python type (i.e. not derived from an extension type).  Fix it.
 | ||||
|     // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first
 | ||||
|     // inherited object is a plain Python type (i.e. not derived from an extension type).  Fix it.
 | ||||
|     ssize_t instance_size = static_cast<ssize_t>(sizeof(instance)); | ||||
|     if (type->tp_basicsize < instance_size) { | ||||
|         type->tp_basicsize = instance_size; | ||||
|     } | ||||
| #endif | ||||
|     PyObject *self = type->tp_alloc(type, 0); | ||||
|     auto inst = reinterpret_cast<instance *>(self); | ||||
|     auto *inst = reinterpret_cast<instance *>(self); | ||||
|     // Allocate the value/holder internals:
 | ||||
|     inst->allocate_layout(); | ||||
| 
 | ||||
|  | @ -360,14 +371,14 @@ extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject | |||
| 
 | ||||
| inline void add_patient(PyObject *nurse, PyObject *patient) { | ||||
|     auto &internals = get_internals(); | ||||
|     auto instance = reinterpret_cast<detail::instance *>(nurse); | ||||
|     auto *instance = reinterpret_cast<detail::instance *>(nurse); | ||||
|     instance->has_patients = true; | ||||
|     Py_INCREF(patient); | ||||
|     internals.patients[nurse].push_back(patient); | ||||
| } | ||||
| 
 | ||||
| inline void clear_patients(PyObject *self) { | ||||
|     auto instance = reinterpret_cast<detail::instance *>(self); | ||||
|     auto *instance = reinterpret_cast<detail::instance *>(self); | ||||
|     auto &internals = get_internals(); | ||||
|     auto pos = internals.patients.find(self); | ||||
|     assert(pos != internals.patients.end()); | ||||
|  | @ -377,14 +388,15 @@ inline void clear_patients(PyObject *self) { | |||
|     auto patients = std::move(pos->second); | ||||
|     internals.patients.erase(pos); | ||||
|     instance->has_patients = false; | ||||
|     for (PyObject *&patient : patients) | ||||
|     for (PyObject *&patient : patients) { | ||||
|         Py_CLEAR(patient); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clears all internal data from the instance and removes it from registered instances in
 | ||||
| /// preparation for deallocation.
 | ||||
| inline void clear_instance(PyObject *self) { | ||||
|     auto instance = reinterpret_cast<detail::instance *>(self); | ||||
|     auto *instance = reinterpret_cast<detail::instance *>(self); | ||||
| 
 | ||||
|     // Deallocate any values/holders, if present:
 | ||||
|     for (auto &v_h : values_and_holders(instance)) { | ||||
|  | @ -392,33 +404,40 @@ inline void clear_instance(PyObject *self) { | |||
| 
 | ||||
|             // We have to deregister before we call dealloc because, for virtual MI types, we still
 | ||||
|             // need to be able to get the parent pointers.
 | ||||
|             if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) | ||||
|                 pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); | ||||
|             if (v_h.instance_registered() | ||||
|                 && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) { | ||||
|                 pybind11_fail( | ||||
|                     "pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); | ||||
|             } | ||||
| 
 | ||||
|             if (instance->owned || v_h.holder_constructed()) | ||||
|             if (instance->owned || v_h.holder_constructed()) { | ||||
|                 v_h.type->dealloc(v_h); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     // Deallocate the value/holder layout internals:
 | ||||
|     instance->deallocate_layout(); | ||||
| 
 | ||||
|     if (instance->weakrefs) | ||||
|     if (instance->weakrefs) { | ||||
|         PyObject_ClearWeakRefs(self); | ||||
|     } | ||||
| 
 | ||||
|     PyObject **dict_ptr = _PyObject_GetDictPtr(self); | ||||
|     if (dict_ptr) | ||||
|     if (dict_ptr) { | ||||
|         Py_CLEAR(*dict_ptr); | ||||
|     } | ||||
| 
 | ||||
|     if (instance->has_patients) | ||||
|     if (instance->has_patients) { | ||||
|         clear_patients(self); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
 | ||||
| /// to destroy the C++ object itself, while the rest is Python bookkeeping.
 | ||||
| extern "C" inline void pybind11_object_dealloc(PyObject *self) { | ||||
|     clear_instance(self); | ||||
| 
 | ||||
|     auto type = Py_TYPE(self); | ||||
|     auto *type = Py_TYPE(self); | ||||
|     type->tp_free(self); | ||||
| 
 | ||||
| #if PY_VERSION_HEX < 0x03080000 | ||||
|  | @ -436,6 +455,8 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) { | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| std::string error_string(); | ||||
| 
 | ||||
| /** Create the type which can be used as a common base for all classes.  This is
 | ||||
|     needed in order to satisfy Python's requirements for multiple inheritance. | ||||
|     Return value: New reference. */ | ||||
|  | @ -447,16 +468,17 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) { | |||
|        issue no Python C API calls which could potentially invoke the | ||||
|        garbage collector (the GC will call type_traverse(), which will in | ||||
|        turn find the newly constructed type in an invalid state) */ | ||||
|     auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); | ||||
|     if (!heap_type) | ||||
|     auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); | ||||
|     if (!heap_type) { | ||||
|         pybind11_fail("make_object_base_type(): error allocating type!"); | ||||
|     } | ||||
| 
 | ||||
|     heap_type->ht_name = name_obj.inc_ref().ptr(); | ||||
| #ifdef PYBIND11_BUILTIN_QUALNAME | ||||
|     heap_type->ht_qualname = name_obj.inc_ref().ptr(); | ||||
| #endif | ||||
| 
 | ||||
|     auto type = &heap_type->ht_type; | ||||
|     auto *type = &heap_type->ht_type; | ||||
|     type->tp_name = name; | ||||
|     type->tp_base = type_incref(&PyBaseObject_Type); | ||||
|     type->tp_basicsize = static_cast<ssize_t>(sizeof(instance)); | ||||
|  | @ -469,8 +491,9 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) { | |||
|     /* Support weak references (needed for the keep_alive feature) */ | ||||
|     type->tp_weaklistoffset = offsetof(instance, weakrefs); | ||||
| 
 | ||||
|     if (PyType_Ready(type) < 0) | ||||
|     if (PyType_Ready(type) < 0) { | ||||
|         pybind11_fail("PyType_Ready failed in make_object_base_type(): " + error_string()); | ||||
|     } | ||||
| 
 | ||||
|     setattr((PyObject *) type, "__module__", str("pybind11_builtins")); | ||||
|     PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); | ||||
|  | @ -482,8 +505,9 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) { | |||
| /// dynamic_attr: Support for `d = instance.__dict__`.
 | ||||
| extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { | ||||
|     PyObject *&dict = *_PyObject_GetDictPtr(self); | ||||
|     if (!dict) | ||||
|     if (!dict) { | ||||
|         dict = PyDict_New(); | ||||
|     } | ||||
|     Py_XINCREF(dict); | ||||
|     return dict; | ||||
| } | ||||
|  | @ -491,7 +515,8 @@ extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { | |||
| /// dynamic_attr: Support for `instance.__dict__ = dict()`.
 | ||||
| extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { | ||||
|     if (!PyDict_Check(new_dict)) { | ||||
|         PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", | ||||
|         PyErr_Format(PyExc_TypeError, | ||||
|                      "__dict__ must be set to a dictionary, not a '%.200s'", | ||||
|                      get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str()); | ||||
|         return -1; | ||||
|     } | ||||
|  | @ -506,6 +531,10 @@ extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void | |||
| extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { | ||||
|     PyObject *&dict = *_PyObject_GetDictPtr(self); | ||||
|     Py_VISIT(dict); | ||||
| // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
 | ||||
| #if PY_VERSION_HEX >= 0x03090000 | ||||
|     Py_VISIT(Py_TYPE(self)); | ||||
| #endif | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -518,17 +547,20 @@ extern "C" inline int pybind11_clear(PyObject *self) { | |||
| 
 | ||||
| /// Give instances of this type a `__dict__` and opt into garbage collection.
 | ||||
| inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { | ||||
|     auto type = &heap_type->ht_type; | ||||
|     auto *type = &heap_type->ht_type; | ||||
|     type->tp_flags |= Py_TPFLAGS_HAVE_GC; | ||||
| #if PY_VERSION_HEX < 0x030B0000 | ||||
|     type->tp_dictoffset = type->tp_basicsize;           // place dict at the end
 | ||||
|     type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
 | ||||
| #else | ||||
|     type->tp_flags |= Py_TPFLAGS_MANAGED_DICT; | ||||
| #endif | ||||
|     type->tp_traverse = pybind11_traverse; | ||||
|     type->tp_clear = pybind11_clear; | ||||
| 
 | ||||
|     static PyGetSetDef getset[] = { | ||||
|         {const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, | ||||
|         {nullptr, nullptr, nullptr, nullptr, nullptr} | ||||
|     }; | ||||
|         {nullptr, nullptr, nullptr, nullptr, nullptr}}; | ||||
|     type->tp_getset = getset; | ||||
| } | ||||
| 
 | ||||
|  | @ -538,12 +570,14 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla | |||
|     type_info *tinfo = nullptr; | ||||
|     for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) { | ||||
|         tinfo = get_type_info((PyTypeObject *) type.ptr()); | ||||
|         if (tinfo && tinfo->get_buffer) | ||||
|         if (tinfo && tinfo->get_buffer) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (view == nullptr || !tinfo || !tinfo->get_buffer) { | ||||
|         if (view) | ||||
|         if (view) { | ||||
|             view->obj = nullptr; | ||||
|         } | ||||
|         PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); | ||||
|         return -1; | ||||
|     } | ||||
|  | @ -561,15 +595,17 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla | |||
|     view->buf = info->ptr; | ||||
|     view->itemsize = info->itemsize; | ||||
|     view->len = view->itemsize; | ||||
|     for (auto s : info->shape) | ||||
|     for (auto s : info->shape) { | ||||
|         view->len *= s; | ||||
|     } | ||||
|     view->readonly = static_cast<int>(info->readonly); | ||||
|     if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) | ||||
|     if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { | ||||
|         view->format = const_cast<char *>(info->format.c_str()); | ||||
|     } | ||||
|     if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { | ||||
|         view->ndim = (int) info->ndim; | ||||
|         view->strides = &info->strides[0]; | ||||
|         view->shape = &info->shape[0]; | ||||
|         view->strides = info->strides.data(); | ||||
|         view->shape = info->shape.data(); | ||||
|     } | ||||
|     Py_INCREF(view->obj); | ||||
|     return 0; | ||||
|  | @ -583,9 +619,6 @@ extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { | |||
| /// Give this type a buffer interface.
 | ||||
| inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { | ||||
|     heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; | ||||
| #if PY_MAJOR_VERSION < 3 | ||||
|     heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; | ||||
| #endif | ||||
| 
 | ||||
|     heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; | ||||
|     heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; | ||||
|  | @ -598,23 +631,20 @@ inline PyObject* make_new_python_type(const type_record &rec) { | |||
| 
 | ||||
|     auto qualname = name; | ||||
|     if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|         qualname = reinterpret_steal<object>( | ||||
|             PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); | ||||
| #else | ||||
|         qualname = str(rec.scope.attr("__qualname__").cast<std::string>() + "." + rec.name); | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|     object module_; | ||||
|     if (rec.scope) { | ||||
|         if (hasattr(rec.scope, "__module__")) | ||||
|         if (hasattr(rec.scope, "__module__")) { | ||||
|             module_ = rec.scope.attr("__module__"); | ||||
|         else if (hasattr(rec.scope, "__name__")) | ||||
|         } else if (hasattr(rec.scope, "__name__")) { | ||||
|             module_ = rec.scope.attr("__name__"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     auto full_name = c_str( | ||||
|     const auto *full_name = c_str( | ||||
| #if !defined(PYPY_VERSION) | ||||
|         module_ ? str(module_).cast<std::string>() + "." + rec.name : | ||||
| #endif | ||||
|  | @ -631,32 +661,33 @@ inline PyObject* make_new_python_type(const type_record &rec) { | |||
| 
 | ||||
|     auto &internals = get_internals(); | ||||
|     auto bases = tuple(rec.bases); | ||||
|     auto base = (bases.empty()) ? internals.instance_base | ||||
|                                     : bases[0].ptr(); | ||||
|     auto *base = (bases.empty()) ? internals.instance_base : bases[0].ptr(); | ||||
| 
 | ||||
|     /* Danger zone: from now (and until PyType_Ready), make sure to
 | ||||
|        issue no Python C API calls which could potentially invoke the | ||||
|        garbage collector (the GC will call type_traverse(), which will in | ||||
|        turn find the newly constructed type in an invalid state) */ | ||||
|     auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() | ||||
|                                          : internals.default_metaclass; | ||||
|     auto *metaclass | ||||
|         = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() : internals.default_metaclass; | ||||
| 
 | ||||
|     auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); | ||||
|     if (!heap_type) | ||||
|     auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); | ||||
|     if (!heap_type) { | ||||
|         pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); | ||||
|     } | ||||
| 
 | ||||
|     heap_type->ht_name = name.release().ptr(); | ||||
| #ifdef PYBIND11_BUILTIN_QUALNAME | ||||
|     heap_type->ht_qualname = qualname.inc_ref().ptr(); | ||||
| #endif | ||||
| 
 | ||||
|     auto type = &heap_type->ht_type; | ||||
|     auto *type = &heap_type->ht_type; | ||||
|     type->tp_name = full_name; | ||||
|     type->tp_doc = tp_doc; | ||||
|     type->tp_base = type_incref((PyTypeObject *) base); | ||||
|     type->tp_basicsize = static_cast<ssize_t>(sizeof(instance)); | ||||
|     if (!bases.empty()) | ||||
|     if (!bases.empty()) { | ||||
|         type->tp_bases = bases.release().ptr(); | ||||
|     } | ||||
| 
 | ||||
|     /* Don't inherit base __init__ */ | ||||
|     type->tp_init = pybind11_object_init; | ||||
|  | @ -665,40 +696,42 @@ inline PyObject* make_new_python_type(const type_record &rec) { | |||
|     type->tp_as_number = &heap_type->as_number; | ||||
|     type->tp_as_sequence = &heap_type->as_sequence; | ||||
|     type->tp_as_mapping = &heap_type->as_mapping; | ||||
| #if PY_VERSION_HEX >= 0x03050000 | ||||
|     type->tp_as_async = &heap_type->as_async; | ||||
| #endif | ||||
| 
 | ||||
|     /* Flags */ | ||||
|     type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; | ||||
| #if PY_MAJOR_VERSION < 3 | ||||
|     type->tp_flags |= Py_TPFLAGS_CHECKTYPES; | ||||
| #endif | ||||
|     if (!rec.is_final) | ||||
|     if (!rec.is_final) { | ||||
|         type->tp_flags |= Py_TPFLAGS_BASETYPE; | ||||
|     } | ||||
| 
 | ||||
|     if (rec.dynamic_attr) | ||||
|     if (rec.dynamic_attr) { | ||||
|         enable_dynamic_attributes(heap_type); | ||||
|     } | ||||
| 
 | ||||
|     if (rec.buffer_protocol) | ||||
|     if (rec.buffer_protocol) { | ||||
|         enable_buffer_protocol(heap_type); | ||||
|     } | ||||
| 
 | ||||
|     if (rec.custom_type_setup_callback) | ||||
|     if (rec.custom_type_setup_callback) { | ||||
|         rec.custom_type_setup_callback(heap_type); | ||||
|     } | ||||
| 
 | ||||
|     if (PyType_Ready(type) < 0) | ||||
|         pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); | ||||
|     if (PyType_Ready(type) < 0) { | ||||
|         pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string()); | ||||
|     } | ||||
| 
 | ||||
|     assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); | ||||
| 
 | ||||
|     /* Register type with the parent scope */ | ||||
|     if (rec.scope) | ||||
|     if (rec.scope) { | ||||
|         setattr(rec.scope, rec.name, (PyObject *) type); | ||||
|     else | ||||
|     } else { | ||||
|         Py_INCREF(type); // Keep it alive forever (reference leak)
 | ||||
|     } | ||||
| 
 | ||||
|     if (module_) // Needed by pydoc
 | ||||
|     if (module_) { // Needed by pydoc
 | ||||
|         setattr((PyObject *) type, "__module__", module_); | ||||
|     } | ||||
| 
 | ||||
|     PYBIND11_SET_OLDPY_QUALNAME(type, qualname); | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,19 +10,19 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #define PYBIND11_VERSION_MAJOR 2 | ||||
| #define PYBIND11_VERSION_MINOR 9 | ||||
| #define PYBIND11_VERSION_PATCH 1 | ||||
| #define PYBIND11_VERSION_MINOR 10 | ||||
| #define PYBIND11_VERSION_PATCH 0 | ||||
| 
 | ||||
| // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
 | ||||
| // Additional convention: 0xD = dev
 | ||||
| #define PYBIND11_VERSION_HEX 0x02090100 | ||||
| #define PYBIND11_VERSION_HEX 0x020A0000 | ||||
| 
 | ||||
| #define PYBIND11_NAMESPACE_BEGIN(name) namespace name { | ||||
| #define PYBIND11_NAMESPACE_END(name) } | ||||
| 
 | ||||
| // Robust support for some features and loading modules compiled against different pybind versions
 | ||||
| // requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on
 | ||||
| // the main `pybind11` namespace.
 | ||||
| // requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute
 | ||||
| // on the main `pybind11` namespace.
 | ||||
| #if !defined(PYBIND11_NAMESPACE) | ||||
| #    ifdef __GNUG__ | ||||
| #        define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) | ||||
|  | @ -38,15 +38,17 @@ | |||
| #            define PYBIND11_CPP17 | ||||
| #            if __cplusplus >= 202002L | ||||
| #                define PYBIND11_CPP20 | ||||
| // Please update tests/pybind11_tests.cpp `cpp_std()` when adding a macro here.
 | ||||
| #            endif | ||||
| #        endif | ||||
| #    endif | ||||
| #elif defined(_MSC_VER) && __cplusplus == 199711L | ||||
| // MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented)
 | ||||
| // Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer
 | ||||
| // MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully
 | ||||
| // implemented). Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3
 | ||||
| // or newer.
 | ||||
| #    if _MSVC_LANG >= 201402L | ||||
| #        define PYBIND11_CPP14 | ||||
| #    if _MSVC_LANG > 201402L && _MSC_VER >= 1910 | ||||
| #        if _MSVC_LANG > 201402L | ||||
| #            define PYBIND11_CPP17 | ||||
| #            if _MSVC_LANG >= 202002L | ||||
| #                define PYBIND11_CPP20 | ||||
|  | @ -80,10 +82,8 @@ | |||
| #        error pybind11 requires gcc 4.8 or newer | ||||
| #    endif | ||||
| #elif defined(_MSC_VER) | ||||
| // Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features
 | ||||
| // (e.g. std::negation) added in 2015u3:
 | ||||
| #  if _MSC_FULL_VER < 190024210 | ||||
| #    error pybind11 requires MSVC 2015 update 3 or newer | ||||
| #    if _MSC_VER < 1910 | ||||
| #        error pybind11 2.10+ requires MSVC 2017 or newer | ||||
| #    endif | ||||
| #endif | ||||
| 
 | ||||
|  | @ -98,7 +98,8 @@ | |||
| #if !defined(PYBIND11_EXPORT_EXCEPTION) | ||||
| #    ifdef __MINGW32__ | ||||
| // workaround for:
 | ||||
| // error: 'dllexport' implies default visibility, but xxx has already been declared with a different visibility
 | ||||
| // error: 'dllexport' implies default visibility, but xxx has already been declared with a
 | ||||
| // different visibility
 | ||||
| #        define PYBIND11_EXPORT_EXCEPTION | ||||
| #    else | ||||
| #        define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT | ||||
|  | @ -111,8 +112,8 @@ | |||
| // However, the measured shared-library size saving when using noinline are only
 | ||||
| // 1.7% for CUDA, -0.2% for GCC7, and 0.0% for GCC8 (using -DCMAKE_BUILD_TYPE=MinSizeRel,
 | ||||
| // the default under pybind11/tests).
 | ||||
| #if !defined(PYBIND11_NOINLINE_FORCED) && \ | ||||
|     (defined(__CUDACC__) || (defined(__GNUC__) && (__GNUC__ == 7 || __GNUC__ == 8))) | ||||
| #if !defined(PYBIND11_NOINLINE_FORCED)                                                            \ | ||||
|     && (defined(__CUDACC__) || (defined(__GNUC__) && (__GNUC__ == 7 || __GNUC__ == 8))) | ||||
| #    define PYBIND11_NOINLINE_DISABLED | ||||
| #endif | ||||
| 
 | ||||
|  | @ -147,15 +148,12 @@ | |||
| 
 | ||||
| /* Don't let Python.h #define (v)snprintf as macro because they are implemented
 | ||||
|    properly in Visual Studio since 2015. */ | ||||
| #if defined(_MSC_VER) && _MSC_VER >= 1900 | ||||
| #if defined(_MSC_VER) | ||||
| #    define HAVE_SNPRINTF 1 | ||||
| #endif | ||||
| 
 | ||||
| /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
 | ||||
| #if defined(_MSC_VER) | ||||
| #  if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) | ||||
| #    define HAVE_ROUND 1 | ||||
| #  endif | ||||
| #    pragma warning(push) | ||||
| // C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
 | ||||
| #    pragma warning(disable : 4505) | ||||
|  | @ -211,8 +209,10 @@ | |||
| #    define PYBIND11_HAS_U8STRING | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #include <Python.h> | ||||
| #if PY_VERSION_HEX < 0x03060000 | ||||
| #    error "PYTHON < 3.6 IS UNSUPPORTED. pybind11 v2.9 was the last to support Python 2 and 3.5." | ||||
| #endif | ||||
| #include <frameobject.h> | ||||
| #include <pythread.h> | ||||
| 
 | ||||
|  | @ -243,16 +243,16 @@ | |||
| 
 | ||||
| #include <cstddef> | ||||
| #include <cstring> | ||||
| #include <forward_list> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <stdexcept> | ||||
| #include <exception> | ||||
| #include <unordered_set> | ||||
| #include <unordered_map> | ||||
| #include <forward_list> | ||||
| #include <memory> | ||||
| #include <typeindex> | ||||
| #include <stdexcept> | ||||
| #include <string> | ||||
| #include <type_traits> | ||||
| #include <typeindex> | ||||
| #include <unordered_map> | ||||
| #include <unordered_set> | ||||
| #include <vector> | ||||
| #if defined(__has_include) | ||||
| #    if __has_include(<version>) | ||||
| #        include <version> | ||||
|  | @ -268,11 +268,11 @@ | |||
| // If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and
 | ||||
| //               pybind11::isinstance<str>() is true only for pybind11::str.
 | ||||
| //               However, for Python 2 only (!), the pybind11::str caster
 | ||||
| //               implicitly decodes bytes to PyUnicodeObject. This is to ease
 | ||||
| //               implicitly decoded bytes to PyUnicodeObject. This was to ease
 | ||||
| //               the transition from the legacy behavior to the non-permissive
 | ||||
| //               behavior.
 | ||||
| 
 | ||||
| #if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
 | ||||
| /// Compatibility macros for Python 2 / Python 3 versions TODO: remove
 | ||||
| #define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) | ||||
| #define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check | ||||
| #define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION | ||||
|  | @ -300,57 +300,16 @@ | |||
|     extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name();                   \ | ||||
|     extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() | ||||
| 
 | ||||
| #else | ||||
| #define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) | ||||
| #define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check | ||||
| #define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION | ||||
| #define PYBIND11_BYTES_CHECK PyString_Check | ||||
| #define PYBIND11_BYTES_FROM_STRING PyString_FromString | ||||
| #define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize | ||||
| #define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize | ||||
| #define PYBIND11_BYTES_AS_STRING PyString_AsString | ||||
| #define PYBIND11_BYTES_SIZE PyString_Size | ||||
| #define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) | ||||
| #define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) | ||||
| #define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed.
 | ||||
| #define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed.
 | ||||
| #define PYBIND11_BYTES_NAME "str" | ||||
| #define PYBIND11_STRING_NAME "unicode" | ||||
| #define PYBIND11_SLICE_OBJECT PySliceObject | ||||
| #define PYBIND11_FROM_STRING PyString_FromString | ||||
| #define PYBIND11_STR_TYPE ::pybind11::bytes | ||||
| #define PYBIND11_BOOL_ATTR "__nonzero__" | ||||
| #define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) | ||||
| #define PYBIND11_BUILTINS_MODULE "__builtin__" | ||||
| // Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy.
 | ||||
| // See comment for PYBIND11_MODULE below for why this is marked "maybe unused".
 | ||||
| #define PYBIND11_PLUGIN_IMPL(name) \ | ||||
|     static PyObject *pybind11_init_wrapper();                           \ | ||||
|     extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT void init##name(); \ | ||||
|     extern "C" PYBIND11_EXPORT void init##name() {                      \ | ||||
|         (void)pybind11_init_wrapper();                                  \ | ||||
|     }                                                                   \ | ||||
|     PyObject *pybind11_init_wrapper() | ||||
| #endif | ||||
| 
 | ||||
| #if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 | ||||
| extern "C" { | ||||
|     struct _Py_atomic_address { void *value; }; | ||||
|     PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code
 | ||||
| #define PYBIND11_STRINGIFY(x) #x | ||||
| #define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) | ||||
| #define PYBIND11_CONCAT(first, second) first##second | ||||
| #define PYBIND11_ENSURE_INTERNALS_READY \ | ||||
|     pybind11::detail::get_internals(); | ||||
| #define PYBIND11_ENSURE_INTERNALS_READY pybind11::detail::get_internals(); | ||||
| 
 | ||||
| #define PYBIND11_CHECK_PYTHON_VERSION                                                             \ | ||||
|     {                                                                                             \ | ||||
|         const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION)         \ | ||||
|             "." PYBIND11_TOSTRING(PY_MINOR_VERSION);                           \ | ||||
|         const char *compiled_ver                                                                  \ | ||||
|             = PYBIND11_TOSTRING(PY_MAJOR_VERSION) "." PYBIND11_TOSTRING(PY_MINOR_VERSION);        \ | ||||
|         const char *runtime_ver = Py_GetVersion();                                                \ | ||||
|         size_t len = std::strlen(compiled_ver);                                                   \ | ||||
|         if (std::strncmp(runtime_ver, compiled_ver, len) != 0                                     \ | ||||
|  | @ -358,34 +317,21 @@ extern "C" { | |||
|             PyErr_Format(PyExc_ImportError,                                                       \ | ||||
|                          "Python version mismatch: module was compiled for Python %s, "           \ | ||||
|                          "but the interpreter version is incompatible: %s.",                      \ | ||||
|                 compiled_ver, runtime_ver);                                    \ | ||||
|                          compiled_ver,                                                            \ | ||||
|                          runtime_ver);                                                            \ | ||||
|             return nullptr;                                                                       \ | ||||
|         }                                                                                         \ | ||||
|     } | ||||
| 
 | ||||
| #if PY_VERSION_HEX >= 0x03030000 | ||||
| 
 | ||||
| #define PYBIND11_CATCH_INIT_EXCEPTIONS                                                            \ | ||||
|     catch (pybind11::error_already_set & e) {                                                     \ | ||||
|         pybind11::raise_from(e, PyExc_ImportError, "initialization failed");                      \ | ||||
|         return nullptr;                                                                           \ | ||||
|         } catch (const std::exception &e) {                                      \ | ||||
|             PyErr_SetString(PyExc_ImportError, e.what());                        \ | ||||
|             return nullptr;                                                      \ | ||||
|     }                                                                                             \ | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| #define PYBIND11_CATCH_INIT_EXCEPTIONS \ | ||||
|         catch (pybind11::error_already_set &e) {                               \ | ||||
|     catch (const std::exception &e) {                                                             \ | ||||
|         PyErr_SetString(PyExc_ImportError, e.what());                                             \ | ||||
|         return nullptr;                                                                           \ | ||||
|         } catch (const std::exception &e) {                                    \ | ||||
|             PyErr_SetString(PyExc_ImportError, e.what());                      \ | ||||
|             return nullptr;                                                    \ | ||||
|         }                                                                      \ | ||||
| 
 | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
| /** \rst
 | ||||
|     ***Deprecated in favor of PYBIND11_MODULE*** | ||||
|  | @ -410,7 +356,8 @@ extern "C" { | |||
|         PYBIND11_ENSURE_INTERNALS_READY                                                           \ | ||||
|         try {                                                                                     \ | ||||
|             return pybind11_init();                                                               \ | ||||
|         } PYBIND11_CATCH_INIT_EXCEPTIONS                                       \ | ||||
|         }                                                                                         \ | ||||
|         PYBIND11_CATCH_INIT_EXCEPTIONS                                                            \ | ||||
|     }                                                                                             \ | ||||
|     PyObject *pybind11_init() | ||||
| 
 | ||||
|  | @ -481,7 +428,7 @@ enum class return_value_policy : uint8_t { | |||
| 
 | ||||
|     /** Reference an existing object (i.e. do not create a new copy) and take
 | ||||
|         ownership. Python will call the destructor and delete operator when the | ||||
|         object’s reference count reaches zero. Undefined behavior ensues when | ||||
|         object's reference count reaches zero. Undefined behavior ensues when | ||||
|         the C++ side does the same.. */ | ||||
|     take_ownership, | ||||
| 
 | ||||
|  | @ -497,7 +444,7 @@ enum class return_value_policy : uint8_t { | |||
|     move, | ||||
| 
 | ||||
|     /** Reference an existing object, but do not take ownership. The C++ side
 | ||||
|         is responsible for managing the object’s lifetime and deallocating it | ||||
|         is responsible for managing the object's lifetime and deallocating it | ||||
|         when it is no longer used. Warning: undefined behavior will ensue when | ||||
|         the C++ side deletes an object that is still referenced and used by | ||||
|         Python. */ | ||||
|  | @ -506,7 +453,7 @@ enum class return_value_policy : uint8_t { | |||
|     /** This policy only applies to methods and properties. It references the
 | ||||
|         object without taking ownership similar to the above | ||||
|         return_value_policy::reference policy. In contrast to that policy, the | ||||
|         function or property’s implicit this argument (called the parent) is | ||||
|         function or property's implicit this argument (called the parent) is | ||||
|         considered to be the the owner of the return value (the child). | ||||
|         pybind11 then couples the lifetime of the parent to the child via a | ||||
|         reference relationship that ensures that the parent cannot be garbage | ||||
|  | @ -518,10 +465,14 @@ enum class return_value_policy : uint8_t { | |||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } | ||||
| inline static constexpr int log2(size_t n, int k = 0) { | ||||
|     return (n <= 1) ? k : log2(n >> 1, k + 1); | ||||
| } | ||||
| 
 | ||||
| // Returns the size as a multiple of sizeof(void *), rounded up.
 | ||||
| inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } | ||||
| inline static constexpr size_t size_in_ptrs(size_t s) { | ||||
|     return 1 + ((s - 1) >> log2(sizeof(void *))); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * The space to allocate for simple layout instance holders (see below) in multiple of the size of | ||||
|  | @ -559,21 +510,21 @@ struct instance { | |||
|     /**
 | ||||
|      * An instance has two possible value/holder layouts. | ||||
|      * | ||||
|      * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer | ||||
|      * and the holder object governing that pointer, i.e. [val1*][holder].  This layout is applied | ||||
|      * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's | ||||
|      * holder will fit in the default space (which is large enough to hold either a std::unique_ptr | ||||
|      * or std::shared_ptr). | ||||
|      * Simple layout (when this flag is true), means the `simple_value_holder` is set with a | ||||
|      * pointer and the holder object governing that pointer, i.e. [val1*][holder].  This layout is | ||||
|      * applied whenever there is no python-side multiple inheritance of bound C++ types *and* the | ||||
|      * type's holder will fit in the default space (which is large enough to hold either a | ||||
|      * std::unique_ptr or std::shared_ptr). | ||||
|      * | ||||
|      * Non-simple layout applies when using custom holders that require more space than `shared_ptr` | ||||
|      * (which is typically the size of two pointers), or when multiple inheritance is used on the | ||||
|      * python side.  Non-simple layout allocates the required amount of memory to have multiple | ||||
|      * bound C++ classes as parents.  Under this layout, `nonsimple.values_and_holders` is set to a | ||||
|      * pointer to allocated space of the required space to hold a sequence of value pointers and | ||||
|      * holders followed `status`, a set of bit flags (1 byte each), i.e. | ||||
|      * [val1*][holder1][val2*][holder2]...[bb...]  where each [block] is rounded up to a multiple of | ||||
|      * `sizeof(void *)`.  `nonsimple.status` is, for convenience, a pointer to the | ||||
|      * beginning of the [bb...] block (but not independently allocated). | ||||
|      * Non-simple layout applies when using custom holders that require more space than | ||||
|      * `shared_ptr` (which is typically the size of two pointers), or when multiple inheritance is | ||||
|      * used on the python side.  Non-simple layout allocates the required amount of memory to have | ||||
|      * multiple bound C++ classes as parents.  Under this layout, `nonsimple.values_and_holders` is | ||||
|      * set to a pointer to allocated space of the required space to hold a sequence of value | ||||
|      * pointers and holders followed `status`, a set of bit flags (1 byte each), i.e. | ||||
|      * [val1*][holder1][val2*][holder2]...[bb...]  where each [block] is rounded up to a multiple | ||||
|      * of `sizeof(void *)`.  `nonsimple.status` is, for convenience, a pointer to the beginning of | ||||
|      * the [bb...] block (but not independently allocated). | ||||
|      * | ||||
|      * Status bits indicate whether the associated holder is constructed (& | ||||
|      * status_holder_constructed) and whether the value pointer is registered (& | ||||
|  | @ -587,7 +538,8 @@ struct instance { | |||
|     /// If true, get_internals().patients has an entry for this object
 | ||||
|     bool has_patients : 1; | ||||
| 
 | ||||
|     /// Initializes all of the above type/values/holders data (but not the instance values themselves)
 | ||||
|     /// Initializes all of the above type/values/holders data (but not the instance values
 | ||||
|     /// themselves)
 | ||||
|     void allocate_layout(); | ||||
| 
 | ||||
|     /// Destroys/deallocates all of the above
 | ||||
|  | @ -596,26 +548,32 @@ struct instance { | |||
|     /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type`
 | ||||
|     /// omitted).  Returns a default-constructed (with `.inst = nullptr`) object on failure if
 | ||||
|     /// `throw_if_missing` is false.
 | ||||
|     value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); | ||||
|     value_and_holder get_value_and_holder(const type_info *find_type = nullptr, | ||||
|                                           bool throw_if_missing = true); | ||||
| 
 | ||||
|     /// Bit values for the non-simple status flags
 | ||||
|     static constexpr uint8_t status_holder_constructed = 1; | ||||
|     static constexpr uint8_t status_instance_registered = 2; | ||||
| }; | ||||
| 
 | ||||
| static_assert(std::is_standard_layout<instance>::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); | ||||
| static_assert(std::is_standard_layout<instance>::value, | ||||
|               "Internal error: `pybind11::detail::instance` is not standard layout!"); | ||||
| 
 | ||||
| /// from __cpp_future__ import (convenient aliases from C++14/17)
 | ||||
| #if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) | ||||
| using std::enable_if_t; | ||||
| #if defined(PYBIND11_CPP14) | ||||
| using std::conditional_t; | ||||
| using std::enable_if_t; | ||||
| using std::remove_cv_t; | ||||
| using std::remove_reference_t; | ||||
| #else | ||||
| template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type; | ||||
| template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type; | ||||
| template <typename T> using remove_cv_t = typename std::remove_cv<T>::type; | ||||
| template <typename T> using remove_reference_t = typename std::remove_reference<T>::type; | ||||
| template <bool B, typename T = void> | ||||
| using enable_if_t = typename std::enable_if<B, T>::type; | ||||
| template <bool B, typename T, typename F> | ||||
| using conditional_t = typename std::conditional<B, T, F>::type; | ||||
| template <typename T> | ||||
| using remove_cv_t = typename std::remove_cv<T>::type; | ||||
| template <typename T> | ||||
| using remove_reference_t = typename std::remove_reference<T>::type; | ||||
| #endif | ||||
| 
 | ||||
| #if defined(PYBIND11_CPP20) | ||||
|  | @ -635,110 +593,185 @@ using remove_cvref_t = typename remove_cvref<T>::type; | |||
| using std::index_sequence; | ||||
| using std::make_index_sequence; | ||||
| #else | ||||
| template<size_t ...> struct index_sequence  { }; | ||||
| template<size_t N, size_t ...S> struct make_index_sequence_impl : make_index_sequence_impl <N - 1, N - 1, S...> { }; | ||||
| template<size_t ...S> struct make_index_sequence_impl <0, S...> { using type = index_sequence<S...>; }; | ||||
| template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type; | ||||
| template <size_t...> | ||||
| struct index_sequence {}; | ||||
| template <size_t N, size_t... S> | ||||
| struct make_index_sequence_impl : make_index_sequence_impl<N - 1, N - 1, S...> {}; | ||||
| template <size_t... S> | ||||
| struct make_index_sequence_impl<0, S...> { | ||||
|     using type = index_sequence<S...>; | ||||
| }; | ||||
| template <size_t N> | ||||
| using make_index_sequence = typename make_index_sequence_impl<N>::type; | ||||
| #endif | ||||
| 
 | ||||
| /// Make an index sequence of the indices of true arguments
 | ||||
| template <typename ISeq, size_t, bool...> struct select_indices_impl { using type = ISeq; }; | ||||
| template <size_t... IPrev, size_t I, bool B, bool... Bs> struct select_indices_impl<index_sequence<IPrev...>, I, B, Bs...> | ||||
|     : select_indices_impl<conditional_t<B, index_sequence<IPrev..., I>, index_sequence<IPrev...>>, I + 1, Bs...> {}; | ||||
| template <bool... Bs> using select_indices = typename select_indices_impl<index_sequence<>, 0, Bs...>::type; | ||||
| template <typename ISeq, size_t, bool...> | ||||
| struct select_indices_impl { | ||||
|     using type = ISeq; | ||||
| }; | ||||
| template <size_t... IPrev, size_t I, bool B, bool... Bs> | ||||
| struct select_indices_impl<index_sequence<IPrev...>, I, B, Bs...> | ||||
|     : select_indices_impl<conditional_t<B, index_sequence<IPrev..., I>, index_sequence<IPrev...>>, | ||||
|                           I + 1, | ||||
|                           Bs...> {}; | ||||
| template <bool... Bs> | ||||
| using select_indices = typename select_indices_impl<index_sequence<>, 0, Bs...>::type; | ||||
| 
 | ||||
| /// Backports of std::bool_constant and std::negation to accommodate older compilers
 | ||||
| template <bool B> using bool_constant = std::integral_constant<bool, B>; | ||||
| template <typename T> struct negation : bool_constant<!T::value> { }; | ||||
| template <bool B> | ||||
| using bool_constant = std::integral_constant<bool, B>; | ||||
| template <typename T> | ||||
| struct negation : bool_constant<!T::value> {}; | ||||
| 
 | ||||
| // PGI/Intel cannot detect operator delete with the "compatible" void_t impl, so
 | ||||
| // using the new one (C++14 defect, so generally works on newer compilers, even
 | ||||
| // if not in C++17 mode)
 | ||||
| #if defined(__PGIC__) || defined(__INTEL_COMPILER) | ||||
| template<typename... > using void_t = void; | ||||
| template <typename...> | ||||
| using void_t = void; | ||||
| #else | ||||
| template <typename...> struct void_t_impl { using type = void; }; | ||||
| template <typename... Ts> using void_t = typename void_t_impl<Ts...>::type; | ||||
| template <typename...> | ||||
| struct void_t_impl { | ||||
|     using type = void; | ||||
| }; | ||||
| template <typename... Ts> | ||||
| using void_t = typename void_t_impl<Ts...>::type; | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /// Compile-time all/any/none of that check the boolean value of all template types
 | ||||
| #if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) | ||||
| template <class... Ts> using all_of = bool_constant<(Ts::value && ...)>; | ||||
| template <class... Ts> using any_of = bool_constant<(Ts::value || ...)>; | ||||
| template <class... Ts> | ||||
| using all_of = bool_constant<(Ts::value && ...)>; | ||||
| template <class... Ts> | ||||
| using any_of = bool_constant<(Ts::value || ...)>; | ||||
| #elif !defined(_MSC_VER) | ||||
| template <bool...> struct bools {}; | ||||
| template <class... Ts> using all_of = std::is_same< | ||||
|     bools<Ts::value..., true>, | ||||
|     bools<true, Ts::value...>>; | ||||
| template <class... Ts> using any_of = negation<all_of<negation<Ts>...>>; | ||||
| template <bool...> | ||||
| struct bools {}; | ||||
| template <class... Ts> | ||||
| using all_of = std::is_same<bools<Ts::value..., true>, bools<true, Ts::value...>>; | ||||
| template <class... Ts> | ||||
| using any_of = negation<all_of<negation<Ts>...>>; | ||||
| #else | ||||
| // MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit
 | ||||
| // at a slight loss of compilation efficiency).
 | ||||
| template <class... Ts> using all_of = std::conjunction<Ts...>; | ||||
| template <class... Ts> using any_of = std::disjunction<Ts...>; | ||||
| template <class... Ts> | ||||
| using all_of = std::conjunction<Ts...>; | ||||
| template <class... Ts> | ||||
| using any_of = std::disjunction<Ts...>; | ||||
| #endif | ||||
| template <class... Ts> using none_of = negation<any_of<Ts...>>; | ||||
| template <class... Ts> | ||||
| using none_of = negation<any_of<Ts...>>; | ||||
| 
 | ||||
| template <class T, template<class> class... Predicates> using satisfies_all_of = all_of<Predicates<T>...>; | ||||
| template <class T, template<class> class... Predicates> using satisfies_any_of = any_of<Predicates<T>...>; | ||||
| template <class T, template<class> class... Predicates> using satisfies_none_of = none_of<Predicates<T>...>; | ||||
| template <class T, template <class> class... Predicates> | ||||
| using satisfies_all_of = all_of<Predicates<T>...>; | ||||
| template <class T, template <class> class... Predicates> | ||||
| using satisfies_any_of = any_of<Predicates<T>...>; | ||||
| template <class T, template <class> class... Predicates> | ||||
| using satisfies_none_of = none_of<Predicates<T>...>; | ||||
| 
 | ||||
| /// Strip the class from a method type
 | ||||
| template <typename T> struct remove_class { }; | ||||
| template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { using type = R (A...); }; | ||||
| template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...) const> { using type = R (A...); }; | ||||
| template <typename T> | ||||
| struct remove_class {}; | ||||
| template <typename C, typename R, typename... A> | ||||
| struct remove_class<R (C::*)(A...)> { | ||||
|     using type = R(A...); | ||||
| }; | ||||
| template <typename C, typename R, typename... A> | ||||
| struct remove_class<R (C::*)(A...) const> { | ||||
|     using type = R(A...); | ||||
| }; | ||||
| 
 | ||||
| /// Helper template to strip away type modifiers
 | ||||
| template <typename T> struct intrinsic_type                       { using type = T; }; | ||||
| template <typename T> struct intrinsic_type<const T>              { using type = typename intrinsic_type<T>::type; }; | ||||
| template <typename T> struct intrinsic_type<T*>                   { using type = typename intrinsic_type<T>::type; }; | ||||
| template <typename T> struct intrinsic_type<T&>                   { using type = typename intrinsic_type<T>::type; }; | ||||
| template <typename T> struct intrinsic_type<T&&>                  { using type = typename intrinsic_type<T>::type; }; | ||||
| template <typename T, size_t N> struct intrinsic_type<const T[N]> { using type = typename intrinsic_type<T>::type; }; | ||||
| template <typename T, size_t N> struct intrinsic_type<T[N]>       { using type = typename intrinsic_type<T>::type; }; | ||||
| template <typename T> using intrinsic_t = typename intrinsic_type<T>::type; | ||||
| template <typename T> | ||||
| struct intrinsic_type { | ||||
|     using type = T; | ||||
| }; | ||||
| template <typename T> | ||||
| struct intrinsic_type<const T> { | ||||
|     using type = typename intrinsic_type<T>::type; | ||||
| }; | ||||
| template <typename T> | ||||
| struct intrinsic_type<T *> { | ||||
|     using type = typename intrinsic_type<T>::type; | ||||
| }; | ||||
| template <typename T> | ||||
| struct intrinsic_type<T &> { | ||||
|     using type = typename intrinsic_type<T>::type; | ||||
| }; | ||||
| template <typename T> | ||||
| struct intrinsic_type<T &&> { | ||||
|     using type = typename intrinsic_type<T>::type; | ||||
| }; | ||||
| template <typename T, size_t N> | ||||
| struct intrinsic_type<const T[N]> { | ||||
|     using type = typename intrinsic_type<T>::type; | ||||
| }; | ||||
| template <typename T, size_t N> | ||||
| struct intrinsic_type<T[N]> { | ||||
|     using type = typename intrinsic_type<T>::type; | ||||
| }; | ||||
| template <typename T> | ||||
| using intrinsic_t = typename intrinsic_type<T>::type; | ||||
| 
 | ||||
| /// Helper type to replace 'void' in some expressions
 | ||||
| struct void_type {}; | ||||
| 
 | ||||
| /// Helper template which holds a list of types
 | ||||
| template <typename...> struct type_list { }; | ||||
| template <typename...> | ||||
| struct type_list {}; | ||||
| 
 | ||||
| /// Compile-time integer sum
 | ||||
| #ifdef __cpp_fold_expressions | ||||
| template <typename... Ts> constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } | ||||
| template <typename... Ts> | ||||
| constexpr size_t constexpr_sum(Ts... ns) { | ||||
|     return (0 + ... + size_t{ns}); | ||||
| } | ||||
| #else | ||||
| constexpr size_t constexpr_sum() { return 0; } | ||||
| template <typename T, typename... Ts> | ||||
| constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } | ||||
| constexpr size_t constexpr_sum(T n, Ts... ns) { | ||||
|     return size_t{n} + constexpr_sum(ns...); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(constexpr_impl) | ||||
| /// Implementation details for constexpr functions
 | ||||
| constexpr int first(int i) { return i; } | ||||
| template <typename T, typename... Ts> | ||||
| constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } | ||||
| constexpr int first(int i, T v, Ts... vs) { | ||||
|     return v ? i : first(i + 1, vs...); | ||||
| } | ||||
| 
 | ||||
| constexpr int last(int /*i*/, int result) { return result; } | ||||
| template <typename T, typename... Ts> | ||||
| constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } | ||||
| constexpr int last(int i, int result, T v, Ts... vs) { | ||||
|     return last(i + 1, v ? i : result, vs...); | ||||
| } | ||||
| PYBIND11_NAMESPACE_END(constexpr_impl) | ||||
| 
 | ||||
| /// Return the index of the first type in Ts which satisfies Predicate<T>.  Returns sizeof...(Ts) if
 | ||||
| /// none match.
 | ||||
| /// Return the index of the first type in Ts which satisfies Predicate<T>.
 | ||||
| /// Returns sizeof...(Ts) if none match.
 | ||||
| template <template <typename> class Predicate, typename... Ts> | ||||
| constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate<Ts>::value...); } | ||||
| constexpr int constexpr_first() { | ||||
|     return constexpr_impl::first(0, Predicate<Ts>::value...); | ||||
| } | ||||
| 
 | ||||
| /// Return the index of the last type in Ts which satisfies Predicate<T>, or -1 if none match.
 | ||||
| template <template <typename> class Predicate, typename... Ts> | ||||
| constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate<Ts>::value...); } | ||||
| constexpr int constexpr_last() { | ||||
|     return constexpr_impl::last(0, -1, Predicate<Ts>::value...); | ||||
| } | ||||
| 
 | ||||
| /// Return the Nth element from the parameter pack
 | ||||
| template <size_t N, typename T, typename... Ts> | ||||
| struct pack_element { using type = typename pack_element<N - 1, Ts...>::type; }; | ||||
| struct pack_element { | ||||
|     using type = typename pack_element<N - 1, Ts...>::type; | ||||
| }; | ||||
| template <typename T, typename... Ts> | ||||
| struct pack_element<0, T, Ts...> { using type = T; }; | ||||
| struct pack_element<0, T, Ts...> { | ||||
|     using type = T; | ||||
| }; | ||||
| 
 | ||||
| /// Return the one and only type which matches the predicate, or Default if none match.
 | ||||
| /// If more than one type matches the predicate, fail at compile-time.
 | ||||
|  | @ -751,39 +784,54 @@ struct exactly_one { | |||
|     using type = conditional_t<found, typename pack_element<index, Ts...>::type, Default>; | ||||
| }; | ||||
| template <template <typename> class P, typename Default> | ||||
| struct exactly_one<P, Default> { using type = Default; }; | ||||
| struct exactly_one<P, Default> { | ||||
|     using type = Default; | ||||
| }; | ||||
| 
 | ||||
| template <template <typename> class Predicate, typename Default, typename... Ts> | ||||
| using exactly_one_t = typename exactly_one<Predicate, Default, Ts...>::type; | ||||
| 
 | ||||
| /// Defer the evaluation of type T until types Us are instantiated
 | ||||
| template <typename T, typename... /*Us*/> struct deferred_type { using type = T; }; | ||||
| template <typename T, typename... Us> using deferred_t = typename deferred_type<T, Us...>::type; | ||||
| template <typename T, typename... /*Us*/> | ||||
| struct deferred_type { | ||||
|     using type = T; | ||||
| }; | ||||
| template <typename T, typename... Us> | ||||
| using deferred_t = typename deferred_type<T, Us...>::type; | ||||
| 
 | ||||
| /// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of<T, T>::value == false`,
 | ||||
| /// unlike `std::is_base_of`)
 | ||||
| template <typename Base, typename Derived> using is_strict_base_of = bool_constant< | ||||
|     std::is_base_of<Base, Derived>::value && !std::is_same<Base, Derived>::value>; | ||||
| template <typename Base, typename Derived> | ||||
| using is_strict_base_of | ||||
|     = bool_constant<std::is_base_of<Base, Derived>::value && !std::is_same<Base, Derived>::value>; | ||||
| 
 | ||||
| /// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer
 | ||||
| /// can be converted to a Base pointer)
 | ||||
| /// For unions, `is_base_of<T, T>::value` is False, so we need to check `is_same` as well.
 | ||||
| template <typename Base, typename Derived> using is_accessible_base_of = bool_constant< | ||||
|     (std::is_same<Base, Derived>::value || std::is_base_of<Base, Derived>::value) && std::is_convertible<Derived *, Base *>::value>; | ||||
| /// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived
 | ||||
| /// pointer can be converted to a Base pointer) For unions, `is_base_of<T, T>::value` is False, so
 | ||||
| /// we need to check `is_same` as well.
 | ||||
| template <typename Base, typename Derived> | ||||
| using is_accessible_base_of | ||||
|     = bool_constant<(std::is_same<Base, Derived>::value || std::is_base_of<Base, Derived>::value) | ||||
|                     && std::is_convertible<Derived *, Base *>::value>; | ||||
| 
 | ||||
| template <template <typename...> class Base> | ||||
| struct is_template_base_of_impl { | ||||
|     template <typename... Us> static std::true_type check(Base<Us...> *); | ||||
|     template <typename... Us> | ||||
|     static std::true_type check(Base<Us...> *); | ||||
|     static std::false_type check(...); | ||||
| }; | ||||
| 
 | ||||
| /// Check if a template is the base of a type. For example:
 | ||||
| /// `is_template_base_of<Base, T>` is true if `struct T : Base<U> {}` where U can be anything
 | ||||
| template <template <typename...> class Base, typename T> | ||||
| // Sadly, all MSVC versions incl. 2022 need the workaround, even in C++20 mode.
 | ||||
| // See also: https://github.com/pybind/pybind11/pull/3741
 | ||||
| #if !defined(_MSC_VER) | ||||
| using is_template_base_of = decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T>*)nullptr)); | ||||
| #else // MSVC2015 has trouble with decltype in template aliases
 | ||||
| struct is_template_base_of : decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T>*)nullptr)) { }; | ||||
| using is_template_base_of | ||||
|     = decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T> *) nullptr)); | ||||
| #else | ||||
| struct is_template_base_of | ||||
|     : decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T> *) nullptr)) { | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| /// Check if T is an instantiation of the template `Class`. For example:
 | ||||
|  | @ -794,18 +842,24 @@ template <template<typename...> class Class, typename... Us> | |||
| struct is_instantiation<Class, Class<Us...>> : std::true_type {}; | ||||
| 
 | ||||
| /// Check if T is std::shared_ptr<U> where U can be anything
 | ||||
| template <typename T> using is_shared_ptr = is_instantiation<std::shared_ptr, T>; | ||||
| template <typename T> | ||||
| using is_shared_ptr = is_instantiation<std::shared_ptr, T>; | ||||
| 
 | ||||
| /// Check if T looks like an input iterator
 | ||||
| template <typename T, typename = void> struct is_input_iterator : std::false_type {}; | ||||
| template <typename T, typename = void> | ||||
| struct is_input_iterator : std::false_type {}; | ||||
| template <typename T> | ||||
| struct is_input_iterator<T, void_t<decltype(*std::declval<T &>()), decltype(++std::declval<T &>())>> | ||||
| struct is_input_iterator<T, | ||||
|                          void_t<decltype(*std::declval<T &>()), decltype(++std::declval<T &>())>> | ||||
|     : std::true_type {}; | ||||
| 
 | ||||
| template <typename T> using is_function_pointer = bool_constant< | ||||
|     std::is_pointer<T>::value && std::is_function<typename std::remove_pointer<T>::type>::value>; | ||||
| template <typename T> | ||||
| using is_function_pointer | ||||
|     = bool_constant<std::is_pointer<T>::value | ||||
|                     && std::is_function<typename std::remove_pointer<T>::type>::value>; | ||||
| 
 | ||||
| template <typename F> struct strip_function_object { | ||||
| template <typename F> | ||||
| struct strip_function_object { | ||||
|     // If you are encountering an
 | ||||
|     // 'error: name followed by "::" must be a class or namespace name'
 | ||||
|     // with the Intel compiler and a noexcept function here,
 | ||||
|  | @ -818,18 +872,18 @@ template <typename Function, typename F = remove_reference_t<Function>> | |||
| using function_signature_t = conditional_t< | ||||
|     std::is_function<F>::value, | ||||
|     F, | ||||
|     typename conditional_t< | ||||
|         std::is_pointer<F>::value || std::is_member_pointer<F>::value, | ||||
|     typename conditional_t<std::is_pointer<F>::value || std::is_member_pointer<F>::value, | ||||
|                            std::remove_pointer<F>, | ||||
|         strip_function_object<F> | ||||
|     >::type | ||||
| >; | ||||
|                            strip_function_object<F>>::type>; | ||||
| 
 | ||||
| /// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member
 | ||||
| /// pointer.  Note that this can catch all sorts of other things, too; this is intended to be used
 | ||||
| /// in a place where passing a lambda makes sense.
 | ||||
| template <typename T> using is_lambda = satisfies_none_of<remove_reference_t<T>, | ||||
|         std::is_function, std::is_pointer, std::is_member_pointer>; | ||||
| template <typename T> | ||||
| using is_lambda = satisfies_none_of<remove_reference_t<T>, | ||||
|                                     std::is_function, | ||||
|                                     std::is_pointer, | ||||
|                                     std::is_member_pointer>; | ||||
| 
 | ||||
| // [workaround(intel)] Internal error on fold expression
 | ||||
| /// Apply a function over each element of a parameter pack
 | ||||
|  | @ -838,14 +892,17 @@ template <typename T> using is_lambda = satisfies_none_of<remove_reference_t<T>, | |||
| #    define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) | ||||
| #else | ||||
| using expand_side_effects = bool[]; | ||||
| #define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (void)pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } | ||||
| #    define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN)                                                 \ | ||||
|         (void) pybind11::detail::expand_side_effects { ((PATTERN), void(), false)..., false } | ||||
| #endif | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
| #if defined(_MSC_VER) | ||||
| #    pragma warning(push) | ||||
| #  pragma warning(disable: 4275) // warning C4275: An exported class was derived from a class that wasn't exported. Can be ignored when derived from a STL class.
 | ||||
| #    pragma warning(disable : 4275) | ||||
| //     warning C4275: An exported class was derived from a class that wasn't exported.
 | ||||
| //     Can be ignored when derived from a STL class.
 | ||||
| #endif | ||||
| /// C++ bindings of builtin Python exceptions
 | ||||
| class PYBIND11_EXPORT_EXCEPTION builtin_exception : public std::runtime_error { | ||||
|  | @ -859,7 +916,8 @@ public: | |||
| #endif | ||||
| 
 | ||||
| #define PYBIND11_RUNTIME_EXCEPTION(name, type)                                                    \ | ||||
|     class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception { public: \ | ||||
|     class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception {                             \ | ||||
|     public:                                                                                       \ | ||||
|         using builtin_exception::builtin_exception;                                               \ | ||||
|         name() : name("") {}                                                                      \ | ||||
|         void set_error() const override { PyErr_SetString(type, what()); }                        \ | ||||
|  | @ -873,13 +931,22 @@ PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) | |||
| PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError) | ||||
| PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError) | ||||
| PYBIND11_RUNTIME_EXCEPTION(attribute_error, PyExc_AttributeError) | ||||
| PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
 | ||||
| PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or
 | ||||
|                                                            /// handle::call fail due to a type
 | ||||
|                                                            /// casting error
 | ||||
| PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
 | ||||
| 
 | ||||
| [[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } | ||||
| [[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } | ||||
| [[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason) { | ||||
|     assert(!PyErr_Occurred()); | ||||
|     throw std::runtime_error(reason); | ||||
| } | ||||
| [[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const std::string &reason) { | ||||
|     assert(!PyErr_Occurred()); | ||||
|     throw std::runtime_error(reason); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename SFINAE = void> struct format_descriptor { }; | ||||
| template <typename T, typename SFINAE = void> | ||||
| struct format_descriptor {}; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| // Returns the index of the given type in the type char array below, and in the list in numpy.h
 | ||||
|  | @ -887,16 +954,28 @@ PYBIND11_NAMESPACE_BEGIN(detail) | |||
| // complex float,double,long double.  Note that the long double types only participate when long
 | ||||
| // double is actually longer than double (it isn't under MSVC).
 | ||||
| // NB: not only the string below but also complex.h and numpy.h rely on this order.
 | ||||
| template <typename T, typename SFINAE = void> struct is_fmt_numeric { static constexpr bool value = false; }; | ||||
| template <typename T> struct is_fmt_numeric<T, enable_if_t<std::is_arithmetic<T>::value>> { | ||||
| template <typename T, typename SFINAE = void> | ||||
| struct is_fmt_numeric { | ||||
|     static constexpr bool value = false; | ||||
| }; | ||||
| template <typename T> | ||||
| struct is_fmt_numeric<T, enable_if_t<std::is_arithmetic<T>::value>> { | ||||
|     static constexpr bool value = true; | ||||
|     static constexpr int index = std::is_same<T, bool>::value ? 0 : 1 + ( | ||||
|         std::is_integral<T>::value ? detail::log2(sizeof(T))*2 + std::is_unsigned<T>::value : 8 + ( | ||||
|         std::is_same<T, double>::value ? 1 : std::is_same<T, long double>::value ? 2 : 0)); | ||||
|     static constexpr int index | ||||
|         = std::is_same<T, bool>::value | ||||
|               ? 0 | ||||
|               : 1 | ||||
|                     + (std::is_integral<T>::value | ||||
|                            ? detail::log2(sizeof(T)) * 2 + std::is_unsigned<T>::value | ||||
|                            : 8 | ||||
|                                  + (std::is_same<T, double>::value        ? 1 | ||||
|                                     : std::is_same<T, long double>::value ? 2 | ||||
|                                                                           : 0)); | ||||
| }; | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
| template <typename T> struct format_descriptor<T, detail::enable_if_t<std::is_arithmetic<T>::value>> { | ||||
| template <typename T> | ||||
| struct format_descriptor<T, detail::enable_if_t<std::is_arithmetic<T>::value>> { | ||||
|     static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric<T>::index]; | ||||
|     static constexpr const char value[2] = {c, '\0'}; | ||||
|     static std::string format() { return std::string(1, c); } | ||||
|  | @ -904,8 +983,9 @@ template <typename T> struct format_descriptor<T, detail::enable_if_t<std::is_ar | |||
| 
 | ||||
| #if !defined(PYBIND11_CPP17) | ||||
| 
 | ||||
| template <typename T> constexpr const char format_descriptor< | ||||
|     T, detail::enable_if_t<std::is_arithmetic<T>::value>>::value[2]; | ||||
| template <typename T> | ||||
| constexpr const char | ||||
|     format_descriptor<T, detail::enable_if_t<std::is_arithmetic<T>::value>>::value[2]; | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | @ -913,29 +993,36 @@ template <typename T> constexpr const char format_descriptor< | |||
| 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); } | ||||
| }; | ||||
| 
 | ||||
| /// Dummy destructor wrapper that can be used to expose classes with a private destructor
 | ||||
| struct nodelete { template <typename T> void operator()(T*) { } }; | ||||
| struct nodelete { | ||||
|     template <typename T> | ||||
|     void operator()(T *) {} | ||||
| }; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| template <typename... Args> | ||||
| struct overload_cast_impl { | ||||
|     // NOLINTNEXTLINE(modernize-use-equals-default):  MSVC 2015 needs this
 | ||||
|     constexpr overload_cast_impl() {} | ||||
| 
 | ||||
|     template <typename Return> | ||||
|     constexpr auto operator()(Return (*pf)(Args...)) const noexcept | ||||
|                               -> decltype(pf) { return pf; } | ||||
|     constexpr auto operator()(Return (*pf)(Args...)) const noexcept -> decltype(pf) { | ||||
|         return pf; | ||||
|     } | ||||
| 
 | ||||
|     template <typename Return, typename Class> | ||||
|     constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept | ||||
|                               -> decltype(pmf) { return pmf; } | ||||
|         -> decltype(pmf) { | ||||
|         return pmf; | ||||
|     } | ||||
| 
 | ||||
|     template <typename Return, typename Class> | ||||
|     constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept | ||||
|                               -> decltype(pmf) { return pmf; } | ||||
|         -> decltype(pmf) { | ||||
|         return pmf; | ||||
|     } | ||||
| }; | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
|  | @ -946,8 +1033,12 @@ PYBIND11_NAMESPACE_END(detail) | |||
| ///  - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
 | ||||
| ///  - sweet:   overload_cast<Arg0, Arg1, Arg2>(&Class::func)
 | ||||
| template <typename... Args> | ||||
| #    if (defined(_MSC_VER) && _MSC_VER < 1920) /* MSVC 2017 */                                    \ | ||||
|         || (defined(__clang__) && __clang_major__ == 5) | ||||
| static constexpr detail::overload_cast_impl<Args...> overload_cast = {}; | ||||
| // MSVC 2015 only accepts this particular initialization syntax for this variable template.
 | ||||
| #    else | ||||
| static constexpr detail::overload_cast_impl<Args...> overload_cast; | ||||
| #    endif | ||||
| #endif | ||||
| 
 | ||||
| /// Const member function selector for overload_cast
 | ||||
|  | @ -956,7 +1047,8 @@ static constexpr detail::overload_cast_impl<Args...> overload_cast = {}; | |||
| static constexpr auto const_ = std::true_type{}; | ||||
| 
 | ||||
| #if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails:
 | ||||
| template <typename... Args> struct overload_cast { | ||||
| template <typename... Args> | ||||
| struct overload_cast { | ||||
|     static_assert(detail::deferred_t<std::false_type, Args...>::value, | ||||
|                   "pybind11::overload_cast<...> requires compiling in C++14 mode"); | ||||
| }; | ||||
|  | @ -970,6 +1062,7 @@ PYBIND11_NAMESPACE_BEGIN(detail) | |||
| template <typename T> | ||||
| class any_container { | ||||
|     std::vector<T> v; | ||||
| 
 | ||||
| public: | ||||
|     any_container() = default; | ||||
| 
 | ||||
|  | @ -977,13 +1070,17 @@ public: | |||
|     template <typename It, typename = enable_if_t<is_input_iterator<It>::value>> | ||||
|     any_container(It first, It last) : v(first, last) {} | ||||
| 
 | ||||
|     // Implicit conversion constructor from any arbitrary container type with values convertible to T
 | ||||
|     template <typename Container, typename = enable_if_t<std::is_convertible<decltype(*std::begin(std::declval<const Container &>())), T>::value>> | ||||
|     // Implicit conversion constructor from any arbitrary container type
 | ||||
|     // with values convertible to T
 | ||||
|     template <typename Container, | ||||
|               typename = enable_if_t< | ||||
|                   std::is_convertible<decltype(*std::begin(std::declval<const Container &>())), | ||||
|                                       T>::value>> | ||||
|     // NOLINTNEXTLINE(google-explicit-constructor)
 | ||||
|     any_container(const Container &c) : any_container(std::begin(c), std::end(c)) {} | ||||
| 
 | ||||
|     // initializer_list's aren't deducible, so don't get matched by the above template; we need this
 | ||||
|     // to explicitly allow implicit conversion from one:
 | ||||
|     // initializer_list's aren't deducible, so don't get matched by the above template;
 | ||||
|     // we need this to explicitly allow implicit conversion from one:
 | ||||
|     template <typename TIn, typename = enable_if_t<std::is_convertible<TIn, T>::value>> | ||||
|     any_container(const std::initializer_list<TIn> &c) : any_container(c.begin(), c.end()) {} | ||||
| 
 | ||||
|  | @ -1008,7 +1105,8 @@ public: | |||
| std::string get_fully_qualified_tp_name(PyTypeObject *); | ||||
| 
 | ||||
| template <typename T> | ||||
| inline static std::shared_ptr<T> try_get_shared_from_this(std::enable_shared_from_this<T> *holder_value_ptr) { | ||||
| inline static std::shared_ptr<T> | ||||
| try_get_shared_from_this(std::enable_shared_from_this<T> *holder_value_ptr) { | ||||
| // Pre C++17, this code path exploits undefined behavior, but is known to work on many platforms.
 | ||||
| // Use at your own risk!
 | ||||
| // See also https://en.cppreference.com/w/cpp/memory/enable_shared_from_this, and in particular
 | ||||
|  | @ -1018,8 +1116,7 @@ inline static std::shared_ptr<T> try_get_shared_from_this(std::enable_shared_fro | |||
| #else | ||||
|     try { | ||||
|         return holder_value_ptr->shared_from_this(); | ||||
|     } | ||||
|     catch (const std::bad_weak_ptr &) { | ||||
|     } catch (const std::bad_weak_ptr &) { | ||||
|         return nullptr; | ||||
|     } | ||||
| #endif | ||||
|  | @ -1027,10 +1124,12 @@ inline static std::shared_ptr<T> try_get_shared_from_this(std::enable_shared_fro | |||
| 
 | ||||
| // For silencing "unused" compiler warnings in special situations.
 | ||||
| template <typename... Args> | ||||
| #if defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER < 1920 // MSVC 2017
 | ||||
| #if defined(_MSC_VER) && _MSC_VER < 1920 // MSVC 2017
 | ||||
| constexpr | ||||
| #endif | ||||
| inline void silence_unused_warnings(Args &&...) {} | ||||
|     inline void | ||||
|     silence_unused_warnings(Args &&...) { | ||||
| } | ||||
| 
 | ||||
| // MSVC warning C4100: Unreferenced formal parameter
 | ||||
| #if defined(_MSC_VER) && _MSC_VER <= 1916 | ||||
|  | @ -1059,5 +1158,12 @@ constexpr inline bool silence_msvc_c4127(bool cond) { return cond; } | |||
| #    define PYBIND11_SILENCE_MSVC_C4127(...) __VA_ARGS__ | ||||
| #endif | ||||
| 
 | ||||
| // Pybind offers detailed error messages by default for all builts that are debug (through the
 | ||||
| // negation of ndebug). This can also be manually enabled by users, for any builds, through
 | ||||
| // defining PYBIND11_DETAILED_ERROR_MESSAGES.
 | ||||
| #if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(NDEBUG) | ||||
| #    define PYBIND11_DETAILED_ERROR_MESSAGES | ||||
| #endif | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) | ||||
|  |  | |||
|  | @ -42,23 +42,30 @@ struct descr { | |||
| }; | ||||
| 
 | ||||
| template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2> | ||||
| constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b, | ||||
|                                                    index_sequence<Is1...>, index_sequence<Is2...>) { | ||||
| constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, | ||||
|                                                    const descr<N2, Ts2...> &b, | ||||
|                                                    index_sequence<Is1...>, | ||||
|                                                    index_sequence<Is2...>) { | ||||
|     PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b); | ||||
|     return {a.text[Is1]..., b.text[Is2]...}; | ||||
| } | ||||
| 
 | ||||
| template <size_t N1, size_t N2, typename... Ts1, typename... Ts2> | ||||
| constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b) { | ||||
| constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a, | ||||
|                                                    const descr<N2, Ts2...> &b) { | ||||
|     return plus_impl(a, b, make_index_sequence<N1>(), make_index_sequence<N2>()); | ||||
| } | ||||
| 
 | ||||
| template <size_t N> | ||||
| constexpr descr<N - 1> const_name(char const(&text)[N]) { return descr<N - 1>(text); } | ||||
| constexpr descr<N - 1> const_name(char const (&text)[N]) { | ||||
|     return descr<N - 1>(text); | ||||
| } | ||||
| constexpr descr<0> const_name(char const (&)[1]) { return {}; } | ||||
| 
 | ||||
| template <size_t Rem, size_t... Digits> struct int_to_str : int_to_str<Rem/10, Rem%10, Digits...> { }; | ||||
| template <size_t...Digits> struct int_to_str<0, Digits...> { | ||||
| template <size_t Rem, size_t... Digits> | ||||
| struct int_to_str : int_to_str<Rem / 10, Rem % 10, Digits...> {}; | ||||
| template <size_t... Digits> | ||||
| struct int_to_str<0, Digits...> { | ||||
|     // WARNING: This only works with C++17 or higher.
 | ||||
|     static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...); | ||||
| }; | ||||
|  | @ -74,16 +81,23 @@ constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const(&)[N1], char cons | |||
| } | ||||
| 
 | ||||
| template <bool B, typename T1, typename T2> | ||||
| constexpr enable_if_t<B, T1> const_name(const T1 &d, const T2 &) { return d; } | ||||
| constexpr enable_if_t<B, T1> const_name(const T1 &d, const T2 &) { | ||||
|     return d; | ||||
| } | ||||
| template <bool B, typename T1, typename T2> | ||||
| constexpr enable_if_t<!B, T2> const_name(const T1 &, const T2 &d) { return d; } | ||||
| constexpr enable_if_t<!B, T2> const_name(const T1 &, const T2 &d) { | ||||
|     return d; | ||||
| } | ||||
| 
 | ||||
| template <size_t Size> | ||||
| auto constexpr const_name() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> { | ||||
|     return int_to_str<Size / 10, Size % 10>::digits; | ||||
| } | ||||
| 
 | ||||
| template <typename Type> constexpr descr<1, Type> const_name() { return {'%'}; } | ||||
| template <typename Type> | ||||
| constexpr descr<1, Type> const_name() { | ||||
|     return {'%'}; | ||||
| } | ||||
| 
 | ||||
| // If "_" is defined as a macro, py::detail::_ cannot be provided.
 | ||||
| // It is therefore best to use py::detail::const_name universally.
 | ||||
|  | @ -92,7 +106,9 @@ template <typename Type> constexpr descr<1, Type> const_name() { return {'%'}; } | |||
| #ifndef _ | ||||
| #    define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY | ||||
| template <size_t N> | ||||
| constexpr descr<N-1> _(char const(&text)[N]) { return const_name<N>(text); } | ||||
| constexpr descr<N - 1> _(char const (&text)[N]) { | ||||
|     return const_name<N>(text); | ||||
| } | ||||
| template <bool B, size_t N1, size_t N2> | ||||
| constexpr enable_if_t<B, descr<N1 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) { | ||||
|     return const_name<B, N1, N2>(text1, text2); | ||||
|  | @ -102,21 +118,30 @@ constexpr enable_if_t<!B, descr<N2 - 1>> _(char const(&text1)[N1], char const(&t | |||
|     return const_name<B, N1, N2>(text1, text2); | ||||
| } | ||||
| template <bool B, typename T1, typename T2> | ||||
| constexpr enable_if_t<B, T1> _(const T1 &d1, const T2 &d2) { return const_name<B,T1,T2>(d1, d2); } | ||||
| constexpr enable_if_t<B, T1> _(const T1 &d1, const T2 &d2) { | ||||
|     return const_name<B, T1, T2>(d1, d2); | ||||
| } | ||||
| template <bool B, typename T1, typename T2> | ||||
| constexpr enable_if_t<!B, T2> _(const T1 &d1, const T2 &d2) { return const_name<B,T1,T2>(d1, d2); } | ||||
| constexpr enable_if_t<!B, T2> _(const T1 &d1, const T2 &d2) { | ||||
|     return const_name<B, T1, T2>(d1, d2); | ||||
| } | ||||
| 
 | ||||
| template <size_t Size> | ||||
| auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> { | ||||
|     return const_name<Size>(); | ||||
| } | ||||
| template <typename Type> constexpr descr<1, Type> _() { return const_name<Type>(); } | ||||
| template <typename Type> | ||||
| constexpr descr<1, Type> _() { | ||||
|     return const_name<Type>(); | ||||
| } | ||||
| #endif // #ifndef _
 | ||||
| 
 | ||||
| constexpr descr<0> concat() { return {}; } | ||||
| 
 | ||||
| template <size_t N, typename... Ts> | ||||
| constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { return descr; } | ||||
| constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { | ||||
|     return descr; | ||||
| } | ||||
| 
 | ||||
| template <size_t N, typename... Ts, typename... Args> | ||||
| constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) | ||||
|  |  | |||
|  | @ -22,7 +22,8 @@ public: | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     template <typename> using cast_op_type = value_and_holder &; | ||||
|     template <typename> | ||||
|     using cast_op_type = value_and_holder &; | ||||
|     explicit operator value_and_holder &() { return *value; } | ||||
|     static constexpr auto name = const_name<value_and_holder>(); | ||||
| 
 | ||||
|  | @ -33,15 +34,21 @@ private: | |||
| PYBIND11_NAMESPACE_BEGIN(initimpl) | ||||
| 
 | ||||
| inline void no_nullptr(void *ptr) { | ||||
|     if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); | ||||
|     if (!ptr) { | ||||
|         throw type_error("pybind11::init(): factory function returned nullptr"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Implementing functions for all forms of py::init<...> and py::init(...)
 | ||||
| template <typename Class> using Cpp = typename Class::type; | ||||
| template <typename Class> using Alias = typename Class::type_alias; | ||||
| template <typename Class> using Holder = typename Class::holder_type; | ||||
| template <typename Class> | ||||
| using Cpp = typename Class::type; | ||||
| template <typename Class> | ||||
| using Alias = typename Class::type_alias; | ||||
| template <typename Class> | ||||
| using Holder = typename Class::holder_type; | ||||
| 
 | ||||
| template <typename Class> using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>; | ||||
| template <typename Class> | ||||
| using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>; | ||||
| 
 | ||||
| // Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance.
 | ||||
| template <typename Class, enable_if_t<Class::has_alias, int> = 0> | ||||
|  | @ -50,17 +57,27 @@ bool is_alias(Cpp<Class> *ptr) { | |||
| } | ||||
| // Failing fallback version of the above for a no-alias class (always returns false)
 | ||||
| template <typename /*Class*/> | ||||
| constexpr bool is_alias(void *) { return false; } | ||||
| constexpr bool is_alias(void *) { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
 | ||||
| // back to brace aggregate initiailization so that for aggregate initialization can be used with
 | ||||
| // py::init, e.g.  `py::init<int, int>` to initialize a `struct T { int a; int b; }`.  For
 | ||||
| // non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
 | ||||
| // works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).
 | ||||
| template <typename Class, typename... Args, detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0> | ||||
| inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward<Args>(args)...); } | ||||
| template <typename Class, typename... Args, detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0> | ||||
| inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward<Args>(args)...}; } | ||||
| template <typename Class, | ||||
|           typename... Args, | ||||
|           detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0> | ||||
| inline Class *construct_or_initialize(Args &&...args) { | ||||
|     return new Class(std::forward<Args>(args)...); | ||||
| } | ||||
| template <typename Class, | ||||
|           typename... Args, | ||||
|           detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0> | ||||
| inline Class *construct_or_initialize(Args &&...args) { | ||||
|     return new Class{std::forward<Args>(args)...}; | ||||
| } | ||||
| 
 | ||||
| // Attempts to constructs an alias using a `Alias(Cpp &&)` constructor.  This allows types with
 | ||||
| // an alias to provide only a single Cpp factory function as long as the Alias can be
 | ||||
|  | @ -69,12 +86,14 @@ inline Class *construct_or_initialize(Args &&...args) { return new Class{std::fo | |||
| // inherit all the base class constructors.
 | ||||
| template <typename Class> | ||||
| void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, | ||||
|                               value_and_holder &v_h, Cpp<Class> &&base) { | ||||
|                               value_and_holder &v_h, | ||||
|                               Cpp<Class> &&base) { | ||||
|     v_h.value_ptr() = new Alias<Class>(std::move(base)); | ||||
| } | ||||
| template <typename Class> | ||||
| [[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, | ||||
|                                            value_and_holder &, Cpp<Class> &&) { | ||||
|                                            value_and_holder &, | ||||
|                                            Cpp<Class> &&) { | ||||
|     throw type_error("pybind11::init(): unable to convert returned instance to required " | ||||
|                      "alias class: no `Alias<Class>(Class &&)` constructor available"); | ||||
| } | ||||
|  | @ -129,16 +148,18 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) { | |||
| 
 | ||||
| // Holder return: copy its pointer, and move or copy the returned holder into the new instance's
 | ||||
| // holder.  This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a
 | ||||
| // derived type (through those holder's implicit conversion from derived class holder constructors).
 | ||||
| // derived type (through those holder's implicit conversion from derived class holder
 | ||||
| // constructors).
 | ||||
| template <typename Class> | ||||
| void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { | ||||
|     PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); | ||||
|     auto *ptr = holder_helper<Holder<Class>>::get(holder); | ||||
|     no_nullptr(ptr); | ||||
|     // If we need an alias, check that the held pointer is actually an alias instance
 | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) { | ||||
|         throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " | ||||
|                          "is not an alias instance"); | ||||
|     } | ||||
| 
 | ||||
|     v_h.value_ptr() = ptr; | ||||
|     v_h.type->init_instance(v_h.inst, &holder); | ||||
|  | @ -153,18 +174,20 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { | |||
|     PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); | ||||
|     static_assert(std::is_move_constructible<Cpp<Class>>::value, | ||||
|                   "pybind11::init() return-by-value factory function requires a movable class"); | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) { | ||||
|         construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result)); | ||||
|     else | ||||
|     } else { | ||||
|         v_h.value_ptr() = new Cpp<Class>(std::move(result)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // return-by-value version 2: returning a value of the alias type itself.  We move-construct an
 | ||||
| // Alias instance (even if no the python-side inheritance is involved).  The is intended for
 | ||||
| // cases where Alias initialization is always desired.
 | ||||
| template <typename Class> | ||||
| void construct(value_and_holder &v_h, Alias<Class> &&result, bool) { | ||||
|     static_assert(std::is_move_constructible<Alias<Class>>::value, | ||||
|     static_assert( | ||||
|         std::is_move_constructible<Alias<Class>>::value, | ||||
|         "pybind11::init() return-by-alias-value factory function requires a movable alias class"); | ||||
|     v_h.value_ptr() = new Alias<Class>(std::move(result)); | ||||
| } | ||||
|  | @ -174,47 +197,75 @@ template <typename... Args> | |||
| struct constructor { | ||||
|     template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0> | ||||
|     static void execute(Class &cl, const Extra &...extra) { | ||||
|         cl.def("__init__", [](value_and_holder &v_h, Args... args) { | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [](value_and_holder &v_h, Args... args) { | ||||
|                 v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); | ||||
|         }, is_new_style_constructor(), extra...); | ||||
|             }, | ||||
|             is_new_style_constructor(), | ||||
|             extra...); | ||||
|     } | ||||
| 
 | ||||
|     template <typename Class, typename... Extra, | ||||
|               enable_if_t<Class::has_alias && | ||||
|                           std::is_constructible<Cpp<Class>, Args...>::value, int> = 0> | ||||
|     template <typename Class, | ||||
|               typename... Extra, | ||||
|               enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value, | ||||
|                           int> = 0> | ||||
|     static void execute(Class &cl, const Extra &...extra) { | ||||
|         cl.def("__init__", [](value_and_holder &v_h, Args... args) { | ||||
|             if (Py_TYPE(v_h.inst) == v_h.type->type) | ||||
|                 v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); | ||||
|             else | ||||
|                 v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); | ||||
|         }, is_new_style_constructor(), extra...); | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [](value_and_holder &v_h, Args... args) { | ||||
|                 if (Py_TYPE(v_h.inst) == v_h.type->type) { | ||||
|                     v_h.value_ptr() | ||||
|                         = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); | ||||
|                 } else { | ||||
|                     v_h.value_ptr() | ||||
|                         = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); | ||||
|                 } | ||||
|             }, | ||||
|             is_new_style_constructor(), | ||||
|             extra...); | ||||
|     } | ||||
| 
 | ||||
|     template <typename Class, typename... Extra, | ||||
|               enable_if_t<Class::has_alias && | ||||
|                           !std::is_constructible<Cpp<Class>, Args...>::value, int> = 0> | ||||
|     template <typename Class, | ||||
|               typename... Extra, | ||||
|               enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value, | ||||
|                           int> = 0> | ||||
|     static void execute(Class &cl, const Extra &...extra) { | ||||
|         cl.def("__init__", [](value_and_holder &v_h, Args... args) { | ||||
|             v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); | ||||
|         }, is_new_style_constructor(), extra...); | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [](value_and_holder &v_h, Args... args) { | ||||
|                 v_h.value_ptr() | ||||
|                     = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); | ||||
|             }, | ||||
|             is_new_style_constructor(), | ||||
|             extra...); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // Implementing class for py::init_alias<...>()
 | ||||
| template <typename... Args> struct alias_constructor { | ||||
|     template <typename Class, typename... Extra, | ||||
|               enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> = 0> | ||||
| template <typename... Args> | ||||
| struct alias_constructor { | ||||
|     template <typename Class, | ||||
|               typename... Extra, | ||||
|               enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, | ||||
|                           int> = 0> | ||||
|     static void execute(Class &cl, const Extra &...extra) { | ||||
|         cl.def("__init__", [](value_and_holder &v_h, Args... args) { | ||||
|             v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); | ||||
|         }, is_new_style_constructor(), extra...); | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [](value_and_holder &v_h, Args... args) { | ||||
|                 v_h.value_ptr() | ||||
|                     = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); | ||||
|             }, | ||||
|             is_new_style_constructor(), | ||||
|             extra...); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // Implementation class for py::init(Func) and py::init(Func, AliasFunc)
 | ||||
| template <typename CFunc, typename AFunc = void_type (*)(), | ||||
|           typename = function_signature_t<CFunc>, typename = function_signature_t<AFunc>> | ||||
| template <typename CFunc, | ||||
|           typename AFunc = void_type (*)(), | ||||
|           typename = function_signature_t<CFunc>, | ||||
|           typename = function_signature_t<AFunc>> | ||||
| struct factory; | ||||
| 
 | ||||
| // Specialization for py::init(Func)
 | ||||
|  | @ -233,21 +284,31 @@ struct factory<Func, void_type (*)(), Return(Args...)> { | |||
|     template <typename Class, typename... Extra> | ||||
|     void execute(Class &cl, const Extra &...extra) && { | ||||
| #if defined(PYBIND11_CPP14) | ||||
|         cl.def("__init__", [func = std::move(class_factory)] | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [func = std::move(class_factory)] | ||||
| #else | ||||
|         auto &func = class_factory; | ||||
|         cl.def("__init__", [func] | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [func] | ||||
| #endif | ||||
|             (value_and_holder &v_h, Args... args) { | ||||
|             construct<Class>(v_h, func(std::forward<Args>(args)...), | ||||
|                              Py_TYPE(v_h.inst) != v_h.type->type); | ||||
|         }, is_new_style_constructor(), extra...); | ||||
|                 construct<Class>( | ||||
|                     v_h, func(std::forward<Args>(args)...), Py_TYPE(v_h.inst) != v_h.type->type); | ||||
|             }, | ||||
|             is_new_style_constructor(), | ||||
|             extra...); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // Specialization for py::init(Func, AliasFunc)
 | ||||
| template <typename CFunc, typename AFunc, | ||||
|           typename CReturn, typename... CArgs, typename AReturn, typename... AArgs> | ||||
| template <typename CFunc, | ||||
|           typename AFunc, | ||||
|           typename CReturn, | ||||
|           typename... CArgs, | ||||
|           typename AReturn, | ||||
|           typename... AArgs> | ||||
| struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> { | ||||
|     static_assert(sizeof...(CArgs) == sizeof...(AArgs), | ||||
|                   "pybind11::init(class_factory, alias_factory): class and alias factories " | ||||
|  | @ -266,23 +327,31 @@ struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> { | |||
|     // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
 | ||||
|     template <typename Class, typename... Extra> | ||||
|     void execute(Class &cl, const Extra &...extra) && { | ||||
|         static_assert(Class::has_alias, "The two-argument version of `py::init()` can " | ||||
|         static_assert(Class::has_alias, | ||||
|                       "The two-argument version of `py::init()` can " | ||||
|                       "only be used if the class has an alias"); | ||||
| #if defined(PYBIND11_CPP14) | ||||
|         cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] | ||||
| #else | ||||
|         auto &class_func = class_factory; | ||||
|         auto &alias_func = alias_factory; | ||||
|         cl.def("__init__", [class_func, alias_func] | ||||
|         cl.def( | ||||
|             "__init__", | ||||
|             [class_func, alias_func] | ||||
| #endif | ||||
|             (value_and_holder &v_h, CArgs... args) { | ||||
|             if (Py_TYPE(v_h.inst) == v_h.type->type) | ||||
|                 // If the instance type equals the registered type we don't have inheritance, so
 | ||||
|                 // don't need the alias and can construct using the class function:
 | ||||
|                 if (Py_TYPE(v_h.inst) == v_h.type->type) { | ||||
|                     // If the instance type equals the registered type we don't have inheritance,
 | ||||
|                     // so don't need the alias and can construct using the class function:
 | ||||
|                     construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false); | ||||
|             else | ||||
|                 } else { | ||||
|                     construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true); | ||||
|         }, is_new_style_constructor(), extra...); | ||||
|                 } | ||||
|             }, | ||||
|             is_new_style_constructor(), | ||||
|             extra...); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -293,7 +362,9 @@ void setstate(value_and_holder &v_h, T &&result, bool need_alias) { | |||
| } | ||||
| 
 | ||||
| /// Set both the C++ and Python states
 | ||||
| template <typename Class, typename T, typename O, | ||||
| template <typename Class, | ||||
|           typename T, | ||||
|           typename O, | ||||
|           enable_if_t<std::is_convertible<O, handle>::value, int> = 0> | ||||
| void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) { | ||||
|     construct<Class>(v_h, std::move(result.first), need_alias); | ||||
|  | @ -307,12 +378,18 @@ void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) | |||
| } | ||||
| 
 | ||||
| /// Implementation for py::pickle(GetState, SetState)
 | ||||
| template <typename Get, typename Set, | ||||
|           typename = function_signature_t<Get>, typename = function_signature_t<Set>> | ||||
| template <typename Get, | ||||
|           typename Set, | ||||
|           typename = function_signature_t<Get>, | ||||
|           typename = function_signature_t<Set>> | ||||
| struct pickle_factory; | ||||
| 
 | ||||
| template <typename Get, typename Set, | ||||
|           typename RetState, typename Self, typename NewInstance, typename ArgState> | ||||
| template <typename Get, | ||||
|           typename Set, | ||||
|           typename RetState, | ||||
|           typename Self, | ||||
|           typename NewInstance, | ||||
|           typename ArgState> | ||||
| struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> { | ||||
|     static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value, | ||||
|                   "The type returned by `__getstate__` must be the same " | ||||
|  | @ -321,26 +398,31 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> { | |||
|     remove_reference_t<Get> get; | ||||
|     remove_reference_t<Set> set; | ||||
| 
 | ||||
|     pickle_factory(Get get, Set set) | ||||
|         : get(std::forward<Get>(get)), set(std::forward<Set>(set)) { } | ||||
|     pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {} | ||||
| 
 | ||||
|     template <typename Class, typename... Extra> | ||||
|     void execute(Class &cl, const Extra &...extra) && { | ||||
|         cl.def("__getstate__", std::move(get)); | ||||
| 
 | ||||
| #if defined(PYBIND11_CPP14) | ||||
|         cl.def("__setstate__", [func = std::move(set)] | ||||
|         cl.def( | ||||
|             "__setstate__", | ||||
|             [func = std::move(set)] | ||||
| #else | ||||
|         auto &func = set; | ||||
|         cl.def("__setstate__", [func] | ||||
|         cl.def( | ||||
|             "__setstate__", | ||||
|             [func] | ||||
| #endif | ||||
|             (value_and_holder &v_h, ArgState state) { | ||||
|             setstate<Class>(v_h, func(std::forward<ArgState>(state)), | ||||
|                             Py_TYPE(v_h.inst) != v_h.type->type); | ||||
|         }, is_new_style_constructor(), extra...); | ||||
|                 setstate<Class>( | ||||
|                     v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type); | ||||
|             }, | ||||
|             is_new_style_constructor(), | ||||
|             extra...); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(initimpl) | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| PYBIND11_NAMESPACE_END(pybind11) | ||||
| PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "../pytypes.h" | ||||
| 
 | ||||
| #include <exception> | ||||
| 
 | ||||
| /// Tracks the `internals` and `type_info` ABI version independent of the main library version.
 | ||||
|  | @ -81,7 +82,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass); | |||
| #    define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0; | ||||
| #    define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1) | ||||
| #    define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) | ||||
| #    if PY_MAJOR_VERSION < 3 || defined(PYPY_VERSION) | ||||
| #    if defined(PYPY_VERSION) | ||||
| // On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set
 | ||||
| // the value if it has already been set.  Instead, it must first be deleted and
 | ||||
| // then set again.
 | ||||
|  | @ -118,8 +119,9 @@ struct type_hash { | |||
|     size_t operator()(const std::type_index &t) const { | ||||
|         size_t hash = 5381; | ||||
|         const char *ptr = t.name(); | ||||
|         while (auto c = static_cast<unsigned char>(*ptr++)) | ||||
|         while (auto c = static_cast<unsigned char>(*ptr++)) { | ||||
|             hash = (hash * 33) ^ c; | ||||
|         } | ||||
|         return hash; | ||||
|     } | ||||
| }; | ||||
|  | @ -146,18 +148,23 @@ struct override_hash { | |||
| /// Whenever binary incompatible changes are made to this structure,
 | ||||
| /// `PYBIND11_INTERNALS_VERSION` must be incremented.
 | ||||
| struct internals { | ||||
|     type_map<type_info *> registered_types_cpp; // std::type_index -> pybind11's type information
 | ||||
|     std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s)
 | ||||
|     // std::type_index -> pybind11's type information
 | ||||
|     type_map<type_info *> registered_types_cpp; | ||||
|     // PyTypeObject* -> base type_info(s)
 | ||||
|     std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; | ||||
|     std::unordered_multimap<const void *, instance *> registered_instances; // void * -> instance*
 | ||||
|     std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> inactive_override_cache; | ||||
|     std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> | ||||
|         inactive_override_cache; | ||||
|     type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions; | ||||
|     std::unordered_map<const PyObject *, std::vector<PyObject *>> patients; | ||||
|     std::forward_list<ExceptionTranslator> registered_exception_translators; | ||||
|     std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
 | ||||
|     std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across
 | ||||
|                                                          // extensions
 | ||||
| #if PYBIND11_INTERNALS_VERSION == 4 | ||||
|     std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5; | ||||
| #endif | ||||
|     std::forward_list<std::string> static_strings; // Stores the std::strings backing detail::c_str()
 | ||||
|     std::forward_list<std::string> static_strings; // Stores the std::strings backing
 | ||||
|                                                    // detail::c_str()
 | ||||
|     PyTypeObject *static_property_type; | ||||
|     PyTypeObject *default_metaclass; | ||||
|     PyObject *instance_base; | ||||
|  | @ -270,11 +277,15 @@ struct type_info { | |||
| #    endif | ||||
| #endif | ||||
| 
 | ||||
| #define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ | ||||
|     PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" | ||||
| #define PYBIND11_INTERNALS_ID                                                                     \ | ||||
|     "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION)                        \ | ||||
|         PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI         \ | ||||
|             PYBIND11_BUILD_TYPE "__" | ||||
| 
 | ||||
| #define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ | ||||
|     PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" | ||||
| #define PYBIND11_MODULE_LOCAL_ID                                                                  \ | ||||
|     "__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION)                     \ | ||||
|         PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI         \ | ||||
|             PYBIND11_BUILD_TYPE "__" | ||||
| 
 | ||||
| /// Each module locally stores a pointer to the `internals` data. The data
 | ||||
| /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
 | ||||
|  | @ -283,7 +294,6 @@ inline internals **&get_internals_pp() { | |||
|     return internals_pp; | ||||
| } | ||||
| 
 | ||||
| #if PY_VERSION_HEX >= 0x03030000 | ||||
| // forward decl
 | ||||
| inline void translate_exception(std::exception_ptr); | ||||
| 
 | ||||
|  | @ -301,27 +311,17 @@ bool handle_nested_exception(const T &exc, const std::exception_ptr &p) { | |||
| template <class T, | ||||
|           enable_if_t<!std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0> | ||||
| bool handle_nested_exception(const T &exc, const std::exception_ptr &p) { | ||||
|     if (auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(exc))) { | ||||
|     if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(exc))) { | ||||
|         return handle_nested_exception(*nep, p); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| template <class T> | ||||
| bool handle_nested_exception(const T &, std::exception_ptr &) { | ||||
|     return false; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| inline bool raise_err(PyObject *exc_type, const char *msg) { | ||||
| #if PY_VERSION_HEX >= 0x03030000 | ||||
|     if (PyErr_Occurred()) { | ||||
|         raise_from(exc_type, msg); | ||||
|         return true; | ||||
|     } | ||||
| #endif | ||||
|     PyErr_SetString(exc_type, msg); | ||||
|     return false; | ||||
| } | ||||
|  | @ -338,7 +338,7 @@ inline void translate_exception(std::exception_ptr p) { | |||
|         return; | ||||
|     } catch (const builtin_exception &e) { | ||||
|         // Could not use template since it's an abstract class.
 | ||||
|         if (auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) { | ||||
|         if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) { | ||||
|             handle_nested_exception(*nep, p); | ||||
|         } | ||||
|         e.set_error(); | ||||
|  | @ -388,9 +388,15 @@ inline void translate_exception(std::exception_ptr p) { | |||
| #if !defined(__GLIBCXX__) | ||||
| inline void translate_local_exception(std::exception_ptr p) { | ||||
|     try { | ||||
|         if (p) std::rethrow_exception(p); | ||||
|     } catch (error_already_set &e)       { e.restore();   return; | ||||
|     } catch (const builtin_exception &e) { e.set_error(); return; | ||||
|         if (p) { | ||||
|             std::rethrow_exception(p); | ||||
|         } | ||||
|     } catch (error_already_set &e) { | ||||
|         e.restore(); | ||||
|         return; | ||||
|     } catch (const builtin_exception &e) { | ||||
|         e.set_error(); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | @ -398,8 +404,9 @@ inline void translate_local_exception(std::exception_ptr p) { | |||
| /// Return a reference to the current `internals` data
 | ||||
| PYBIND11_NOINLINE internals &get_internals() { | ||||
|     auto **&internals_pp = get_internals_pp(); | ||||
|     if (internals_pp && *internals_pp) | ||||
|     if (internals_pp && *internals_pp) { | ||||
|         return **internals_pp; | ||||
|     } | ||||
| 
 | ||||
|     // Ensure that the GIL is held since we will need to make Python calls.
 | ||||
|     // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
 | ||||
|  | @ -408,6 +415,7 @@ PYBIND11_NOINLINE internals &get_internals() { | |||
|         ~gil_scoped_acquire_local() { PyGILState_Release(state); } | ||||
|         const PyGILState_STATE state; | ||||
|     } gil; | ||||
|     error_scope err_scope; | ||||
| 
 | ||||
|     PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID); | ||||
|     auto builtins = handle(PyEval_GetBuiltins()); | ||||
|  | @ -425,7 +433,9 @@ PYBIND11_NOINLINE internals &get_internals() { | |||
|         (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); | ||||
| #endif | ||||
|     } else { | ||||
|         if (!internals_pp) internals_pp = new internals*(); | ||||
|         if (!internals_pp) { | ||||
|             internals_pp = new internals *(); | ||||
|         } | ||||
|         auto *&internals_ptr = *internals_pp; | ||||
|         internals_ptr = new internals(); | ||||
| #if defined(WITH_THREAD) | ||||
|  | @ -506,7 +516,6 @@ inline local_internals &get_local_internals() { | |||
|     return locals; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
 | ||||
| /// `c_str()`.  Such strings objects have a long storage duration -- the internal strings are only
 | ||||
| /// cleared when the program exits or after interpreter shutdown (when embedding), and so are
 | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ | |||
| #include "descr.h" | ||||
| #include "internals.h" | ||||
| #include "typeid.h" | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <iterator> | ||||
| #include <new> | ||||
|  | @ -62,19 +63,18 @@ private: | |||
| 
 | ||||
| public: | ||||
|     /// A new patient frame is created when a function is entered
 | ||||
|     loader_life_support() { | ||||
|         parent = get_stack_top(); | ||||
|         set_stack_top(this); | ||||
|     } | ||||
|     loader_life_support() : parent{get_stack_top()} { set_stack_top(this); } | ||||
| 
 | ||||
|     /// ... and destroyed after it returns
 | ||||
|     ~loader_life_support() { | ||||
|         if (get_stack_top() != this) | ||||
|         if (get_stack_top() != this) { | ||||
|             pybind11_fail("loader_life_support: internal error"); | ||||
|         } | ||||
|         set_stack_top(parent); | ||||
|         for (auto* item : keep_alive) | ||||
|         for (auto *item : keep_alive) { | ||||
|             Py_DECREF(item); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// This can only be used inside a pybind11-bound function, either by `argument_loader`
 | ||||
|     /// at argument preparation time or by `py::cast()` at execution time.
 | ||||
|  | @ -90,47 +90,56 @@ public: | |||
|                              "of temporary values"); | ||||
|         } | ||||
| 
 | ||||
|         if (frame->keep_alive.insert(h.ptr()).second) | ||||
|         if (frame->keep_alive.insert(h.ptr()).second) { | ||||
|             Py_INCREF(h.ptr()); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // Gets the cache entry for the given type, creating it if necessary.  The return value is the pair
 | ||||
| // returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
 | ||||
| // just created.
 | ||||
| inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type); | ||||
| inline std::pair<decltype(internals::registered_types_py)::iterator, bool> | ||||
| all_type_info_get_cache(PyTypeObject *type); | ||||
| 
 | ||||
| // Populates a just-created cache entry.
 | ||||
| PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) { | ||||
|     std::vector<PyTypeObject *> check; | ||||
|     for (handle parent : reinterpret_borrow<tuple>(t->tp_bases)) | ||||
|     for (handle parent : reinterpret_borrow<tuple>(t->tp_bases)) { | ||||
|         check.push_back((PyTypeObject *) parent.ptr()); | ||||
|     } | ||||
| 
 | ||||
|     auto const &type_dict = get_internals().registered_types_py; | ||||
|     for (size_t i = 0; i < check.size(); i++) { | ||||
|         auto type = check[i]; | ||||
|         auto *type = check[i]; | ||||
|         // Ignore Python2 old-style class super type:
 | ||||
|         if (!PyType_Check((PyObject *) type)) continue; | ||||
|         if (!PyType_Check((PyObject *) type)) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // Check `type` in the current set of registered python types:
 | ||||
|         auto it = type_dict.find(type); | ||||
|         if (it != type_dict.end()) { | ||||
|             // We found a cache entry for it, so it's either pybind-registered or has pre-computed
 | ||||
|             // pybind bases, but we have to make sure we haven't already seen the type(s) before: we
 | ||||
|             // want to follow Python/virtual C++ rules that there should only be one instance of a
 | ||||
|             // common base.
 | ||||
|             // pybind bases, but we have to make sure we haven't already seen the type(s) before:
 | ||||
|             // we want to follow Python/virtual C++ rules that there should only be one instance of
 | ||||
|             // a common base.
 | ||||
|             for (auto *tinfo : it->second) { | ||||
|                 // NB: Could use a second set here, rather than doing a linear search, but since
 | ||||
|                 // having a large number of immediate pybind11-registered types seems fairly
 | ||||
|                 // unlikely, that probably isn't worthwhile.
 | ||||
|                 bool found = false; | ||||
|                 for (auto *known : bases) { | ||||
|                     if (known == tinfo) { found = true; break; } | ||||
|                 } | ||||
|                 if (!found) bases.push_back(tinfo); | ||||
|                     if (known == tinfo) { | ||||
|                         found = true; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|         else if (type->tp_bases) { | ||||
|                 if (!found) { | ||||
|                     bases.push_back(tinfo); | ||||
|                 } | ||||
|             } | ||||
|         } else if (type->tp_bases) { | ||||
|             // It's some python type, so keep follow its bases classes to look for one or more
 | ||||
|             // registered types
 | ||||
|             if (i + 1 == check.size()) { | ||||
|  | @ -140,11 +149,12 @@ PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_ | |||
|                 check.pop_back(); | ||||
|                 i--; | ||||
|             } | ||||
|             for (handle parent : reinterpret_borrow<tuple>(type->tp_bases)) | ||||
|             for (handle parent : reinterpret_borrow<tuple>(type->tp_bases)) { | ||||
|                 check.push_back((PyTypeObject *) parent.ptr()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Extracts vector of type_info pointers of pybind-registered roots of the given Python type.  Will | ||||
|  | @ -158,9 +168,10 @@ PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_ | |||
|  */ | ||||
| inline const std::vector<detail::type_info *> &all_type_info(PyTypeObject *type) { | ||||
|     auto ins = all_type_info_get_cache(type); | ||||
|     if (ins.second) | ||||
|     if (ins.second) { | ||||
|         // New cache entry: populate it
 | ||||
|         all_type_info_populate(type, ins.first->second); | ||||
|     } | ||||
| 
 | ||||
|     return ins.first->second; | ||||
| } | ||||
|  | @ -171,42 +182,51 @@ inline const std::vector<detail::type_info *> &all_type_info(PyTypeObject *type) | |||
|  * `all_type_info` instead if you want to support multiple bases. | ||||
|  */ | ||||
| PYBIND11_NOINLINE detail::type_info *get_type_info(PyTypeObject *type) { | ||||
|     auto &bases = all_type_info(type); | ||||
|     if (bases.empty()) | ||||
|     const auto &bases = all_type_info(type); | ||||
|     if (bases.empty()) { | ||||
|         return nullptr; | ||||
|     if (bases.size() > 1) | ||||
|         pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); | ||||
|     } | ||||
|     if (bases.size() > 1) { | ||||
|         pybind11_fail( | ||||
|             "pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); | ||||
|     } | ||||
|     return bases.front(); | ||||
| } | ||||
| 
 | ||||
| inline detail::type_info *get_local_type_info(const std::type_index &tp) { | ||||
|     auto &locals = get_local_internals().registered_types_cpp; | ||||
|     auto it = locals.find(tp); | ||||
|     if (it != locals.end()) | ||||
|     if (it != locals.end()) { | ||||
|         return it->second; | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| inline detail::type_info *get_global_type_info(const std::type_index &tp) { | ||||
|     auto &types = get_internals().registered_types_cpp; | ||||
|     auto it = types.find(tp); | ||||
|     if (it != types.end()) | ||||
|     if (it != types.end()) { | ||||
|         return it->second; | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| /// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr.
 | ||||
| /// Return the type info for a given C++ type; on lookup failure can either throw or return
 | ||||
| /// nullptr.
 | ||||
| PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp, | ||||
|                                                    bool throw_if_missing = false) { | ||||
|     if (auto ltype = get_local_type_info(tp)) | ||||
|     if (auto *ltype = get_local_type_info(tp)) { | ||||
|         return ltype; | ||||
|     if (auto gtype = get_global_type_info(tp)) | ||||
|     } | ||||
|     if (auto *gtype = get_global_type_info(tp)) { | ||||
|         return gtype; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
|  | @ -221,11 +241,12 @@ PYBIND11_NOINLINE handle find_registered_python_instance(void *src, | |||
|                                                          const detail::type_info *tinfo) { | ||||
|     auto it_instances = get_internals().registered_instances.equal_range(src); | ||||
|     for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { | ||||
|         for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { | ||||
|             if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) | ||||
|         for (auto *instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { | ||||
|             if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) { | ||||
|                 return handle((PyObject *) it_i->second).inc_ref(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return handle(); | ||||
| } | ||||
| 
 | ||||
|  | @ -236,10 +257,10 @@ struct value_and_holder { | |||
|     void **vh = nullptr; | ||||
| 
 | ||||
|     // Main constructor for a found value/holder:
 | ||||
|     value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : | ||||
|         inst{i}, index{index}, type{type}, | ||||
|         vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} | ||||
|     {} | ||||
|     value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) | ||||
|         : inst{i}, index{index}, type{type}, vh{inst->simple_layout | ||||
|                                                     ? inst->simple_value_holder | ||||
|                                                     : &inst->nonsimple.values_and_holders[vpos]} {} | ||||
| 
 | ||||
|     // Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
 | ||||
|     value_and_holder() = default; | ||||
|  | @ -247,13 +268,15 @@ struct value_and_holder { | |||
|     // Used for past-the-end iterator
 | ||||
|     explicit value_and_holder(size_t index) : index{index} {} | ||||
| 
 | ||||
|     template <typename V = void> V *&value_ptr() const { | ||||
|     template <typename V = void> | ||||
|     V *&value_ptr() const { | ||||
|         return reinterpret_cast<V *&>(vh[0]); | ||||
|     } | ||||
|     // True if this `value_and_holder` has a non-null value pointer
 | ||||
|     explicit operator bool() const { return value_ptr() != nullptr; } | ||||
| 
 | ||||
|     template <typename H> H &holder() const { | ||||
|     template <typename H> | ||||
|     H &holder() const { | ||||
|         return reinterpret_cast<H &>(vh[1]); | ||||
|     } | ||||
|     bool holder_constructed() const { | ||||
|  | @ -263,13 +286,14 @@ struct value_and_holder { | |||
|     } | ||||
|     // NOLINTNEXTLINE(readability-make-member-function-const)
 | ||||
|     void set_holder_constructed(bool v = true) { | ||||
|         if (inst->simple_layout) | ||||
|         if (inst->simple_layout) { | ||||
|             inst->simple_holder_constructed = v; | ||||
|         else if (v) | ||||
|         } else if (v) { | ||||
|             inst->nonsimple.status[index] |= instance::status_holder_constructed; | ||||
|         else | ||||
|         } else { | ||||
|             inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_holder_constructed; | ||||
|         } | ||||
|     } | ||||
|     bool instance_registered() const { | ||||
|         return inst->simple_layout | ||||
|                    ? inst->simple_instance_registered | ||||
|  | @ -277,13 +301,14 @@ struct value_and_holder { | |||
|     } | ||||
|     // NOLINTNEXTLINE(readability-make-member-function-const)
 | ||||
|     void set_instance_registered(bool v = true) { | ||||
|         if (inst->simple_layout) | ||||
|         if (inst->simple_layout) { | ||||
|             inst->simple_instance_registered = v; | ||||
|         else if (v) | ||||
|         } else if (v) { | ||||
|             inst->nonsimple.status[index] |= instance::status_instance_registered; | ||||
|         else | ||||
|         } else { | ||||
|             inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_instance_registered; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // Container for accessing and iterating over an instance's values/holders
 | ||||
|  | @ -308,8 +333,7 @@ public: | |||
|               curr(inst /* instance */, | ||||
|                    types->empty() ? nullptr : (*types)[0] /* type info */, | ||||
|                    0, /* vpos: (non-simple types only): the first vptr comes first */ | ||||
|                  0 /* index */) | ||||
|         {} | ||||
|                    0 /* index */) {} | ||||
|         // Past-the-end iterator:
 | ||||
|         explicit iterator(size_t end) : curr(end) {} | ||||
| 
 | ||||
|  | @ -317,8 +341,9 @@ public: | |||
|         bool operator==(const iterator &other) const { return curr.index == other.curr.index; } | ||||
|         bool operator!=(const iterator &other) const { return curr.index != other.curr.index; } | ||||
|         iterator &operator++() { | ||||
|             if (!inst->simple_layout) | ||||
|             if (!inst->simple_layout) { | ||||
|                 curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; | ||||
|             } | ||||
|             ++curr.index; | ||||
|             curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; | ||||
|             return *this; | ||||
|  | @ -332,7 +357,9 @@ public: | |||
| 
 | ||||
|     iterator find(const type_info *find_type) { | ||||
|         auto it = begin(), endit = end(); | ||||
|         while (it != endit && it->type != find_type) ++it; | ||||
|         while (it != endit && it->type != find_type) { | ||||
|             ++it; | ||||
|         } | ||||
|         return it; | ||||
|     } | ||||
| 
 | ||||
|  | @ -349,166 +376,116 @@ public: | |||
|  * The returned object should be short-lived: in particular, it must not outlive the called-upon | ||||
|  * instance. | ||||
|  */ | ||||
| PYBIND11_NOINLINE value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { | ||||
| PYBIND11_NOINLINE value_and_holder | ||||
| instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, | ||||
|                                bool throw_if_missing /*= true in common.h*/) { | ||||
|     // Optimize common case:
 | ||||
|     if (!find_type || Py_TYPE(this) == find_type->type) | ||||
|     if (!find_type || Py_TYPE(this) == find_type->type) { | ||||
|         return value_and_holder(this, find_type, 0, 0); | ||||
|     } | ||||
| 
 | ||||
|     detail::values_and_holders vhs(this); | ||||
|     auto it = vhs.find(find_type); | ||||
|     if (it != vhs.end()) | ||||
|     if (it != vhs.end()) { | ||||
|         return *it; | ||||
|     } | ||||
| 
 | ||||
|     if (!throw_if_missing) | ||||
|     if (!throw_if_missing) { | ||||
|         return value_and_holder(); | ||||
|     } | ||||
| 
 | ||||
| #if defined(NDEBUG) | ||||
|     pybind11_fail("pybind11::detail::instance::get_value_and_holder: " | ||||
|             "type is not a pybind11 base of the given instance " | ||||
|             "(compile in debug mode for type details)"); | ||||
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|     pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" | ||||
|                   + get_fully_qualified_tp_name(find_type->type) | ||||
|                   + "' is not a pybind11 base of the given `" | ||||
|                   + get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance"); | ||||
| #else | ||||
|     pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + | ||||
|             get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" + | ||||
|             get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance"); | ||||
|     pybind11_fail( | ||||
|         "pybind11::detail::instance::get_value_and_holder: " | ||||
|         "type is not a pybind11 base of the given instance " | ||||
|         "(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for type details)"); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NOINLINE void instance::allocate_layout() { | ||||
|     auto &tinfo = all_type_info(Py_TYPE(this)); | ||||
|     const auto &tinfo = all_type_info(Py_TYPE(this)); | ||||
| 
 | ||||
|     const size_t n_types = tinfo.size(); | ||||
| 
 | ||||
|     if (n_types == 0) | ||||
|         pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); | ||||
|     if (n_types == 0) { | ||||
|         pybind11_fail( | ||||
|             "instance allocation failed: new instance has no pybind11-registered base types"); | ||||
|     } | ||||
| 
 | ||||
|     simple_layout = | ||||
|         n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); | ||||
|     simple_layout | ||||
|         = n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); | ||||
| 
 | ||||
|     // Simple path: no python-side multiple inheritance, and a small-enough holder
 | ||||
|     if (simple_layout) { | ||||
|         simple_value_holder[0] = nullptr; | ||||
|         simple_holder_constructed = false; | ||||
|         simple_instance_registered = false; | ||||
|     } | ||||
|     else { // multiple base types or a too-large holder
 | ||||
|     } else { // multiple base types or a too-large holder
 | ||||
|         // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer,
 | ||||
|         // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool
 | ||||
|         // values that tracks whether each associated holder has been initialized.  Each [block] is
 | ||||
|         // padded, if necessary, to an integer multiple of sizeof(void *).
 | ||||
|         size_t space = 0; | ||||
|         for (auto t : tinfo) { | ||||
|         for (auto *t : tinfo) { | ||||
|             space += 1;                      // value pointer
 | ||||
|             space += t->holder_size_in_ptrs; // holder instance
 | ||||
|         } | ||||
|         size_t flags_at = space; | ||||
|         space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered)
 | ||||
|         space += size_in_ptrs(n_types); // status bytes (holder_constructed and
 | ||||
|                                         // instance_registered)
 | ||||
| 
 | ||||
|         // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
 | ||||
|         // in particular, need to be 0).  Use Python's memory allocation functions: in Python 3.6
 | ||||
|         // they default to using pymalloc, which is designed to be efficient for small allocations
 | ||||
|         // like the one we're doing here; in earlier versions (and for larger allocations) they are
 | ||||
|         // just wrappers around malloc.
 | ||||
| #if PY_VERSION_HEX >= 0x03050000 | ||||
|         // in particular, need to be 0).  Use Python's memory allocation
 | ||||
|         // functions: Python is using pymalloc, which is designed to be
 | ||||
|         // efficient for small allocations like the one we're doing here;
 | ||||
|         // for larger allocations they are just wrappers around malloc.
 | ||||
|         // TODO: is this still true for pure Python 3.6?
 | ||||
|         nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); | ||||
|         if (!nonsimple.values_and_holders) throw std::bad_alloc(); | ||||
| #else | ||||
|         nonsimple.values_and_holders = (void **) PyMem_New(void *, space); | ||||
|         if (!nonsimple.values_and_holders) throw std::bad_alloc(); | ||||
|         std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); | ||||
| #endif | ||||
|         nonsimple.status = reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]); | ||||
|         if (!nonsimple.values_and_holders) { | ||||
|             throw std::bad_alloc(); | ||||
|         } | ||||
|         nonsimple.status | ||||
|             = reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]); | ||||
|     } | ||||
|     owned = true; | ||||
| } | ||||
| 
 | ||||
| // NOLINTNEXTLINE(readability-make-member-function-const)
 | ||||
| PYBIND11_NOINLINE void instance::deallocate_layout() { | ||||
|     if (!simple_layout) | ||||
|     if (!simple_layout) { | ||||
|         PyMem_Free(nonsimple.values_and_holders); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp) { | ||||
|     handle type = detail::get_type_handle(tp, false); | ||||
|     if (!type) | ||||
|     if (!type) { | ||||
|         return false; | ||||
|     } | ||||
|     return isinstance(obj, type); | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NOINLINE std::string error_string() { | ||||
|     if (!PyErr_Occurred()) { | ||||
|         PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); | ||||
|         return "Unknown internal error occurred"; | ||||
|     } | ||||
| 
 | ||||
|     error_scope scope; // Preserve error state
 | ||||
| 
 | ||||
|     std::string errorString; | ||||
|     if (scope.type) { | ||||
|         errorString += handle(scope.type).attr("__name__").cast<std::string>(); | ||||
|         errorString += ": "; | ||||
|     } | ||||
|     if (scope.value) | ||||
|         errorString += (std::string) str(scope.value); | ||||
| 
 | ||||
|     PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); | ||||
| 
 | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|     if (scope.trace != nullptr) | ||||
|         PyException_SetTraceback(scope.value, scope.trace); | ||||
| #endif | ||||
| 
 | ||||
| #if !defined(PYPY_VERSION) | ||||
|     if (scope.trace) { | ||||
|         auto *trace = (PyTracebackObject *) scope.trace; | ||||
| 
 | ||||
|         /* Get the deepest trace possible */ | ||||
|         while (trace->tb_next) | ||||
|             trace = trace->tb_next; | ||||
| 
 | ||||
|         PyFrameObject *frame = trace->tb_frame; | ||||
|         errorString += "\n\nAt:\n"; | ||||
|         while (frame) { | ||||
| #if PY_VERSION_HEX >= 0x03090000 | ||||
|             PyCodeObject *f_code = PyFrame_GetCode(frame); | ||||
| #else | ||||
|             PyCodeObject *f_code = frame->f_code; | ||||
|             Py_INCREF(f_code); | ||||
| #endif | ||||
|             int lineno = PyFrame_GetLineNumber(frame); | ||||
|             errorString += | ||||
|                 "  " + handle(f_code->co_filename).cast<std::string>() + | ||||
|                 "(" + std::to_string(lineno) + "): " + | ||||
|                 handle(f_code->co_name).cast<std::string>() + "\n"; | ||||
|             frame = frame->f_back; | ||||
|             Py_DECREF(f_code); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     return errorString; | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) { | ||||
|     auto &instances = get_internals().registered_instances; | ||||
|     auto range = instances.equal_range(ptr); | ||||
|     for (auto it = range.first; it != range.second; ++it) { | ||||
|         for (const auto &vh : values_and_holders(it->second)) { | ||||
|             if (vh.type == type) | ||||
|             if (vh.type == type) { | ||||
|                 return handle((PyObject *) it->second); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return handle(); | ||||
| } | ||||
| 
 | ||||
| inline PyThreadState *get_thread_state_unchecked() { | ||||
| #if defined(PYPY_VERSION) | ||||
|     return PyThreadState_GET(); | ||||
| #elif PY_VERSION_HEX < 0x03000000 | ||||
|     return _PyThreadState_Current; | ||||
| #elif PY_VERSION_HEX < 0x03050000 | ||||
|     return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); | ||||
| #elif PY_VERSION_HEX < 0x03050200 | ||||
|     return (PyThreadState*) _PyThreadState_Current.value; | ||||
| #else | ||||
|     return _PyThreadState_UncheckedGet(); | ||||
| #endif | ||||
|  | @ -526,27 +503,30 @@ public: | |||
|     explicit type_caster_generic(const type_info *typeinfo) | ||||
|         : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) {} | ||||
| 
 | ||||
|     bool load(handle src, bool convert) { | ||||
|         return load_impl<type_caster_generic>(src, convert); | ||||
|     } | ||||
|     bool load(handle src, bool convert) { return load_impl<type_caster_generic>(src, convert); } | ||||
| 
 | ||||
|     PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, | ||||
|     PYBIND11_NOINLINE static handle cast(const void *_src, | ||||
|                                          return_value_policy policy, | ||||
|                                          handle parent, | ||||
|                                          const detail::type_info *tinfo, | ||||
|                                          void *(*copy_constructor)(const void *), | ||||
|                                          void *(*move_constructor)(const void *), | ||||
|                                          const void *existing_holder = nullptr) { | ||||
|         if (!tinfo) // no type info: error will be set already
 | ||||
|         if (!tinfo) { // no type info: error will be set already
 | ||||
|             return handle(); | ||||
|         } | ||||
| 
 | ||||
|         void *src = const_cast<void *>(_src); | ||||
|         if (src == nullptr) | ||||
|         if (src == nullptr) { | ||||
|             return none().release(); | ||||
|         } | ||||
| 
 | ||||
|         if (handle registered_inst = find_registered_python_instance(src, tinfo)) | ||||
|         if (handle registered_inst = find_registered_python_instance(src, tinfo)) { | ||||
|             return registered_inst; | ||||
|         } | ||||
| 
 | ||||
|         auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type)); | ||||
|         auto wrapper = reinterpret_cast<instance *>(inst.ptr()); | ||||
|         auto *wrapper = reinterpret_cast<instance *>(inst.ptr()); | ||||
|         wrapper->owned = false; | ||||
|         void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); | ||||
| 
 | ||||
|  | @ -564,37 +544,39 @@ public: | |||
|                 break; | ||||
| 
 | ||||
|             case return_value_policy::copy: | ||||
|                 if (copy_constructor) | ||||
|                 if (copy_constructor) { | ||||
|                     valueptr = copy_constructor(src); | ||||
|                 else { | ||||
| #if defined(NDEBUG) | ||||
|                     throw cast_error("return_value_policy = copy, but type is " | ||||
|                                      "non-copyable! (compile in debug mode for details)"); | ||||
| #else | ||||
|                 } else { | ||||
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|                     std::string type_name(tinfo->cpptype->name()); | ||||
|                     detail::clean_type_id(type_name); | ||||
|                     throw cast_error("return_value_policy = copy, but type " + | ||||
|                                      type_name + " is non-copyable!"); | ||||
|                     throw cast_error("return_value_policy = copy, but type " + type_name | ||||
|                                      + " is non-copyable!"); | ||||
| #else | ||||
|                     throw cast_error("return_value_policy = copy, but type is " | ||||
|                                      "non-copyable! (#define PYBIND11_DETAILED_ERROR_MESSAGES or " | ||||
|                                      "compile in debug mode for details)"); | ||||
| #endif | ||||
|                 } | ||||
|                 wrapper->owned = true; | ||||
|                 break; | ||||
| 
 | ||||
|             case return_value_policy::move: | ||||
|                 if (move_constructor) | ||||
|                 if (move_constructor) { | ||||
|                     valueptr = move_constructor(src); | ||||
|                 else if (copy_constructor) | ||||
|                 } else if (copy_constructor) { | ||||
|                     valueptr = copy_constructor(src); | ||||
|                 else { | ||||
| #if defined(NDEBUG) | ||||
|                     throw cast_error("return_value_policy = move, but type is neither " | ||||
|                                      "movable nor copyable! " | ||||
|                                      "(compile in debug mode for details)"); | ||||
| #else | ||||
|                 } else { | ||||
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|                     std::string type_name(tinfo->cpptype->name()); | ||||
|                     detail::clean_type_id(type_name); | ||||
|                     throw cast_error("return_value_policy = move, but type " + | ||||
|                                      type_name + " is neither movable nor copyable!"); | ||||
|                     throw cast_error("return_value_policy = move, but type " + type_name | ||||
|                                      + " is neither movable nor copyable!"); | ||||
| #else | ||||
|                     throw cast_error("return_value_policy = move, but type is neither " | ||||
|                                      "movable nor copyable! " | ||||
|                                      "(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in " | ||||
|                                      "debug mode for details)"); | ||||
| #endif | ||||
|                 } | ||||
|                 wrapper->owned = true; | ||||
|  | @ -620,23 +602,25 @@ public: | |||
|         auto *&vptr = v_h.value_ptr(); | ||||
|         // Lazy allocation for unallocated values:
 | ||||
|         if (vptr == nullptr) { | ||||
|             auto *type = v_h.type ? v_h.type : typeinfo; | ||||
|             const auto *type = v_h.type ? v_h.type : typeinfo; | ||||
|             if (type->operator_new) { | ||||
|                 vptr = type->operator_new(type->type_size); | ||||
|             } else { | ||||
| #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) | ||||
|                     if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) | ||||
|                         vptr = ::operator new(type->type_size, | ||||
|                                               std::align_val_t(type->type_align)); | ||||
|                     else | ||||
|                 #endif | ||||
|                 if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { | ||||
|                     vptr = ::operator new(type->type_size, std::align_val_t(type->type_align)); | ||||
|                 } else { | ||||
|                     vptr = ::operator new(type->type_size); | ||||
|                 } | ||||
| #else | ||||
|                 vptr = ::operator new(type->type_size); | ||||
| #endif | ||||
|             } | ||||
|         } | ||||
|         value = vptr; | ||||
|     } | ||||
|     bool try_implicit_casts(handle src, bool convert) { | ||||
|         for (auto &cast : typeinfo->implicit_casts) { | ||||
|         for (const auto &cast : typeinfo->implicit_casts) { | ||||
|             type_caster_generic sub_caster(*cast.first); | ||||
|             if (sub_caster.load(src, convert)) { | ||||
|                 value = cast.second(sub_caster.value); | ||||
|  | @ -647,17 +631,19 @@ public: | |||
|     } | ||||
|     bool try_direct_conversions(handle src) { | ||||
|         for (auto &converter : *typeinfo->direct_conversions) { | ||||
|             if (converter(src.ptr(), value)) | ||||
|             if (converter(src.ptr(), value)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|     void check_holder_compat() {} | ||||
| 
 | ||||
|     PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { | ||||
|         auto caster = type_caster_generic(ti); | ||||
|         if (caster.load(src, false)) | ||||
|         if (caster.load(src, false)) { | ||||
|             return caster.value; | ||||
|         } | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|  | @ -666,16 +652,19 @@ public: | |||
|     PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { | ||||
|         constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; | ||||
|         const auto pytype = type::handle_of(src); | ||||
|         if (!hasattr(pytype, local_key)) | ||||
|         if (!hasattr(pytype, local_key)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         type_info *foreign_typeinfo = reinterpret_borrow<capsule>(getattr(pytype, local_key)); | ||||
|         // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type
 | ||||
|         // Only consider this foreign loader if actually foreign and is a loader of the correct cpp
 | ||||
|         // type
 | ||||
|         if (foreign_typeinfo->module_local_load == &local_load | ||||
|             || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) | ||||
|             || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { | ||||
|         if (auto *result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { | ||||
|             value = result; | ||||
|             return true; | ||||
|         } | ||||
|  | @ -687,8 +676,12 @@ public: | |||
|     // logic (without having to resort to virtual inheritance).
 | ||||
|     template <typename ThisT> | ||||
|     PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { | ||||
|         if (!src) return false; | ||||
|         if (!typeinfo) return try_load_foreign_module_local(src); | ||||
|         if (!src) { | ||||
|             return false; | ||||
|         } | ||||
|         if (!typeinfo) { | ||||
|             return try_load_foreign_module_local(src); | ||||
|         } | ||||
| 
 | ||||
|         auto &this_ = static_cast<ThisT &>(*this); | ||||
|         this_.check_holder_compat(); | ||||
|  | @ -703,7 +696,7 @@ public: | |||
|         } | ||||
|         // Case 2: We have a derived class
 | ||||
|         if (PyType_IsSubtype(srctype, typeinfo->type)) { | ||||
|             auto &bases = all_type_info(srctype); | ||||
|             const auto &bases = all_type_info(srctype); | ||||
|             bool no_cpp_mi = typeinfo->simple_type; | ||||
| 
 | ||||
|             // Case 2a: the python type is a Python-inherited derived class that inherits from just
 | ||||
|  | @ -716,41 +709,45 @@ public: | |||
|                 this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder()); | ||||
|                 return true; | ||||
|             } | ||||
|             // Case 2b: the python type inherits from multiple C++ bases.  Check the bases to see if
 | ||||
|             // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
 | ||||
|             // can safely reinterpret_cast to the relevant pointer.
 | ||||
|             // Case 2b: the python type inherits from multiple C++ bases.  Check the bases to see
 | ||||
|             // if we can find an exact match (or, for a simple C++ type, an inherited match); if
 | ||||
|             // so, we can safely reinterpret_cast to the relevant pointer.
 | ||||
|             if (bases.size() > 1) { | ||||
|                 for (auto base : bases) { | ||||
|                     if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { | ||||
|                         this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base)); | ||||
|                 for (auto *base : bases) { | ||||
|                     if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) | ||||
|                                   : base->type == typeinfo->type) { | ||||
|                         this_.load_value( | ||||
|                             reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base)); | ||||
|                         return true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
 | ||||
|             // in the registered bases, above, so try implicit casting (needed for proper C++ casting
 | ||||
|             // when MI is involved).
 | ||||
|             if (this_.try_implicit_casts(src, convert)) | ||||
|             // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type
 | ||||
|             // match in the registered bases, above, so try implicit casting (needed for proper C++
 | ||||
|             // casting when MI is involved).
 | ||||
|             if (this_.try_implicit_casts(src, convert)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Perform an implicit conversion
 | ||||
|         if (convert) { | ||||
|             for (auto &converter : typeinfo->implicit_conversions) { | ||||
|             for (const auto &converter : typeinfo->implicit_conversions) { | ||||
|                 auto temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type)); | ||||
|                 if (load_impl<ThisT>(temp, false)) { | ||||
|                     loader_life_support::add_patient(temp); | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|             if (this_.try_direct_conversions(src)) | ||||
|             if (this_.try_direct_conversions(src)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Failed to match local typeinfo. Try again with global.
 | ||||
|         if (typeinfo->module_local) { | ||||
|             if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { | ||||
|             if (auto *gtype = get_global_type_info(*typeinfo->cpptype)) { | ||||
|                 typeinfo = gtype; | ||||
|                 return load(src, false); | ||||
|             } | ||||
|  | @ -764,7 +761,9 @@ public: | |||
|         // Custom converters didn't take None, now we convert None to nullptr.
 | ||||
|         if (src.is_none()) { | ||||
|             // Defer accepting None to other overloads (if we aren't in convert mode):
 | ||||
|            if (!convert) return false; | ||||
|             if (!convert) { | ||||
|                 return false; | ||||
|             } | ||||
|             value = nullptr; | ||||
|             return true; | ||||
|         } | ||||
|  | @ -772,14 +771,16 @@ public: | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
 | ||||
|     // isn't needed or can't be used.  If the type is unknown, sets the error and returns a pair
 | ||||
|     // with .second = nullptr.  (p.first = nullptr is not an error: it becomes None).
 | ||||
|     PYBIND11_NOINLINE static std::pair<const void *, const type_info *> src_and_type( | ||||
|             const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { | ||||
|         if (auto *tpi = get_type_info(cast_type)) | ||||
|     PYBIND11_NOINLINE static std::pair<const void *, const type_info *> | ||||
|     src_and_type(const void *src, | ||||
|                  const std::type_info &cast_type, | ||||
|                  const std::type_info *rtti_type = nullptr) { | ||||
|         if (auto *tpi = get_type_info(cast_type)) { | ||||
|             return {src, const_cast<const type_info *>(tpi)}; | ||||
|         } | ||||
| 
 | ||||
|         // Not found, set error:
 | ||||
|         std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); | ||||
|  | @ -802,8 +803,7 @@ public: | |||
|  * `movable_cast_op_type` instead. | ||||
|  */ | ||||
| template <typename T> | ||||
| using cast_op_type = | ||||
|     conditional_t<std::is_pointer<remove_reference_t<T>>::value, | ||||
| using cast_op_type = conditional_t<std::is_pointer<remove_reference_t<T>>::value, | ||||
|                                    typename std::add_pointer<intrinsic_t<T>>::type, | ||||
|                                    typename std::add_lvalue_reference<intrinsic_t<T>>::type>; | ||||
| 
 | ||||
|  | @ -815,8 +815,8 @@ using cast_op_type = | |||
|  * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. | ||||
|  */ | ||||
| template <typename T> | ||||
| using movable_cast_op_type = | ||||
|     conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value, | ||||
| using movable_cast_op_type | ||||
|     = conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value, | ||||
|                     typename std::add_pointer<intrinsic_t<T>>::type, | ||||
|                     conditional_t<std::is_rvalue_reference<T>::value, | ||||
|                                   typename std::add_rvalue_reference<intrinsic_t<T>>::type, | ||||
|  | @ -824,31 +824,41 @@ using movable_cast_op_type = | |||
| 
 | ||||
| // std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
 | ||||
| // T is non-copyable, but code containing such a copy constructor fails to actually compile.
 | ||||
| template <typename T, typename SFINAE = void> struct is_copy_constructible : std::is_copy_constructible<T> {}; | ||||
| template <typename T, typename SFINAE = void> | ||||
| struct is_copy_constructible : std::is_copy_constructible<T> {}; | ||||
| 
 | ||||
| // Specialization for types that appear to be copy constructible but also look like stl containers
 | ||||
| // (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
 | ||||
| // so, copy constructability depends on whether the value_type is copy constructible.
 | ||||
| template <typename Container> struct is_copy_constructible<Container, enable_if_t<all_of< | ||||
|         std::is_copy_constructible<Container>, | ||||
| template <typename Container> | ||||
| struct is_copy_constructible< | ||||
|     Container, | ||||
|     enable_if_t< | ||||
|         all_of<std::is_copy_constructible<Container>, | ||||
|                std::is_same<typename Container::value_type &, typename Container::reference>, | ||||
|                // Avoid infinite recursion
 | ||||
|         negation<std::is_same<Container, typename Container::value_type>> | ||||
|     >::value>> : is_copy_constructible<typename Container::value_type> {}; | ||||
|                negation<std::is_same<Container, typename Container::value_type>>>::value>> | ||||
|     : is_copy_constructible<typename Container::value_type> {}; | ||||
| 
 | ||||
| // Likewise for std::pair
 | ||||
| // (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves
 | ||||
| // copy constructible, but this can not be relied upon when T1 or T2 are themselves containers).
 | ||||
| template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T2>> | ||||
| // (after C++17 it is mandatory that the copy constructor not exist when the two types aren't
 | ||||
| // themselves copy constructible, but this can not be relied upon when T1 or T2 are themselves
 | ||||
| // containers).
 | ||||
| template <typename T1, typename T2> | ||||
| struct is_copy_constructible<std::pair<T1, T2>> | ||||
|     : all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {}; | ||||
| 
 | ||||
| // The same problems arise with std::is_copy_assignable, so we use the same workaround.
 | ||||
| template <typename T, typename SFINAE = void> struct is_copy_assignable : std::is_copy_assignable<T> {}; | ||||
| template <typename Container> struct is_copy_assignable<Container, enable_if_t<all_of< | ||||
|         std::is_copy_assignable<Container>, | ||||
|         std::is_same<typename Container::value_type &, typename Container::reference> | ||||
|     >::value>> : is_copy_assignable<typename Container::value_type> {}; | ||||
| template <typename T1, typename T2> struct is_copy_assignable<std::pair<T1, T2>> | ||||
| template <typename T, typename SFINAE = void> | ||||
| struct is_copy_assignable : std::is_copy_assignable<T> {}; | ||||
| template <typename Container> | ||||
| struct is_copy_assignable<Container, | ||||
|                           enable_if_t<all_of<std::is_copy_assignable<Container>, | ||||
|                                              std::is_same<typename Container::value_type &, | ||||
|                                                           typename Container::reference>>::value>> | ||||
|     : is_copy_assignable<typename Container::value_type> {}; | ||||
| template <typename T1, typename T2> | ||||
| struct is_copy_assignable<std::pair<T1, T2>> | ||||
|     : all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {}; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
|  | @ -875,13 +885,11 @@ PYBIND11_NAMESPACE_END(detail) | |||
| // std::enable_if. User provided specializations will always have higher priority than
 | ||||
| // the default implementation and specialization provided in polymorphic_type_hook_base.
 | ||||
| template <typename itype, typename SFINAE = void> | ||||
| struct polymorphic_type_hook_base | ||||
| { | ||||
| struct polymorphic_type_hook_base { | ||||
|     static const void *get(const itype *src, const std::type_info *&) { return src; } | ||||
| }; | ||||
| template <typename itype> | ||||
| struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>> | ||||
| { | ||||
| struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>> { | ||||
|     static const void *get(const itype *src, const std::type_info *&type) { | ||||
|         type = src ? &typeid(*src) : nullptr; | ||||
|         return dynamic_cast<const void *>(src); | ||||
|  | @ -893,7 +901,8 @@ struct polymorphic_type_hook : public polymorphic_type_hook_base<itype> {}; | |||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| /// Generic type caster for objects stored on the heap
 | ||||
| template <typename type> class type_caster_base : public type_caster_generic { | ||||
| template <typename type> | ||||
| class type_caster_base : public type_caster_generic { | ||||
|     using itype = intrinsic_t<type>; | ||||
| 
 | ||||
| public: | ||||
|  | @ -903,8 +912,10 @@ public: | |||
|     explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) {} | ||||
| 
 | ||||
|     static handle cast(const itype &src, return_value_policy policy, handle parent) { | ||||
|         if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) | ||||
|         if (policy == return_value_policy::automatic | ||||
|             || policy == return_value_policy::automatic_reference) { | ||||
|             policy = return_value_policy::copy; | ||||
|         } | ||||
|         return cast(&src, policy, parent); | ||||
|     } | ||||
| 
 | ||||
|  | @ -916,7 +927,7 @@ public: | |||
|     // polymorphic type (using RTTI by default, but can be overridden by specializing
 | ||||
|     // polymorphic_type_hook). If the instance isn't derived, returns the base version.
 | ||||
|     static std::pair<const void *, const type_info *> src_and_type(const itype *src) { | ||||
|         auto &cast_type = typeid(itype); | ||||
|         const auto &cast_type = typeid(itype); | ||||
|         const std::type_info *instance_type = nullptr; | ||||
|         const void *vsrc = polymorphic_type_hook<itype>::get(src, instance_type); | ||||
|         if (instance_type && !same_type(cast_type, *instance_type)) { | ||||
|  | @ -928,50 +939,64 @@ public: | |||
|             // except via a user-provided specialization of polymorphic_type_hook,
 | ||||
|             // and the user has promised that no this-pointer adjustment is
 | ||||
|             // required in that case, so it's OK to use static_cast.
 | ||||
|             if (const auto *tpi = get_type_info(*instance_type)) | ||||
|             if (const auto *tpi = get_type_info(*instance_type)) { | ||||
|                 return {vsrc, tpi}; | ||||
|             } | ||||
|         // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so
 | ||||
|         // don't do a cast
 | ||||
|         } | ||||
|         // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer,
 | ||||
|         // so don't do a cast
 | ||||
|         return type_caster_generic::src_and_type(src, cast_type, instance_type); | ||||
|     } | ||||
| 
 | ||||
|     static handle cast(const itype *src, return_value_policy policy, handle parent) { | ||||
|         auto st = src_and_type(src); | ||||
|         return type_caster_generic::cast( | ||||
|             st.first, policy, parent, st.second, | ||||
|             make_copy_constructor(src), make_move_constructor(src)); | ||||
|         return type_caster_generic::cast(st.first, | ||||
|                                          policy, | ||||
|                                          parent, | ||||
|                                          st.second, | ||||
|                                          make_copy_constructor(src), | ||||
|                                          make_move_constructor(src)); | ||||
|     } | ||||
| 
 | ||||
|     static handle cast_holder(const itype *src, const void *holder) { | ||||
|         auto st = src_and_type(src); | ||||
|         return type_caster_generic::cast( | ||||
|             st.first, return_value_policy::take_ownership, {}, st.second, | ||||
|             nullptr, nullptr, holder); | ||||
|         return type_caster_generic::cast(st.first, | ||||
|                                          return_value_policy::take_ownership, | ||||
|                                          {}, | ||||
|                                          st.second, | ||||
|                                          nullptr, | ||||
|                                          nullptr, | ||||
|                                          holder); | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> using cast_op_type = detail::cast_op_type<T>; | ||||
|     template <typename T> | ||||
|     using cast_op_type = detail::cast_op_type<T>; | ||||
| 
 | ||||
|     // NOLINTNEXTLINE(google-explicit-constructor)
 | ||||
|     operator itype *() { return (type *) value; } | ||||
|     // NOLINTNEXTLINE(google-explicit-constructor)
 | ||||
|     operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } | ||||
|     operator itype &() { | ||||
|         if (!value) { | ||||
|             throw reference_cast_error(); | ||||
|         } | ||||
|         return *((itype *) value); | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     using Constructor = void *(*) (const void *); | ||||
| 
 | ||||
|     /* Only enabled when the types are {copy,move}-constructible *and* when the type
 | ||||
|        does not have a private operator new implementation. A comma operator is used in the decltype | ||||
|        argument to apply SFINAE to the public copy/move constructors.*/ | ||||
|        does not have a private operator new implementation. A comma operator is used in the | ||||
|        decltype argument to apply SFINAE to the public copy/move constructors.*/ | ||||
|     template <typename T, typename = enable_if_t<is_copy_constructible<T>::value>> | ||||
|     static auto make_copy_constructor(const T *) -> decltype(new T(std::declval<const T>()), Constructor{}) { | ||||
|         return [](const void *arg) -> void * { | ||||
|             return new T(*reinterpret_cast<const T *>(arg)); | ||||
|         }; | ||||
|     static auto make_copy_constructor(const T *) | ||||
|         -> decltype(new T(std::declval<const T>()), Constructor{}) { | ||||
|         return [](const void *arg) -> void * { return new T(*reinterpret_cast<const T *>(arg)); }; | ||||
|     } | ||||
| 
 | ||||
|     template <typename T, typename = enable_if_t<std::is_move_constructible<T>::value>> | ||||
|     static auto make_move_constructor(const T *) -> decltype(new T(std::declval<T&&>()), Constructor{}) { | ||||
|     static auto make_move_constructor(const T *) | ||||
|         -> decltype(new T(std::declval<T &&>()), Constructor{}) { | ||||
|         return [](const void *arg) -> void * { | ||||
|             return new T(std::move(*const_cast<T *>(reinterpret_cast<const T *>(arg)))); | ||||
|         }; | ||||
|  |  | |||
|  | @ -20,11 +20,14 @@ | |||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| /// Erase all occurrences of a substring
 | ||||
| inline void erase_all(std::string &string, const std::string &search) { | ||||
|     for (size_t pos = 0;;) { | ||||
|         pos = string.find(search, pos); | ||||
|         if (pos == std::string::npos) break; | ||||
|         if (pos == std::string::npos) { | ||||
|             break; | ||||
|         } | ||||
|         string.erase(pos, search.length()); | ||||
|     } | ||||
| } | ||||
|  | @ -34,8 +37,9 @@ PYBIND11_NOINLINE void clean_type_id(std::string &name) { | |||
|     int status = 0; | ||||
|     std::unique_ptr<char, void (*)(void *)> res{ | ||||
|         abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free}; | ||||
|     if (status == 0) | ||||
|     if (status == 0) { | ||||
|         name = res.get(); | ||||
|     } | ||||
| #else | ||||
|     detail::erase_all(name, "class "); | ||||
|     detail::erase_all(name, "struct "); | ||||
|  | @ -43,13 +47,19 @@ PYBIND11_NOINLINE void clean_type_id(std::string &name) { | |||
| #endif | ||||
|     detail::erase_all(name, "pybind11::"); | ||||
| } | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
| /// Return a string representation of a C++ type
 | ||||
| template <typename T> static std::string type_id() { | ||||
|     std::string name(typeid(T).name()); | ||||
| inline std::string clean_type_id(const char *typeid_name) { | ||||
|     std::string name(typeid_name); | ||||
|     detail::clean_type_id(name); | ||||
|     return name; | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
| /// Return a string representation of a C++ type
 | ||||
| template <typename T> | ||||
| static std::string type_id() { | ||||
|     return detail::clean_type_id(typeid(T).name()); | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) | ||||
|  |  | |||
|  | @ -25,6 +25,8 @@ | |||
| #if defined(_MSC_VER) | ||||
| #    pragma warning(push) | ||||
| #    pragma warning(disable : 4127) // C4127: conditional expression is constant
 | ||||
| #    pragma warning(disable : 5054) // https://github.com/pybind/pybind11/pull/3741
 | ||||
| //       C5054: operator '&': deprecated between enumerations of different types
 | ||||
| #endif | ||||
| 
 | ||||
| #include <Eigen/Core> | ||||
|  | @ -37,14 +39,17 @@ | |||
| // Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
 | ||||
| // move constructors that break things.  We could detect this an explicitly copy, but an extra copy
 | ||||
| // of matrices seems highly undesirable.
 | ||||
| static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); | ||||
| static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7), | ||||
|               "Eigen support in pybind11 requires Eigen >= 3.2.7"); | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| 
 | ||||
| // Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
 | ||||
| using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>; | ||||
| template <typename MatrixType> using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>; | ||||
| template <typename MatrixType> using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>; | ||||
| template <typename MatrixType> | ||||
| using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>; | ||||
| template <typename MatrixType> | ||||
| using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
|  | @ -59,21 +64,28 @@ using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageInd | |||
| #endif | ||||
| 
 | ||||
| // Matches Eigen::Map, Eigen::Ref, blocks, etc:
 | ||||
| template <typename T> using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>, std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>; | ||||
| template <typename T> using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>; | ||||
| template <typename T> using is_eigen_dense_plain = all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>; | ||||
| template <typename T> using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>; | ||||
| template <typename T> | ||||
| using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>, | ||||
|                                   std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>; | ||||
| template <typename T> | ||||
| using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>; | ||||
| template <typename T> | ||||
| using is_eigen_dense_plain | ||||
|     = all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>; | ||||
| template <typename T> | ||||
| using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>; | ||||
| // Test for objects inheriting from EigenBase<Derived> that aren't captured by the above.  This
 | ||||
| // basically covers anything that can be assigned to a dense matrix but that don't have a typical
 | ||||
| // matrix data layout that can be copied from their .data().  For example, DiagonalMatrix and
 | ||||
| // SelfAdjointView fall into this category.
 | ||||
| template <typename T> using is_eigen_other = all_of< | ||||
|     is_template_base_of<Eigen::EigenBase, T>, | ||||
|     negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>> | ||||
| >; | ||||
| template <typename T> | ||||
| using is_eigen_other | ||||
|     = all_of<is_template_base_of<Eigen::EigenBase, T>, | ||||
|              negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>; | ||||
| 
 | ||||
| // Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
 | ||||
| template <bool EigenRowMajor> struct EigenConformable { | ||||
| template <bool EigenRowMajor> | ||||
| struct EigenConformable { | ||||
|     bool conformable = false; | ||||
|     EigenIndex rows = 0, cols = 0; | ||||
|     EigenDStride stride{0, 0};    // Only valid if negativestrides is false!
 | ||||
|  | @ -82,94 +94,114 @@ template <bool EigenRowMajor> struct EigenConformable { | |||
|     // NOLINTNEXTLINE(google-explicit-constructor)
 | ||||
|     EigenConformable(bool fits = false) : conformable{fits} {} | ||||
|     // Matrix type:
 | ||||
|     EigenConformable(EigenIndex r, EigenIndex c, | ||||
|             EigenIndex rstride, EigenIndex cstride) : | ||||
|         conformable{true}, rows{r}, cols{c}, | ||||
|         //TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
 | ||||
|         stride{EigenRowMajor ? (rstride > 0 ? rstride : 0) : (cstride > 0 ? cstride : 0) /* outer stride */, | ||||
|                EigenRowMajor ? (cstride > 0 ? cstride : 0) : (rstride > 0 ? rstride : 0) /* inner stride */ }, | ||||
|         negativestrides{rstride < 0 || cstride < 0} { | ||||
| 
 | ||||
|     } | ||||
|     EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride) | ||||
|         : conformable{true}, rows{r}, cols{c}, | ||||
|           // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
 | ||||
|           // http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
 | ||||
|           stride{EigenRowMajor ? (rstride > 0 ? rstride : 0) | ||||
|                                : (cstride > 0 ? cstride : 0) /* outer stride */, | ||||
|                  EigenRowMajor ? (cstride > 0 ? cstride : 0) | ||||
|                                : (rstride > 0 ? rstride : 0) /* inner stride */}, | ||||
|           negativestrides{rstride < 0 || cstride < 0} {} | ||||
|     // Vector type:
 | ||||
|     EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) | ||||
|         : EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {} | ||||
| 
 | ||||
|     template <typename props> bool stride_compatible() const { | ||||
|     template <typename props> | ||||
|     bool stride_compatible() const { | ||||
|         // To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
 | ||||
|         // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant)
 | ||||
|         return | ||||
|             !negativestrides && | ||||
|             (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || | ||||
|                 (EigenRowMajor ? cols : rows) == 1) && | ||||
|             (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || | ||||
|                 (EigenRowMajor ? rows : cols) == 1); | ||||
|         // matching strides, or a dimension size of 1 (in which case the stride value is
 | ||||
|         // irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant
 | ||||
|         // (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly).
 | ||||
|         if (negativestrides) { | ||||
|             return false; | ||||
|         } | ||||
|         if (rows == 0 || cols == 0) { | ||||
|             return true; | ||||
|         } | ||||
|         return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() | ||||
|                 || (EigenRowMajor ? cols : rows) == 1) | ||||
|                && (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() | ||||
|                    || (EigenRowMajor ? rows : cols) == 1); | ||||
|     } | ||||
|     // NOLINTNEXTLINE(google-explicit-constructor)
 | ||||
|     operator bool() const { return conformable; } | ||||
| }; | ||||
| 
 | ||||
| template <typename Type> struct eigen_extract_stride { using type = Type; }; | ||||
| template <typename Type> | ||||
| struct eigen_extract_stride { | ||||
|     using type = Type; | ||||
| }; | ||||
| template <typename PlainObjectType, int MapOptions, typename StrideType> | ||||
| struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> { using type = StrideType; }; | ||||
| struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> { | ||||
|     using type = StrideType; | ||||
| }; | ||||
| template <typename PlainObjectType, int Options, typename StrideType> | ||||
| struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> { using type = StrideType; }; | ||||
| struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> { | ||||
|     using type = StrideType; | ||||
| }; | ||||
| 
 | ||||
| // Helper struct for extracting information from an Eigen type
 | ||||
| template <typename Type_> struct EigenProps { | ||||
| template <typename Type_> | ||||
| struct EigenProps { | ||||
|     using Type = Type_; | ||||
|     using Scalar = typename Type::Scalar; | ||||
|     using StrideType = typename eigen_extract_stride<Type>::type; | ||||
|     static constexpr EigenIndex | ||||
|         rows = Type::RowsAtCompileTime, | ||||
|         cols = Type::ColsAtCompileTime, | ||||
|     static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime, | ||||
|                                 size = Type::SizeAtCompileTime; | ||||
|     static constexpr bool | ||||
|         row_major = Type::IsRowMajor, | ||||
|         vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
 | ||||
|         fixed_rows = rows != Eigen::Dynamic, | ||||
|         fixed_cols = cols != Eigen::Dynamic, | ||||
|     static constexpr bool row_major = Type::IsRowMajor, | ||||
|                           vector | ||||
|                           = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
 | ||||
|         fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic, | ||||
|                           fixed = size != Eigen::Dynamic, // Fully-fixed size
 | ||||
|         dynamic = !fixed_rows && !fixed_cols;             // Fully-dynamic size
 | ||||
| 
 | ||||
|     template <EigenIndex i, EigenIndex ifzero> using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>; | ||||
|     static constexpr EigenIndex inner_stride = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value, | ||||
|     template <EigenIndex i, EigenIndex ifzero> | ||||
|     using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>; | ||||
|     static constexpr EigenIndex inner_stride | ||||
|         = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value, | ||||
|         outer_stride = if_zero < StrideType::OuterStrideAtCompileTime, | ||||
|                                                        vector ? size : row_major ? cols : rows>::value; | ||||
|     static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; | ||||
|     static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; | ||||
|     static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; | ||||
|         vector      ? size | ||||
|         : row_major ? cols | ||||
|                     : rows > ::value; | ||||
|     static constexpr bool dynamic_stride | ||||
|         = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; | ||||
|     static constexpr bool requires_row_major | ||||
|         = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; | ||||
|     static constexpr bool requires_col_major | ||||
|         = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; | ||||
| 
 | ||||
|     // Takes an input array and determines whether we can make it fit into the Eigen type.  If
 | ||||
|     // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
 | ||||
|     // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type).
 | ||||
|     static EigenConformable<row_major> conformable(const array &a) { | ||||
|         const auto dims = a.ndim(); | ||||
|         if (dims < 1 || dims > 2) | ||||
|         if (dims < 1 || dims > 2) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (dims == 2) { // Matrix type: require exact match (or dynamic)
 | ||||
| 
 | ||||
|             EigenIndex | ||||
|                 np_rows = a.shape(0), | ||||
|                 np_cols = a.shape(1), | ||||
|             EigenIndex np_rows = a.shape(0), np_cols = a.shape(1), | ||||
|                        np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)), | ||||
|                        np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar)); | ||||
|             if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows) || | ||||
|                 (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) | ||||
|             if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows) | ||||
|                 || (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             return {np_rows, np_cols, np_rstride, np_cstride}; | ||||
|         } | ||||
| 
 | ||||
|         // Otherwise we're storing an n-vector.  Only one of the strides will be used, but whichever
 | ||||
|         // is used, we want the (single) numpy stride value.
 | ||||
|         // Otherwise we're storing an n-vector.  Only one of the strides will be used, but
 | ||||
|         // whichever is used, we want the (single) numpy stride value.
 | ||||
|         const EigenIndex n = a.shape(0), | ||||
|                          stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)); | ||||
| 
 | ||||
|         if (vector) { // Eigen type is a compile-time vector
 | ||||
|             if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) | ||||
|             if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) { | ||||
|                 return false; // Vector size mismatch
 | ||||
|             } | ||||
|             return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; | ||||
|         } | ||||
|         if (fixed) { | ||||
|  | @ -179,48 +211,59 @@ template <typename Type_> struct EigenProps { | |||
|         if (fixed_cols) { | ||||
|             // Since this isn't a vector, cols must be != 1.  We allow this only if it exactly
 | ||||
|             // equals the number of elements (rows is Dynamic, and so 1 row is allowed).
 | ||||
|             if (cols != n) return false; | ||||
|             if (cols != n) { | ||||
|                 return false; | ||||
|             } | ||||
|             return {1, n, stride}; | ||||
|         } // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
 | ||||
|             if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) return false; | ||||
|         if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) { | ||||
|             return false; | ||||
|         } | ||||
|         return {n, 1, stride}; | ||||
|     } | ||||
| 
 | ||||
|     static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value; | ||||
|     static constexpr bool show_writeable | ||||
|         = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value; | ||||
|     static constexpr bool show_order = is_eigen_dense_map<Type>::value; | ||||
|     static constexpr bool show_c_contiguous = show_order && requires_row_major; | ||||
|     static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; | ||||
|     static constexpr bool show_f_contiguous | ||||
|         = !show_c_contiguous && show_order && requires_col_major; | ||||
| 
 | ||||
|     static constexpr auto descriptor = | ||||
|         const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + | ||||
|         const_name("[")  + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + | ||||
|         const_name(", ") + const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + | ||||
|         const_name("]") + | ||||
|         // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
 | ||||
|         // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
 | ||||
|         // options, possibly f_contiguous or c_contiguous.  We include them in the descriptor output
 | ||||
|         // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
 | ||||
|         // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
 | ||||
|         // *gave* a numpy.ndarray of the right type and dimensions.
 | ||||
|         const_name<show_writeable>(", flags.writeable", "") + | ||||
|         const_name<show_c_contiguous>(", flags.c_contiguous", "") + | ||||
|         const_name<show_f_contiguous>(", flags.f_contiguous", "") + | ||||
|         const_name("]"); | ||||
|     static constexpr auto descriptor | ||||
|         = const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[") | ||||
|           + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ") | ||||
|           + const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]") | ||||
|           + | ||||
|           // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
 | ||||
|           // be satisfied: writeable=True (for a mutable reference), and, depending on the map's
 | ||||
|           // stride options, possibly f_contiguous or c_contiguous.  We include them in the
 | ||||
|           // descriptor output to provide some hint as to why a TypeError is occurring (otherwise
 | ||||
|           // it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
 | ||||
|           // an error message that you *gave* a numpy.ndarray of the right type and dimensions.
 | ||||
|           const_name<show_writeable>(", flags.writeable", "") | ||||
|           + const_name<show_c_contiguous>(", flags.c_contiguous", "") | ||||
|           + const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]"); | ||||
| }; | ||||
| 
 | ||||
| // Casts an Eigen type to numpy array.  If given a base, the numpy array references the src data,
 | ||||
| // otherwise it'll make a copy.  writeable lets you turn off the writeable flag for the array.
 | ||||
| template <typename props> handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { | ||||
| template <typename props> | ||||
| handle | ||||
| eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { | ||||
|     constexpr ssize_t elem_size = sizeof(typename props::Scalar); | ||||
|     array a; | ||||
|     if (props::vector) | ||||
|     if (props::vector) { | ||||
|         a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base); | ||||
|     else | ||||
|         a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, | ||||
|                   src.data(), base); | ||||
|     } else { | ||||
|         a = array({src.rows(), src.cols()}, | ||||
|                   {elem_size * src.rowStride(), elem_size * src.colStride()}, | ||||
|                   src.data(), | ||||
|                   base); | ||||
|     } | ||||
| 
 | ||||
|     if (!writeable) | ||||
|     if (!writeable) { | ||||
|         array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; | ||||
|     } | ||||
| 
 | ||||
|     return a.release(); | ||||
| } | ||||
|  | @ -236,10 +279,10 @@ handle eigen_ref_array(Type &src, handle parent = none()) { | |||
|     return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value); | ||||
| } | ||||
| 
 | ||||
| // Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy
 | ||||
| // array that references the encapsulated data with a python-side reference to the capsule to tie
 | ||||
| // its destruction to that of any dependent python objects.  Const-ness is determined by whether or
 | ||||
| // not the Type of the pointer given is const.
 | ||||
| // Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
 | ||||
| // numpy array that references the encapsulated data with a python-side reference to the capsule to
 | ||||
| // tie its destruction to that of any dependent python objects.  Const-ness is determined by
 | ||||
| // whether or not the Type of the pointer given is const.
 | ||||
| template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>> | ||||
| handle eigen_encapsulate(Type *src) { | ||||
|     capsule base(src, [](void *o) { delete static_cast<Type *>(o); }); | ||||
|  | @ -255,28 +298,35 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> { | |||
| 
 | ||||
|     bool load(handle src, bool convert) { | ||||
|         // If we're in no-convert mode, only load if given an array of the correct type
 | ||||
|         if (!convert && !isinstance<array_t<Scalar>>(src)) | ||||
|         if (!convert && !isinstance<array_t<Scalar>>(src)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         // Coerce into an array, but don't do type conversion yet; the copy below handles it.
 | ||||
|         auto buf = array::ensure(src); | ||||
| 
 | ||||
|         if (!buf) | ||||
|         if (!buf) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         auto dims = buf.ndim(); | ||||
|         if (dims < 1 || dims > 2) | ||||
|         if (dims < 1 || dims > 2) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         auto fits = props::conformable(buf); | ||||
|         if (!fits) | ||||
|         if (!fits) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         // Allocate the new type, then build a numpy reference into it
 | ||||
|         value = Type(fits.rows, fits.cols); | ||||
|         auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value)); | ||||
|         if (dims == 1) ref = ref.squeeze(); | ||||
|         else if (ref.ndim() == 1) buf = buf.squeeze(); | ||||
|         if (dims == 1) { | ||||
|             ref = ref.squeeze(); | ||||
|         } else if (ref.ndim() == 1) { | ||||
|             buf = buf.squeeze(); | ||||
|         } | ||||
| 
 | ||||
|         int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); | ||||
| 
 | ||||
|  | @ -289,7 +339,6 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> { | |||
|     } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     // Cast implementation
 | ||||
|     template <typename CType> | ||||
|     static handle cast_impl(CType *src, return_value_policy policy, handle parent) { | ||||
|  | @ -312,7 +361,6 @@ private: | |||
|     } | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     // Normal returned non-reference, non-const value:
 | ||||
|     static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { | ||||
|         return cast_impl(&src, return_value_policy::move, parent); | ||||
|  | @ -323,14 +371,18 @@ public: | |||
|     } | ||||
|     // lvalue reference return; default (automatic) becomes copy
 | ||||
|     static handle cast(Type &src, return_value_policy policy, handle parent) { | ||||
|         if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) | ||||
|         if (policy == return_value_policy::automatic | ||||
|             || policy == return_value_policy::automatic_reference) { | ||||
|             policy = return_value_policy::copy; | ||||
|         } | ||||
|         return cast_impl(&src, policy, parent); | ||||
|     } | ||||
|     // const lvalue reference return; default (automatic) becomes copy
 | ||||
|     static handle cast(const Type &src, return_value_policy policy, handle parent) { | ||||
|         if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) | ||||
|         if (policy == return_value_policy::automatic | ||||
|             || policy == return_value_policy::automatic_reference) { | ||||
|             policy = return_value_policy::copy; | ||||
|         } | ||||
|         return cast(&src, policy, parent); | ||||
|     } | ||||
|     // non-const pointer return
 | ||||
|  | @ -350,25 +402,26 @@ public: | |||
|     operator Type &() { return value; } | ||||
|     // NOLINTNEXTLINE(google-explicit-constructor)
 | ||||
|     operator Type &&() && { return std::move(value); } | ||||
|     template <typename T> using cast_op_type = movable_cast_op_type<T>; | ||||
|     template <typename T> | ||||
|     using cast_op_type = movable_cast_op_type<T>; | ||||
| 
 | ||||
| private: | ||||
|     Type value; | ||||
| }; | ||||
| 
 | ||||
| // Base class for casting reference/map/block/etc. objects back to python.
 | ||||
| template <typename MapType> struct eigen_map_caster { | ||||
| template <typename MapType> | ||||
| struct eigen_map_caster { | ||||
| private: | ||||
|     using props = EigenProps<MapType>; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
 | ||||
|     // to stay around), but we'll allow it under the assumption that you know what you're doing (and
 | ||||
|     // have an appropriate keep_alive in place).  We return a numpy array pointing directly at the
 | ||||
|     // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note
 | ||||
|     // that this means you need to ensure you don't destroy the object in some other way (e.g. with
 | ||||
|     // an appropriate keep_alive, or with a reference to a statically allocated matrix).
 | ||||
|     // to stay around), but we'll allow it under the assumption that you know what you're doing
 | ||||
|     // (and have an appropriate keep_alive in place).  We return a numpy array pointing directly at
 | ||||
|     // the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
 | ||||
|     // Note that this means you need to ensure you don't destroy the object in some other way (e.g.
 | ||||
|     // with an appropriate keep_alive, or with a reference to a statically allocated matrix).
 | ||||
|     static handle cast(const MapType &src, return_value_policy policy, handle parent) { | ||||
|         switch (policy) { | ||||
|             case return_value_policy::copy: | ||||
|  | @ -392,43 +445,50 @@ public: | |||
|     // you end up here if you try anyway.
 | ||||
|     bool load(handle, bool) = delete; | ||||
|     operator MapType() = delete; | ||||
|     template <typename> using cast_op_type = MapType; | ||||
|     template <typename> | ||||
|     using cast_op_type = MapType; | ||||
| }; | ||||
| 
 | ||||
| // We can return any map-like object (but can only load Refs, specialized next):
 | ||||
| template <typename Type> struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> | ||||
|     : eigen_map_caster<Type> {}; | ||||
| template <typename Type> | ||||
| struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {}; | ||||
| 
 | ||||
| // Loader for Ref<...> arguments.  See the documentation for info on how to make this work without
 | ||||
| // copying (it requires some extra effort in many cases).
 | ||||
| template <typename PlainObjectType, typename StrideType> | ||||
| struct type_caster< | ||||
|     Eigen::Ref<PlainObjectType, 0, StrideType>, | ||||
|     enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value> | ||||
| > : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> { | ||||
|     enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>> | ||||
|     : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> { | ||||
| private: | ||||
|     using Type = Eigen::Ref<PlainObjectType, 0, StrideType>; | ||||
|     using props = EigenProps<Type>; | ||||
|     using Scalar = typename props::Scalar; | ||||
|     using MapType = Eigen::Map<PlainObjectType, 0, StrideType>; | ||||
|     using Array = array_t<Scalar, array::forcecast | | ||||
|                 ((props::row_major ? props::inner_stride : props::outer_stride) == 1 ? array::c_style : | ||||
|                  (props::row_major ? props::outer_stride : props::inner_stride) == 1 ? array::f_style : 0)>; | ||||
|     using Array | ||||
|         = array_t<Scalar, | ||||
|                   array::forcecast | ||||
|                       | ((props::row_major ? props::inner_stride : props::outer_stride) == 1 | ||||
|                              ? array::c_style | ||||
|                          : (props::row_major ? props::outer_stride : props::inner_stride) == 1 | ||||
|                              ? array::f_style | ||||
|                              : 0)>; | ||||
|     static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value; | ||||
|     // Delay construction (these have no default constructor)
 | ||||
|     std::unique_ptr<MapType> map; | ||||
|     std::unique_ptr<Type> ref; | ||||
|     // Our array.  When possible, this is just a numpy array pointing to the source data, but
 | ||||
|     // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible
 | ||||
|     // layout, or is an array of a type that needs to be converted).  Using a numpy temporary
 | ||||
|     // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and
 | ||||
|     // storage order conversion.  (Note that we refuse to use this temporary copy when loading an
 | ||||
|     // argument for a Ref<M> with M non-const, i.e. a read-write reference).
 | ||||
|     // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
 | ||||
|     // incompatible layout, or is an array of a type that needs to be converted).  Using a numpy
 | ||||
|     // temporary (rather than an Eigen temporary) saves an extra copy when we need both type
 | ||||
|     // conversion and storage order conversion.  (Note that we refuse to use this temporary copy
 | ||||
|     // when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
 | ||||
|     Array copy_or_ref; | ||||
| 
 | ||||
| public: | ||||
|     bool load(handle src, bool convert) { | ||||
|         // First check whether what we have is already an array of the right type.  If not, we can't
 | ||||
|         // avoid a copy (because the copy is also going to do type conversion).
 | ||||
|         // First check whether what we have is already an array of the right type.  If not, we
 | ||||
|         // can't avoid a copy (because the copy is also going to do type conversion).
 | ||||
|         bool need_copy = !isinstance<Array>(src); | ||||
| 
 | ||||
|         EigenConformable<props::row_major> fits; | ||||
|  | @ -439,13 +499,15 @@ public: | |||
| 
 | ||||
|             if (aref && (!need_writeable || aref.writeable())) { | ||||
|                 fits = props::conformable(aref); | ||||
|                 if (!fits) return false; // Incompatible dimensions
 | ||||
|                 if (!fits.template stride_compatible<props>()) | ||||
|                 if (!fits) { | ||||
|                     return false; // Incompatible dimensions
 | ||||
|                 } | ||||
|                 if (!fits.template stride_compatible<props>()) { | ||||
|                     need_copy = true; | ||||
|                 else | ||||
|                 } else { | ||||
|                     copy_or_ref = std::move(aref); | ||||
|                 } | ||||
|             else { | ||||
|             } else { | ||||
|                 need_copy = true; | ||||
|             } | ||||
|         } | ||||
|  | @ -454,19 +516,27 @@ public: | |||
|             // We need to copy: If we need a mutable reference, or we're not supposed to convert
 | ||||
|             // (either because we're in the no-convert overload pass, or because we're explicitly
 | ||||
|             // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
 | ||||
|             if (!convert || need_writeable) return false; | ||||
|             if (!convert || need_writeable) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             Array copy = Array::ensure(src); | ||||
|             if (!copy) return false; | ||||
|             fits = props::conformable(copy); | ||||
|             if (!fits || !fits.template stride_compatible<props>()) | ||||
|             if (!copy) { | ||||
|                 return false; | ||||
|             } | ||||
|             fits = props::conformable(copy); | ||||
|             if (!fits || !fits.template stride_compatible<props>()) { | ||||
|                 return false; | ||||
|             } | ||||
|             copy_or_ref = std::move(copy); | ||||
|             loader_life_support::add_patient(copy_or_ref); | ||||
|         } | ||||
| 
 | ||||
|         ref.reset(); | ||||
|         map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); | ||||
|         map.reset(new MapType(data(copy_or_ref), | ||||
|                               fits.rows, | ||||
|                               fits.cols, | ||||
|                               make_stride(fits.stride.outer(), fits.stride.inner()))); | ||||
|         ref.reset(new Type(*map)); | ||||
| 
 | ||||
|         return true; | ||||
|  | @ -476,44 +546,63 @@ public: | |||
|     operator Type *() { return ref.get(); } | ||||
|     // NOLINTNEXTLINE(google-explicit-constructor)
 | ||||
|     operator Type &() { return *ref; } | ||||
|     template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>; | ||||
|     template <typename _T> | ||||
|     using cast_op_type = pybind11::detail::cast_op_type<_T>; | ||||
| 
 | ||||
| private: | ||||
|     template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0> | ||||
|     Scalar *data(Array &a) { return a.mutable_data(); } | ||||
|     Scalar *data(Array &a) { | ||||
|         return a.mutable_data(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0> | ||||
|     const Scalar *data(Array &a) { return a.data(); } | ||||
|     const Scalar *data(Array &a) { | ||||
|         return a.data(); | ||||
|     } | ||||
| 
 | ||||
|     // Attempt to figure out a constructor of `Stride` that will work.
 | ||||
|     // If both strides are fixed, use a default constructor:
 | ||||
|     template <typename S> using stride_ctor_default = bool_constant< | ||||
|         S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && | ||||
|         std::is_default_constructible<S>::value>; | ||||
|     template <typename S> | ||||
|     using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic | ||||
|                                               && S::OuterStrideAtCompileTime != Eigen::Dynamic | ||||
|                                               && std::is_default_constructible<S>::value>; | ||||
|     // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
 | ||||
|     // Eigen::Stride, and use it:
 | ||||
|     template <typename S> using stride_ctor_dual = bool_constant< | ||||
|         !stride_ctor_default<S>::value && std::is_constructible<S, EigenIndex, EigenIndex>::value>; | ||||
|     template <typename S> | ||||
|     using stride_ctor_dual | ||||
|         = bool_constant<!stride_ctor_default<S>::value | ||||
|                         && std::is_constructible<S, EigenIndex, EigenIndex>::value>; | ||||
|     // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
 | ||||
|     // it (passing whichever stride is dynamic).
 | ||||
|     template <typename S> using stride_ctor_outer = bool_constant< | ||||
|         !any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value && | ||||
|         S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && | ||||
|         std::is_constructible<S, EigenIndex>::value>; | ||||
|     template <typename S> using stride_ctor_inner = bool_constant< | ||||
|         !any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value && | ||||
|         S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && | ||||
|         std::is_constructible<S, EigenIndex>::value>; | ||||
|     template <typename S> | ||||
|     using stride_ctor_outer | ||||
|         = bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value | ||||
|                         && S::OuterStrideAtCompileTime == Eigen::Dynamic | ||||
|                         && S::InnerStrideAtCompileTime != Eigen::Dynamic | ||||
|                         && std::is_constructible<S, EigenIndex>::value>; | ||||
|     template <typename S> | ||||
|     using stride_ctor_inner | ||||
|         = bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value | ||||
|                         && S::InnerStrideAtCompileTime == Eigen::Dynamic | ||||
|                         && S::OuterStrideAtCompileTime != Eigen::Dynamic | ||||
|                         && std::is_constructible<S, EigenIndex>::value>; | ||||
| 
 | ||||
|     template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0> | ||||
|     static S make_stride(EigenIndex, EigenIndex) { return S(); } | ||||
|     static S make_stride(EigenIndex, EigenIndex) { | ||||
|         return S(); | ||||
|     } | ||||
|     template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0> | ||||
|     static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } | ||||
|     static S make_stride(EigenIndex outer, EigenIndex inner) { | ||||
|         return S(outer, inner); | ||||
|     } | ||||
|     template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0> | ||||
|     static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } | ||||
|     static S make_stride(EigenIndex outer, EigenIndex) { | ||||
|         return S(outer); | ||||
|     } | ||||
|     template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0> | ||||
|     static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } | ||||
| 
 | ||||
|     static S make_stride(EigenIndex, EigenIndex inner) { | ||||
|         return S(inner); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
 | ||||
|  | @ -523,14 +612,18 @@ private: | |||
| template <typename Type> | ||||
| struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> { | ||||
| protected: | ||||
|     using Matrix = Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>; | ||||
|     using Matrix | ||||
|         = Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>; | ||||
|     using props = EigenProps<Matrix>; | ||||
| 
 | ||||
| public: | ||||
|     static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { | ||||
|         handle h = eigen_encapsulate<props>(new Matrix(src)); | ||||
|         return h; | ||||
|     } | ||||
|     static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } | ||||
|     static handle cast(const Type *src, return_value_policy policy, handle parent) { | ||||
|         return cast(*src, policy, parent); | ||||
|     } | ||||
| 
 | ||||
|     static constexpr auto name = props::descriptor; | ||||
| 
 | ||||
|  | @ -539,7 +632,8 @@ public: | |||
|     // you end up here if you try anyway.
 | ||||
|     bool load(handle, bool) = delete; | ||||
|     operator Type() = delete; | ||||
|     template <typename> using cast_op_type = Type; | ||||
|     template <typename> | ||||
|     using cast_op_type = Type; | ||||
| }; | ||||
| 
 | ||||
| template <typename Type> | ||||
|  | @ -550,13 +644,13 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> { | |||
|     static constexpr bool rowMajor = Type::IsRowMajor; | ||||
| 
 | ||||
|     bool load(handle src, bool) { | ||||
|         if (!src) | ||||
|         if (!src) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         auto obj = reinterpret_borrow<object>(src); | ||||
|         object sparse_module = module_::import("scipy.sparse"); | ||||
|         object matrix_type = sparse_module.attr( | ||||
|             rowMajor ? "csr_matrix" : "csc_matrix"); | ||||
|         object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix"); | ||||
| 
 | ||||
|         if (!type::handle_of(obj).is(matrix_type)) { | ||||
|             try { | ||||
|  | @ -572,14 +666,18 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> { | |||
|         auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); | ||||
|         auto nnz = obj.attr("nnz").cast<Index>(); | ||||
| 
 | ||||
|         if (!values || !innerIndices || !outerIndices) | ||||
|         if (!values || !innerIndices || !outerIndices) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         value = EigenMapSparseMatrix<Scalar, | ||||
|                                      Type::Flags &(Eigen::RowMajor | Eigen::ColMajor), | ||||
|                                      StorageIndex>( | ||||
|             shape[0].cast<Index>(), shape[1].cast<Index>(), nnz, | ||||
|             outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); | ||||
|                                      StorageIndex>(shape[0].cast<Index>(), | ||||
|                                                    shape[1].cast<Index>(), | ||||
|                                                    std::move(nnz), | ||||
|                                                    outerIndices.mutable_data(), | ||||
|                                                    innerIndices.mutable_data(), | ||||
|                                                    values.mutable_data()); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
|  | @ -587,20 +685,22 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> { | |||
|     static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { | ||||
|         const_cast<Type &>(src).makeCompressed(); | ||||
| 
 | ||||
|         object matrix_type = module_::import("scipy.sparse").attr( | ||||
|             rowMajor ? "csr_matrix" : "csc_matrix"); | ||||
|         object matrix_type | ||||
|             = module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix"); | ||||
| 
 | ||||
|         array data(src.nonZeros(), src.valuePtr()); | ||||
|         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), | ||||
|             std::make_pair(src.rows(), src.cols()) | ||||
|         ).release(); | ||||
|         return matrix_type(pybind11::make_tuple( | ||||
|                                std::move(data), std::move(innerIndices), std::move(outerIndices)), | ||||
|                            pybind11::make_tuple(src.rows(), src.cols())) | ||||
|             .release(); | ||||
|     } | ||||
| 
 | ||||
|     PYBIND11_TYPE_CASTER(Type, const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") | ||||
|     PYBIND11_TYPE_CASTER(Type, | ||||
|                          const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", | ||||
|                                                              "scipy.sparse.csc_matrix[") | ||||
|                              + npy_format_descriptor<Scalar>::name + const_name("]")); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,19 +19,9 @@ | |||
| #    error Embedding the interpreter is not supported with PyPy | ||||
| #endif | ||||
| 
 | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
| #define PYBIND11_EMBEDDED_MODULE_IMPL(name)                                                       \ | ||||
|     extern "C" PyObject *pybind11_init_impl_##name();                                             \ | ||||
|       extern "C" PyObject *pybind11_init_impl_##name() { \ | ||||
|           return pybind11_init_wrapper_##name();         \ | ||||
|       } | ||||
| #else | ||||
| #  define PYBIND11_EMBEDDED_MODULE_IMPL(name)            \ | ||||
|       extern "C" void pybind11_init_impl_##name();       \ | ||||
|       extern "C" void pybind11_init_impl_##name() {      \ | ||||
|           pybind11_init_wrapper_##name();                \ | ||||
|       } | ||||
| #endif | ||||
|     extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); } | ||||
| 
 | ||||
| /** \rst
 | ||||
|     Add a new module to the table of builtins for the interpreter. Must be | ||||
|  | @ -71,102 +61,31 @@ PYBIND11_NAMESPACE_BEGIN(detail) | |||
| 
 | ||||
| /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
 | ||||
| struct embedded_module { | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|     using init_t = PyObject *(*) (); | ||||
| #else | ||||
|     using init_t = void (*)(); | ||||
| #endif | ||||
|     embedded_module(const char *name, init_t init) { | ||||
|         if (Py_IsInitialized() != 0) | ||||
|         if (Py_IsInitialized() != 0) { | ||||
|             pybind11_fail("Can't add new modules after the interpreter has been initialized"); | ||||
|         } | ||||
| 
 | ||||
|         auto result = PyImport_AppendInittab(name, init); | ||||
|         if (result == -1) | ||||
|         if (result == -1) { | ||||
|             pybind11_fail("Insufficient memory to add a new module"); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| struct wide_char_arg_deleter { | ||||
|     void operator()(wchar_t *ptr) const { | ||||
| #if PY_VERSION_HEX >= 0x030500f0 | ||||
|         // API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
 | ||||
|         PyMem_RawFree(ptr); | ||||
| #else | ||||
|         delete[] ptr; | ||||
| #endif | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| inline wchar_t *widen_chars(const char *safe_arg) { | ||||
| #if PY_VERSION_HEX >= 0x030500f0 | ||||
|     wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr); | ||||
| #else | ||||
|     wchar_t *widened_arg = nullptr; | ||||
| 
 | ||||
| // warning C4996: 'mbstowcs': This function or variable may be unsafe.
 | ||||
| #if defined(_MSC_VER) | ||||
| #pragma warning(push) | ||||
| #pragma warning(disable:4996) | ||||
| #endif | ||||
| 
 | ||||
| #    if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS | ||||
|     size_t count = std::strlen(safe_arg); | ||||
| #    else | ||||
|     size_t count = std::mbstowcs(nullptr, safe_arg, 0); | ||||
| #    endif | ||||
|     if (count != static_cast<size_t>(-1)) { | ||||
|         widened_arg = new wchar_t[count + 1]; | ||||
|         std::mbstowcs(widened_arg, safe_arg, count + 1); | ||||
|     } | ||||
| 
 | ||||
| #if defined(_MSC_VER) | ||||
| #pragma warning(pop) | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|     return widened_arg; | ||||
| } | ||||
| 
 | ||||
| /// Python 2.x/3.x-compatible version of `PySys_SetArgv`
 | ||||
| inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) { | ||||
|     // Before it was special-cased in python 3.8, passing an empty or null argv
 | ||||
|     // caused a segfault, so we have to reimplement the special case ourselves.
 | ||||
|     bool special_case = (argv == nullptr || argc <= 0); | ||||
| 
 | ||||
|     const char *const empty_argv[]{"\0"}; | ||||
|     const char *const *safe_argv = special_case ? empty_argv : argv; | ||||
|     if (special_case) | ||||
|         argc = 1; | ||||
| 
 | ||||
|     auto argv_size = static_cast<size_t>(argc); | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|     // SetArgv* on python 3 takes wchar_t, so we have to convert.
 | ||||
|     std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]); | ||||
|     std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries; | ||||
|     widened_argv_entries.reserve(argv_size); | ||||
|     for (size_t ii = 0; ii < argv_size; ++ii) { | ||||
|         widened_argv_entries.emplace_back(widen_chars(safe_argv[ii])); | ||||
|         if (!widened_argv_entries.back()) { | ||||
|             // A null here indicates a character-encoding failure or the python
 | ||||
|             // interpreter out of memory. Give up.
 | ||||
|             return; | ||||
|         } | ||||
|         widened_argv[ii] = widened_argv_entries.back().get(); | ||||
|     } | ||||
| 
 | ||||
|     auto pysys_argv = widened_argv.get(); | ||||
| #else | ||||
|     // python 2.x
 | ||||
|     std::vector<std::string> strings{safe_argv, safe_argv + argv_size}; | ||||
|     std::vector<char *> char_strings{argv_size}; | ||||
|     for (std::size_t i = 0; i < argv_size; ++i) | ||||
|         char_strings[i] = &strings[i][0]; | ||||
|     char **pysys_argv = char_strings.data(); | ||||
| #endif | ||||
| 
 | ||||
|     PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path)); | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
| /** \rst
 | ||||
|  | @ -192,12 +111,68 @@ inline void initialize_interpreter(bool init_signal_handlers = true, | |||
|                                    int argc = 0, | ||||
|                                    const char *const *argv = nullptr, | ||||
|                                    bool add_program_dir_to_path = true) { | ||||
|     if (Py_IsInitialized() != 0) | ||||
|     if (Py_IsInitialized() != 0) { | ||||
|         pybind11_fail("The interpreter is already running"); | ||||
|     } | ||||
| 
 | ||||
| #if PY_VERSION_HEX < 0x030B0000 | ||||
| 
 | ||||
|     Py_InitializeEx(init_signal_handlers ? 1 : 0); | ||||
| 
 | ||||
|     detail::set_interpreter_argv(argc, argv, add_program_dir_to_path); | ||||
|     // Before it was special-cased in python 3.8, passing an empty or null argv
 | ||||
|     // caused a segfault, so we have to reimplement the special case ourselves.
 | ||||
|     bool special_case = (argv == nullptr || argc <= 0); | ||||
| 
 | ||||
|     const char *const empty_argv[]{"\0"}; | ||||
|     const char *const *safe_argv = special_case ? empty_argv : argv; | ||||
|     if (special_case) { | ||||
|         argc = 1; | ||||
|     } | ||||
| 
 | ||||
|     auto argv_size = static_cast<size_t>(argc); | ||||
|     // SetArgv* on python 3 takes wchar_t, so we have to convert.
 | ||||
|     std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]); | ||||
|     std::vector<std::unique_ptr<wchar_t[], detail::wide_char_arg_deleter>> widened_argv_entries; | ||||
|     widened_argv_entries.reserve(argv_size); | ||||
|     for (size_t ii = 0; ii < argv_size; ++ii) { | ||||
|         widened_argv_entries.emplace_back(detail::widen_chars(safe_argv[ii])); | ||||
|         if (!widened_argv_entries.back()) { | ||||
|             // A null here indicates a character-encoding failure or the python
 | ||||
|             // interpreter out of memory. Give up.
 | ||||
|             return; | ||||
|         } | ||||
|         widened_argv[ii] = widened_argv_entries.back().get(); | ||||
|     } | ||||
| 
 | ||||
|     auto *pysys_argv = widened_argv.get(); | ||||
| 
 | ||||
|     PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path)); | ||||
| #else | ||||
|     PyConfig config; | ||||
|     PyConfig_InitIsolatedConfig(&config); | ||||
|     config.install_signal_handlers = init_signal_handlers ? 1 : 0; | ||||
| 
 | ||||
|     PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast<char *const *>(argv)); | ||||
|     if (PyStatus_Exception(status)) { | ||||
|         // A failure here indicates a character-encoding failure or the python
 | ||||
|         // interpreter out of memory. Give up.
 | ||||
|         PyConfig_Clear(&config); | ||||
|         throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg | ||||
|                                                           : "Failed to prepare CPython"); | ||||
|     } | ||||
|     status = Py_InitializeFromConfig(&config); | ||||
|     PyConfig_Clear(&config); | ||||
|     if (PyStatus_Exception(status)) { | ||||
|         throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg | ||||
|                                                           : "Failed to init CPython"); | ||||
|     } | ||||
|     if (add_program_dir_to_path) { | ||||
|         PyRun_SimpleString("import sys, os.path; " | ||||
|                            "sys.path.insert(0, " | ||||
|                            "os.path.abspath(os.path.dirname(sys.argv[0])) " | ||||
|                            "if sys.argv and os.path.exists(sys.argv[0]) else '')"); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /** \rst
 | ||||
|  | @ -244,8 +219,13 @@ inline void finalize_interpreter() { | |||
|     // during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
 | ||||
|     detail::internals **internals_ptr_ptr = detail::get_internals_pp(); | ||||
|     // It could also be stashed in builtins, so look there too:
 | ||||
|     if (builtins.contains(id) && isinstance<capsule>(builtins[id])) | ||||
|     if (builtins.contains(id) && isinstance<capsule>(builtins[id])) { | ||||
|         internals_ptr_ptr = capsule(builtins[id]); | ||||
|     } | ||||
|     // Local internals contains data managed by the current interpreter, so we must clear them to
 | ||||
|     // avoid undefined behaviors when initializing another interpreter
 | ||||
|     detail::get_local_internals().registered_types_cpp.clear(); | ||||
|     detail::get_local_internals().registered_exception_translators.clear(); | ||||
| 
 | ||||
|     Py_Finalize(); | ||||
| 
 | ||||
|  | @ -285,9 +265,10 @@ public: | |||
|     scoped_interpreter &operator=(scoped_interpreter &&) = delete; | ||||
| 
 | ||||
|     ~scoped_interpreter() { | ||||
|         if (is_valid) | ||||
|         if (is_valid) { | ||||
|             finalize_interpreter(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     bool is_valid = true; | ||||
|  |  | |||
|  | @ -11,19 +11,19 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "pybind11.h" | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| inline void ensure_builtins_in_globals(object &global) { | ||||
| #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000 | ||||
|         // Running exec and eval on Python 2 and 3 adds `builtins` module under
 | ||||
|         // `__builtins__` key to globals if not yet present.
 | ||||
|         // Python 3.8 made PyRun_String behave similarly. Let's also do that for
 | ||||
|         // older versions, for consistency. This was missing from PyPy3.8 7.3.7.
 | ||||
|     // Running exec and eval adds `builtins` module under `__builtins__` key to
 | ||||
|     // globals if not yet present.  Python 3.8 made PyRun_String behave
 | ||||
|     // similarly. Let's also do that for older versions, for consistency. This
 | ||||
|     // was missing from PyPy3.8 7.3.7.
 | ||||
|     if (!global.contains("__builtins__")) | ||||
|         global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE); | ||||
| #else | ||||
|  | @ -46,8 +46,9 @@ enum eval_mode { | |||
| 
 | ||||
| template <eval_mode mode = eval_expr> | ||||
| object eval(const str &expr, object global = globals(), object local = object()) { | ||||
|     if (!local) | ||||
|     if (!local) { | ||||
|         local = global; | ||||
|     } | ||||
| 
 | ||||
|     detail::ensure_builtins_in_globals(global); | ||||
| 
 | ||||
|  | @ -57,24 +58,31 @@ object eval(const str &expr, object global = globals(), object local = object()) | |||
| 
 | ||||
|     int start = 0; | ||||
|     switch (mode) { | ||||
|         case eval_expr:             start = Py_eval_input;   break; | ||||
|         case eval_single_statement: start = Py_single_input; break; | ||||
|         case eval_statements:       start = Py_file_input;   break; | ||||
|         default: pybind11_fail("invalid evaluation mode"); | ||||
|         case eval_expr: | ||||
|             start = Py_eval_input; | ||||
|             break; | ||||
|         case eval_single_statement: | ||||
|             start = Py_single_input; | ||||
|             break; | ||||
|         case eval_statements: | ||||
|             start = Py_file_input; | ||||
|             break; | ||||
|         default: | ||||
|             pybind11_fail("invalid evaluation mode"); | ||||
|     } | ||||
| 
 | ||||
|     PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); | ||||
|     if (!result) | ||||
|     if (!result) { | ||||
|         throw error_already_set(); | ||||
|     } | ||||
|     return reinterpret_steal<object>(result); | ||||
| } | ||||
| 
 | ||||
| template <eval_mode mode = eval_expr, size_t N> | ||||
| 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<mode>(expr, global, local); | ||||
|     auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s); | ||||
|     return eval<mode>(expr, std::move(global), std::move(local)); | ||||
| } | ||||
| 
 | ||||
| inline void exec(const str &expr, object global = globals(), object local = object()) { | ||||
|  | @ -83,10 +91,10 @@ inline void exec(const str &expr, object global = globals(), object local = obje | |||
| 
 | ||||
| template <size_t N> | ||||
| void exec(const char (&s)[N], object global = globals(), object local = object()) { | ||||
|     eval<eval_statements>(s, global, local); | ||||
|     eval<eval_statements>(s, std::move(global), std::move(local)); | ||||
| } | ||||
| 
 | ||||
| #if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000 | ||||
| #if defined(PYPY_VERSION) | ||||
| template <eval_mode mode = eval_statements> | ||||
| object eval_file(str, object, object) { | ||||
|     pybind11_fail("eval_file not supported in PyPy3. Use eval"); | ||||
|  | @ -102,60 +110,45 @@ object eval_file(str) { | |||
| #else | ||||
| template <eval_mode mode = eval_statements> | ||||
| object eval_file(str fname, object global = globals(), object local = object()) { | ||||
|     if (!local) | ||||
|     if (!local) { | ||||
|         local = global; | ||||
|     } | ||||
| 
 | ||||
|     detail::ensure_builtins_in_globals(global); | ||||
| 
 | ||||
|     int start = 0; | ||||
|     switch (mode) { | ||||
|         case eval_expr:             start = Py_eval_input;   break; | ||||
|         case eval_single_statement: start = Py_single_input; break; | ||||
|         case eval_statements:       start = Py_file_input;   break; | ||||
|         default: pybind11_fail("invalid evaluation mode"); | ||||
|         case eval_expr: | ||||
|             start = Py_eval_input; | ||||
|             break; | ||||
|         case eval_single_statement: | ||||
|             start = Py_single_input; | ||||
|             break; | ||||
|         case eval_statements: | ||||
|             start = Py_file_input; | ||||
|             break; | ||||
|         default: | ||||
|             pybind11_fail("invalid evaluation mode"); | ||||
|     } | ||||
| 
 | ||||
|     int closeFile = 1; | ||||
|     std::string fname_str = (std::string) fname; | ||||
| #if PY_VERSION_HEX >= 0x03040000 | ||||
|     FILE *f = _Py_fopen_obj(fname.ptr(), "r"); | ||||
| #elif PY_VERSION_HEX >= 0x03000000 | ||||
|     FILE *f = _Py_fopen(fname.ptr(), "r"); | ||||
| #else | ||||
|     /* No unicode support in open() :( */ | ||||
|     auto fobj = reinterpret_steal<object>(PyFile_FromString( | ||||
|         const_cast<char *>(fname_str.c_str()), | ||||
|         const_cast<char*>("r"))); | ||||
|     FILE *f = nullptr; | ||||
|     if (fobj) | ||||
|         f = PyFile_AsFile(fobj.ptr()); | ||||
|     closeFile = 0; | ||||
| #endif | ||||
|     if (!f) { | ||||
|         PyErr_Clear(); | ||||
|         pybind11_fail("File \"" + fname_str + "\" could not be opened!"); | ||||
|     } | ||||
| 
 | ||||
|     // In Python2, this should be encoded by getfilesystemencoding.
 | ||||
|     // We don't boher setting it since Python2 is past EOL anyway.
 | ||||
|     // See PR#3233
 | ||||
| #if PY_VERSION_HEX >= 0x03000000 | ||||
|     if (!global.contains("__file__")) { | ||||
|         global["__file__"] = std::move(fname); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| #if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) | ||||
|     PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), | ||||
|                                   local.ptr()); | ||||
|     (void) closeFile; | ||||
| #else | ||||
|     PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), | ||||
|                                     local.ptr(), closeFile); | ||||
| #endif | ||||
|     PyObject *result | ||||
|         = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile); | ||||
| 
 | ||||
|     if (!result) | ||||
|     if (!result) { | ||||
|         throw error_already_set(); | ||||
|     } | ||||
|     return reinterpret_steal<object>(result); | ||||
| } | ||||
| #endif | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "pybind11.h" | ||||
| 
 | ||||
| #include <functional> | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
|  | @ -25,12 +26,15 @@ public: | |||
|     bool load(handle src, bool convert) { | ||||
|         if (src.is_none()) { | ||||
|             // Defer accepting None to other overloads (if we aren't in convert mode):
 | ||||
|             if (!convert) return false; | ||||
|             if (!convert) { | ||||
|                 return false; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         if (!isinstance<function>(src)) | ||||
|         if (!isinstance<function>(src)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         auto func = reinterpret_borrow<function>(src); | ||||
| 
 | ||||
|  | @ -43,10 +47,10 @@ public: | |||
|            captured variables), in which case the roundtrip can be avoided. | ||||
|          */ | ||||
|         if (auto cfunc = func.cpp_function()) { | ||||
|             auto cfunc_self = PyCFunction_GET_SELF(cfunc.ptr()); | ||||
|             auto *cfunc_self = PyCFunction_GET_SELF(cfunc.ptr()); | ||||
|             if (isinstance<capsule>(cfunc_self)) { | ||||
|                 auto c = reinterpret_borrow<capsule>(cfunc_self); | ||||
|                 auto rec = (function_record *) c; | ||||
|                 auto *rec = (function_record *) c; | ||||
| 
 | ||||
|                 while (rec != nullptr) { | ||||
|                     if (rec->is_stateless | ||||
|  | @ -73,7 +77,9 @@ public: | |||
|             // This triggers a syntax error under very special conditions (very weird indeed).
 | ||||
|             explicit | ||||
| #endif | ||||
|             func_handle(function &&f_) noexcept : f(std::move(f_)) {} | ||||
|                 func_handle(function &&f_) noexcept | ||||
|                 : f(std::move(f_)) { | ||||
|             } | ||||
|             func_handle(const func_handle &f_) { operator=(f_); } | ||||
|             func_handle &operator=(const func_handle &f_) { | ||||
|                 gil_scoped_acquire acq; | ||||
|  | @ -92,9 +98,8 @@ public: | |||
|             explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {} | ||||
|             Return operator()(Args... args) const { | ||||
|                 gil_scoped_acquire acq; | ||||
|                 object retval(hfunc.f(std::forward<Args>(args)...)); | ||||
|                 /* Visual studio 2015 parser issue: need parentheses around this expression */ | ||||
|                 return (retval.template cast<Return>()); | ||||
|                 // casts the returned object as a rvalue to the return type
 | ||||
|                 return hfunc.f(std::forward<Args>(args)...).template cast<Return>(); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|  | @ -104,17 +109,21 @@ public: | |||
| 
 | ||||
|     template <typename Func> | ||||
|     static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { | ||||
|         if (!f_) | ||||
|         if (!f_) { | ||||
|             return none().inc_ref(); | ||||
|         } | ||||
| 
 | ||||
|         auto result = f_.template target<function_type>(); | ||||
|         if (result) | ||||
|         if (result) { | ||||
|             return cpp_function(*result, policy).release(); | ||||
|         } | ||||
|         return cpp_function(std::forward<Func>(f_), policy).release(); | ||||
|     } | ||||
| 
 | ||||
|     PYBIND11_TYPE_CASTER(type, const_name("Callable[[") + concat(make_caster<Args>::name...) + const_name("], ") | ||||
|                                + make_caster<retval_type>::name + const_name("]")); | ||||
|     PYBIND11_TYPE_CASTER(type, | ||||
|                          const_name("Callable[[") + concat(make_caster<Args>::name...) | ||||
|                              + const_name("], ") + make_caster<retval_type>::name | ||||
|                              + const_name("]")); | ||||
| }; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
|  |  | |||
|  | @ -14,7 +14,6 @@ | |||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| 
 | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| // forward declarations
 | ||||
|  | @ -22,7 +21,6 @@ PyThreadState *get_thread_state_unchecked(); | |||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
| 
 | ||||
| #if defined(WITH_THREAD) && !defined(PYPY_VERSION) | ||||
| 
 | ||||
| /* The functions below essentially reproduce the PyGILState_* API using a RAII
 | ||||
|  | @ -64,9 +62,10 @@ public: | |||
| 
 | ||||
|         if (!tstate) { | ||||
|             tstate = PyThreadState_New(internals.istate); | ||||
|             #if !defined(NDEBUG) | ||||
|                 if (!tstate) | ||||
| #    if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|             if (!tstate) { | ||||
|                 pybind11_fail("scoped_acquire: could not create thread state!"); | ||||
|             } | ||||
| #    endif | ||||
|             tstate->gilstate_counter = 0; | ||||
|             PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); | ||||
|  | @ -81,26 +80,28 @@ public: | |||
|         inc_ref(); | ||||
|     } | ||||
| 
 | ||||
|     void inc_ref() { | ||||
|         ++tstate->gilstate_counter; | ||||
|     } | ||||
|     void inc_ref() { ++tstate->gilstate_counter; } | ||||
| 
 | ||||
|     PYBIND11_NOINLINE void dec_ref() { | ||||
|         --tstate->gilstate_counter; | ||||
|         #if !defined(NDEBUG) | ||||
|             if (detail::get_thread_state_unchecked() != tstate) | ||||
| #    if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|         if (detail::get_thread_state_unchecked() != tstate) { | ||||
|             pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); | ||||
|             if (tstate->gilstate_counter < 0) | ||||
|         } | ||||
|         if (tstate->gilstate_counter < 0) { | ||||
|             pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); | ||||
|         } | ||||
| #    endif | ||||
|         if (tstate->gilstate_counter == 0) { | ||||
|             #if !defined(NDEBUG) | ||||
|                 if (!release) | ||||
| #    if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|             if (!release) { | ||||
|                 pybind11_fail("scoped_acquire::dec_ref(): internal error!"); | ||||
|             } | ||||
| #    endif | ||||
|             PyThreadState_Clear(tstate); | ||||
|             if (active) | ||||
|             if (active) { | ||||
|                 PyThreadState_DeleteCurrent(); | ||||
|             } | ||||
|             PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); | ||||
|             release = false; | ||||
|         } | ||||
|  | @ -111,15 +112,15 @@ public: | |||
|     /// could be shutting down when this is called, as thread deletion is not
 | ||||
|     /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
 | ||||
|     /// protect subsequent code.
 | ||||
|     PYBIND11_NOINLINE void disarm() { | ||||
|         active = false; | ||||
|     } | ||||
|     PYBIND11_NOINLINE void disarm() { active = false; } | ||||
| 
 | ||||
|     PYBIND11_NOINLINE ~gil_scoped_acquire() { | ||||
|         dec_ref(); | ||||
|         if (release) | ||||
|         if (release) { | ||||
|             PyEval_SaveThread(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     PyThreadState *tstate = nullptr; | ||||
|     bool release = true; | ||||
|  | @ -133,8 +134,11 @@ public: | |||
|         // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
 | ||||
|         // initialization race could occur as multiple threads try `gil_scoped_acquire`.
 | ||||
|         auto &internals = detail::get_internals(); | ||||
|         // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
 | ||||
|         tstate = PyEval_SaveThread(); | ||||
|         if (disassoc) { | ||||
|             // Python >= 3.7 can remove this, it's an int before 3.7
 | ||||
|             // NOLINTNEXTLINE(readability-qualified-auto)
 | ||||
|             auto key = internals.tstate; | ||||
|             PYBIND11_TLS_DELETE_VALUE(key); | ||||
|         } | ||||
|  | @ -145,21 +149,24 @@ public: | |||
|     /// could be shutting down when this is called, as thread deletion is not
 | ||||
|     /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
 | ||||
|     /// protect subsequent code.
 | ||||
|     PYBIND11_NOINLINE void disarm() { | ||||
|         active = false; | ||||
|     } | ||||
|     PYBIND11_NOINLINE void disarm() { active = false; } | ||||
| 
 | ||||
|     ~gil_scoped_release() { | ||||
|         if (!tstate) | ||||
|         if (!tstate) { | ||||
|             return; | ||||
|         } | ||||
|         // `PyEval_RestoreThread()` should not be called if runtime is finalizing
 | ||||
|         if (active) | ||||
|         if (active) { | ||||
|             PyEval_RestoreThread(tstate); | ||||
|         } | ||||
|         if (disassoc) { | ||||
|             // Python >= 3.7 can remove this, it's an int before 3.7
 | ||||
|             // NOLINTNEXTLINE(readability-qualified-auto)
 | ||||
|             auto key = detail::get_internals().tstate; | ||||
|             PYBIND11_TLS_REPLACE_VALUE(key, tstate); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     PyThreadState *tstate; | ||||
|     bool disassoc; | ||||
|  | @ -168,6 +175,7 @@ private: | |||
| #elif defined(PYPY_VERSION) | ||||
| class gil_scoped_acquire { | ||||
|     PyGILState_STATE state; | ||||
| 
 | ||||
| public: | ||||
|     gil_scoped_acquire() { state = PyGILState_Ensure(); } | ||||
|     ~gil_scoped_acquire() { PyGILState_Release(state); } | ||||
|  | @ -176,6 +184,7 @@ public: | |||
| 
 | ||||
| class gil_scoped_release { | ||||
|     PyThreadState *state; | ||||
| 
 | ||||
| public: | ||||
|     gil_scoped_release() { state = PyEval_SaveThread(); } | ||||
|     ~gil_scoped_release() { PyEval_RestoreThread(state); } | ||||
|  |  | |||
|  | @ -58,36 +58,31 @@ private: | |||
|     size_t utf8_remainder() const { | ||||
|         const auto rbase = std::reverse_iterator<char *>(pbase()); | ||||
|         const auto rpptr = std::reverse_iterator<char *>(pptr()); | ||||
|         auto is_ascii = [](char c) { | ||||
|             return (static_cast<unsigned char>(c) & 0x80) == 0x00; | ||||
|         }; | ||||
|         auto is_leading = [](char c) { | ||||
|             return (static_cast<unsigned char>(c) & 0xC0) == 0xC0; | ||||
|         }; | ||||
|         auto is_leading_2b = [](char c) { | ||||
|             return static_cast<unsigned char>(c) <= 0xDF; | ||||
|         }; | ||||
|         auto is_leading_3b = [](char c) { | ||||
|             return static_cast<unsigned char>(c) <= 0xEF; | ||||
|         }; | ||||
|         auto is_ascii = [](char c) { return (static_cast<unsigned char>(c) & 0x80) == 0x00; }; | ||||
|         auto is_leading = [](char c) { return (static_cast<unsigned char>(c) & 0xC0) == 0xC0; }; | ||||
|         auto is_leading_2b = [](char c) { return static_cast<unsigned char>(c) <= 0xDF; }; | ||||
|         auto is_leading_3b = [](char c) { return static_cast<unsigned char>(c) <= 0xEF; }; | ||||
|         // If the last character is ASCII, there are no incomplete code points
 | ||||
|         if (is_ascii(*rpptr)) | ||||
|         if (is_ascii(*rpptr)) { | ||||
|             return 0; | ||||
|         } | ||||
|         // Otherwise, work back from the end of the buffer and find the first
 | ||||
|         // UTF-8 leading byte
 | ||||
|         const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase; | ||||
|         const auto leading = std::find_if(rpptr, rpend, is_leading); | ||||
|         if (leading == rbase) | ||||
|         if (leading == rbase) { | ||||
|             return 0; | ||||
|         } | ||||
|         const auto dist = static_cast<size_t>(leading - rpptr); | ||||
|         size_t remainder = 0; | ||||
| 
 | ||||
|         if (dist == 0) | ||||
|         if (dist == 0) { | ||||
|             remainder = 1; // 1-byte code point is impossible
 | ||||
|         else if (dist == 1) | ||||
|         } else if (dist == 1) { | ||||
|             remainder = is_leading_2b(*leading) ? 0 : dist + 1; | ||||
|         else if (dist == 2) | ||||
|         } else if (dist == 2) { | ||||
|             remainder = is_leading_3b(*leading) ? 0 : dist + 1; | ||||
|         } | ||||
|         // else if (dist >= 3), at least 4 bytes before encountering an UTF-8
 | ||||
|         // leading byte, either no remainder or invalid UTF-8.
 | ||||
|         // Invalid UTF-8 will cause an exception later when converting
 | ||||
|  | @ -105,22 +100,21 @@ private: | |||
| 
 | ||||
|             if (size > remainder) { | ||||
|                 str line(pbase(), size - remainder); | ||||
|                 pywrite(line); | ||||
|                 pywrite(std::move(line)); | ||||
|                 pyflush(); | ||||
|             } | ||||
| 
 | ||||
|             // Copy the remainder at the end of the buffer to the beginning:
 | ||||
|             if (remainder > 0) | ||||
|             if (remainder > 0) { | ||||
|                 std::memmove(pbase(), pptr() - remainder, remainder); | ||||
|             } | ||||
|             setp(pbase(), epptr()); | ||||
|             pbump(static_cast<int>(remainder)); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     int sync() override { | ||||
|         return _sync(); | ||||
|     } | ||||
|     int sync() override { return _sync(); } | ||||
| 
 | ||||
| public: | ||||
|     explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024) | ||||
|  | @ -132,14 +126,11 @@ public: | |||
|     pythonbuf(pythonbuf &&) = default; | ||||
| 
 | ||||
|     /// Sync before destroy
 | ||||
|     ~pythonbuf() override { | ||||
|         _sync(); | ||||
|     } | ||||
|     ~pythonbuf() override { _sync(); } | ||||
| }; | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| 
 | ||||
| 
 | ||||
| /** \rst
 | ||||
|     This a move-only guard that redirects output. | ||||
| 
 | ||||
|  | @ -160,7 +151,8 @@ PYBIND11_NAMESPACE_END(detail) | |||
|     .. code-block:: cpp | ||||
| 
 | ||||
|         { | ||||
|             py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; | ||||
|             py::scoped_ostream_redirect output{ | ||||
|                 std::cerr, py::module::import("sys").attr("stderr")}; | ||||
|             std::cout << "Hello, World!"; | ||||
|         } | ||||
|  \endrst */ | ||||
|  | @ -178,9 +170,7 @@ public: | |||
|         old = costream.rdbuf(&buffer); | ||||
|     } | ||||
| 
 | ||||
|     ~scoped_ostream_redirect() { | ||||
|         costream.rdbuf(old); | ||||
|     } | ||||
|     ~scoped_ostream_redirect() { costream.rdbuf(old); } | ||||
| 
 | ||||
|     scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; | ||||
|     scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; | ||||
|  | @ -188,7 +178,6 @@ public: | |||
|     scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /** \rst
 | ||||
|     Like `scoped_ostream_redirect`, but redirects cerr by default. This class | ||||
|     is provided primary to make ``py::call_guard`` easier to make. | ||||
|  | @ -208,7 +197,6 @@ public: | |||
|         : scoped_ostream_redirect(costream, pyostream) {} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| // Class to redirect output as a context manager. C++ backend.
 | ||||
|  | @ -223,11 +211,13 @@ public: | |||
|         : do_stdout_(do_stdout), do_stderr_(do_stderr) {} | ||||
| 
 | ||||
|     void enter() { | ||||
|         if (do_stdout_) | ||||
|         if (do_stdout_) { | ||||
|             redirect_stdout.reset(new scoped_ostream_redirect()); | ||||
|         if (do_stderr_) | ||||
|         } | ||||
|         if (do_stderr_) { | ||||
|             redirect_stderr.reset(new scoped_estream_redirect()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void exit() { | ||||
|         redirect_stdout.reset(); | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -16,12 +16,50 @@ PYBIND11_NAMESPACE_BEGIN(detail) | |||
| 
 | ||||
| /// Enumeration with all supported operator types
 | ||||
| enum op_id : int { | ||||
|     op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, | ||||
|     op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, | ||||
|     op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, | ||||
|     op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, | ||||
|     op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, | ||||
|     op_repr, op_truediv, op_itruediv, op_hash | ||||
|     op_add, | ||||
|     op_sub, | ||||
|     op_mul, | ||||
|     op_div, | ||||
|     op_mod, | ||||
|     op_divmod, | ||||
|     op_pow, | ||||
|     op_lshift, | ||||
|     op_rshift, | ||||
|     op_and, | ||||
|     op_xor, | ||||
|     op_or, | ||||
|     op_neg, | ||||
|     op_pos, | ||||
|     op_abs, | ||||
|     op_invert, | ||||
|     op_int, | ||||
|     op_long, | ||||
|     op_float, | ||||
|     op_str, | ||||
|     op_cmp, | ||||
|     op_gt, | ||||
|     op_ge, | ||||
|     op_lt, | ||||
|     op_le, | ||||
|     op_eq, | ||||
|     op_ne, | ||||
|     op_iadd, | ||||
|     op_isub, | ||||
|     op_imul, | ||||
|     op_idiv, | ||||
|     op_imod, | ||||
|     op_ilshift, | ||||
|     op_irshift, | ||||
|     op_iand, | ||||
|     op_ixor, | ||||
|     op_ior, | ||||
|     op_complex, | ||||
|     op_bool, | ||||
|     op_nonzero, | ||||
|     op_repr, | ||||
|     op_truediv, | ||||
|     op_itruediv, | ||||
|     op_hash | ||||
| }; | ||||
| 
 | ||||
| enum op_type : int { | ||||
|  | @ -40,44 +78,39 @@ struct undefined_t { }; | |||
| inline self_t __self() { return self; } | ||||
| 
 | ||||
| /// base template of operator implementations
 | ||||
| template <op_id, op_type, typename B, typename L, typename R> struct op_impl { }; | ||||
| template <op_id, op_type, typename B, typename L, typename R> | ||||
| struct op_impl {}; | ||||
| 
 | ||||
| /// Operator implementation generator
 | ||||
| template <op_id id, op_type ot, typename L, typename R> struct op_ { | ||||
|     template <typename Class, typename... Extra> void execute(Class &cl, const Extra&... extra) const { | ||||
| template <op_id id, op_type ot, typename L, typename R> | ||||
| struct op_ { | ||||
|     template <typename Class, typename... Extra> | ||||
|     void execute(Class &cl, const Extra &...extra) const { | ||||
|         using Base = typename Class::type; | ||||
|         using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>; | ||||
|         using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>; | ||||
|         using op = op_impl<id, ot, Base, L_type, R_type>; | ||||
|         cl.def(op::name(), &op::execute, is_operator(), extra...); | ||||
|         #if PY_MAJOR_VERSION < 3 | ||||
|         if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) || | ||||
|             PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv)) | ||||
|             cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", | ||||
|                     &op::execute, is_operator(), extra...); | ||||
|         #endif | ||||
|     } | ||||
|     template <typename Class, typename... Extra> void execute_cast(Class &cl, const Extra&... extra) const { | ||||
|     template <typename Class, typename... Extra> | ||||
|     void execute_cast(Class &cl, const Extra &...extra) const { | ||||
|         using Base = typename Class::type; | ||||
|         using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>; | ||||
|         using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>; | ||||
|         using op = op_impl<id, ot, Base, L_type, R_type>; | ||||
|         cl.def(op::name(), &op::execute_cast, is_operator(), extra...); | ||||
|         #if PY_MAJOR_VERSION < 3 | ||||
|         if (id == op_truediv || id == op_itruediv) | ||||
|             cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", | ||||
|                     &op::execute, is_operator(), extra...); | ||||
|         #endif | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| #define PYBIND11_BINARY_OPERATOR(id, rid, op, expr)                                               \ | ||||
| template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \ | ||||
|     template <typename B, typename L, typename R>                                                 \ | ||||
|     struct op_impl<op_##id, op_l, B, L, R> {                                                      \ | ||||
|         static char const *name() { return "__" #id "__"; }                                       \ | ||||
|         static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); }          \ | ||||
|         static B execute_cast(const L &l, const R &r) { return B(expr); }                         \ | ||||
|     };                                                                                            \ | ||||
| template <typename B, typename L, typename R> struct op_impl<op_##id, op_r, B, L, R> { \ | ||||
|     template <typename B, typename L, typename R>                                                 \ | ||||
|     struct op_impl<op_##id, op_r, B, L, R> {                                                      \ | ||||
|         static char const *name() { return "__" #rid "__"; }                                      \ | ||||
|         static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); }          \ | ||||
|         static B execute_cast(const R &r, const L &l) { return B(expr); }                         \ | ||||
|  | @ -85,25 +118,30 @@ template <typename B, typename L, typename R> struct op_impl<op_##id, op_r, B, L | |||
|     inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) {                \ | ||||
|         return op_<op_##id, op_l, self_t, self_t>();                                              \ | ||||
|     }                                                                                             \ | ||||
| template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) {    \ | ||||
|     template <typename T>                                                                         \ | ||||
|     op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) {                                 \ | ||||
|         return op_<op_##id, op_l, self_t, T>();                                                   \ | ||||
|     }                                                                                             \ | ||||
| template <typename T> op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) {    \ | ||||
|     template <typename T>                                                                         \ | ||||
|     op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) {                                 \ | ||||
|         return op_<op_##id, op_r, T, self_t>();                                                   \ | ||||
|     } | ||||
| 
 | ||||
| #define PYBIND11_INPLACE_OPERATOR(id, op, expr)                                                   \ | ||||
| template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \ | ||||
|     template <typename B, typename L, typename R>                                                 \ | ||||
|     struct op_impl<op_##id, op_l, B, L, R> {                                                      \ | ||||
|         static char const *name() { return "__" #id "__"; }                                       \ | ||||
|         static auto execute(L &l, const R &r) -> decltype(expr) { return expr; }                  \ | ||||
|         static B execute_cast(L &l, const R &r) { return B(expr); }                               \ | ||||
|     };                                                                                            \ | ||||
| template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) {    \ | ||||
|     template <typename T>                                                                         \ | ||||
|     op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) {                                 \ | ||||
|         return op_<op_##id, op_l, self_t, T>();                                                   \ | ||||
|     } | ||||
| 
 | ||||
| #define PYBIND11_UNARY_OPERATOR(id, op, expr)                                                     \ | ||||
| template <typename B, typename L> struct op_impl<op_##id, op_u, B, L, undefined_t> {   \ | ||||
|     template <typename B, typename L>                                                             \ | ||||
|     struct op_impl<op_##id, op_u, B, L, undefined_t> {                                            \ | ||||
|         static char const *name() { return "__" #id "__"; }                                       \ | ||||
|         static auto execute(const L &l) -> decltype(expr) { return expr; }                        \ | ||||
|         static B execute_cast(const L &l) { return B(expr); }                                     \ | ||||
|  |  | |||
|  | @ -15,7 +15,6 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | |||
| 
 | ||||
| class options { | ||||
| public: | ||||
| 
 | ||||
|     // Default RAII constructor, which leaves settings as they currently are.
 | ||||
|     options() : previous_state(global_state()) {} | ||||
| 
 | ||||
|  | @ -24,23 +23,35 @@ public: | |||
|     options &operator=(const options &) = delete; | ||||
| 
 | ||||
|     // Destructor, which restores settings that were in effect before.
 | ||||
|     ~options() { | ||||
|         global_state() = previous_state; | ||||
|     } | ||||
|     ~options() { global_state() = previous_state; } | ||||
| 
 | ||||
|     // Setter methods (affect the global state):
 | ||||
| 
 | ||||
|     options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } | ||||
|     options &disable_user_defined_docstrings() & { | ||||
|         global_state().show_user_defined_docstrings = false; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } | ||||
|     options &enable_user_defined_docstrings() & { | ||||
|         global_state().show_user_defined_docstrings = true; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } | ||||
|     options &disable_function_signatures() & { | ||||
|         global_state().show_function_signatures = false; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } | ||||
|     options &enable_function_signatures() & { | ||||
|         global_state().show_function_signatures = true; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     // Getter methods (return the global state):
 | ||||
| 
 | ||||
|     static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } | ||||
|     static bool show_user_defined_docstrings() { | ||||
|         return global_state().show_user_defined_docstrings; | ||||
|     } | ||||
| 
 | ||||
|     static bool show_function_signatures() { return global_state().show_function_signatures; } | ||||
| 
 | ||||
|  | @ -48,10 +59,10 @@ public: | |||
|     void *operator new(size_t) = delete; | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     struct state { | ||||
|         bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
 | ||||
|         bool show_function_signatures = true;      //< Include auto-generated function signatures in docstrings.
 | ||||
|         bool show_function_signatures = true;     //< Include auto-generated function signatures
 | ||||
|                                                   //  in docstrings.
 | ||||
|     }; | ||||
| 
 | ||||
|     static state &global_state() { | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -9,15 +9,16 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "detail/common.h" | ||||
| #include "pybind11.h" | ||||
| #include <set> | ||||
| #include <unordered_set> | ||||
| #include <map> | ||||
| #include <unordered_map> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| #include "detail/common.h" | ||||
| 
 | ||||
| #include <deque> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <ostream> | ||||
| #include <set> | ||||
| #include <unordered_map> | ||||
| #include <unordered_set> | ||||
| #include <valarray> | ||||
| 
 | ||||
| // See `detail/common.h` for implementation of these guards.
 | ||||
|  | @ -37,8 +38,9 @@ PYBIND11_NAMESPACE_BEGIN(detail) | |||
| /// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
 | ||||
| /// forwarding a container element).  Typically used indirect via forwarded_type(), below.
 | ||||
| template <typename T, typename U> | ||||
| using forwarded_type = conditional_t< | ||||
|     std::is_lvalue_reference<T>::value, remove_reference_t<U> &, remove_reference_t<U> &&>; | ||||
| using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value, | ||||
|                                      remove_reference_t<U> &, | ||||
|                                      remove_reference_t<U> &&>; | ||||
| 
 | ||||
| /// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically
 | ||||
| /// used for forwarding a container's elements.
 | ||||
|  | @ -47,19 +49,22 @@ forwarded_type<T, U> forward_like(U &&u) { | |||
|     return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u)); | ||||
| } | ||||
| 
 | ||||
| template <typename Type, typename Key> struct set_caster { | ||||
| template <typename Type, typename Key> | ||||
| struct set_caster { | ||||
|     using type = Type; | ||||
|     using key_conv = make_caster<Key>; | ||||
| 
 | ||||
|     bool load(handle src, bool convert) { | ||||
|         if (!isinstance<pybind11::set>(src)) | ||||
|         if (!isinstance<anyset>(src)) { | ||||
|             return false; | ||||
|         auto s = reinterpret_borrow<pybind11::set>(src); | ||||
|         } | ||||
|         auto s = reinterpret_borrow<anyset>(src); | ||||
|         value.clear(); | ||||
|         for (auto entry : s) { | ||||
|             key_conv conv; | ||||
|             if (!conv.load(entry, convert)) | ||||
|             if (!conv.load(entry, convert)) { | ||||
|                 return false; | ||||
|             } | ||||
|             value.insert(cast_op<Key &&>(std::move(conv))); | ||||
|         } | ||||
|         return true; | ||||
|  | @ -67,35 +72,40 @@ template <typename Type, typename Key> struct set_caster { | |||
| 
 | ||||
|     template <typename T> | ||||
|     static handle cast(T &&src, return_value_policy policy, handle parent) { | ||||
|         if (!std::is_lvalue_reference<T>::value) | ||||
|         if (!std::is_lvalue_reference<T>::value) { | ||||
|             policy = return_value_policy_override<Key>::policy(policy); | ||||
|         } | ||||
|         pybind11::set s; | ||||
|         for (auto &&value : src) { | ||||
|             auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent)); | ||||
|             if (!value_ || !s.add(value_)) | ||||
|             auto value_ = reinterpret_steal<object>( | ||||
|                 key_conv::cast(forward_like<T>(value), policy, parent)); | ||||
|             if (!value_ || !s.add(std::move(value_))) { | ||||
|                 return handle(); | ||||
|             } | ||||
|         } | ||||
|         return s.release(); | ||||
|     } | ||||
| 
 | ||||
|     PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]")); | ||||
| }; | ||||
| 
 | ||||
| template <typename Type, typename Key, typename Value> struct map_caster { | ||||
| template <typename Type, typename Key, typename Value> | ||||
| struct map_caster { | ||||
|     using key_conv = make_caster<Key>; | ||||
|     using value_conv = make_caster<Value>; | ||||
| 
 | ||||
|     bool load(handle src, bool convert) { | ||||
|         if (!isinstance<dict>(src)) | ||||
|         if (!isinstance<dict>(src)) { | ||||
|             return false; | ||||
|         } | ||||
|         auto d = reinterpret_borrow<dict>(src); | ||||
|         value.clear(); | ||||
|         for (auto it : d) { | ||||
|             key_conv kconv; | ||||
|             value_conv vconv; | ||||
|             if (!kconv.load(it.first.ptr(), convert) || | ||||
|                 !vconv.load(it.second.ptr(), convert)) | ||||
|             if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) { | ||||
|                 return false; | ||||
|             } | ||||
|             value.emplace(cast_op<Key &&>(std::move(kconv)), cast_op<Value &&>(std::move(vconv))); | ||||
|         } | ||||
|         return true; | ||||
|  | @ -111,31 +121,39 @@ template <typename Type, typename Key, typename Value> struct map_caster { | |||
|             policy_value = return_value_policy_override<Value>::policy(policy_value); | ||||
|         } | ||||
|         for (auto &&kv : src) { | ||||
|             auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy_key, parent)); | ||||
|             auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy_value, parent)); | ||||
|             if (!key || !value) | ||||
|             auto key = reinterpret_steal<object>( | ||||
|                 key_conv::cast(forward_like<T>(kv.first), policy_key, parent)); | ||||
|             auto value = reinterpret_steal<object>( | ||||
|                 value_conv::cast(forward_like<T>(kv.second), policy_value, parent)); | ||||
|             if (!key || !value) { | ||||
|                 return handle(); | ||||
|             d[key] = value; | ||||
|             } | ||||
|             d[std::move(key)] = std::move(value); | ||||
|         } | ||||
|         return d.release(); | ||||
|     } | ||||
| 
 | ||||
|     PYBIND11_TYPE_CASTER(Type, const_name("Dict[") + key_conv::name + const_name(", ") + value_conv::name + const_name("]")); | ||||
|     PYBIND11_TYPE_CASTER(Type, | ||||
|                          const_name("Dict[") + key_conv::name + const_name(", ") + value_conv::name | ||||
|                              + const_name("]")); | ||||
| }; | ||||
| 
 | ||||
| template <typename Type, typename Value> struct list_caster { | ||||
| template <typename Type, typename Value> | ||||
| struct list_caster { | ||||
|     using value_conv = make_caster<Value>; | ||||
| 
 | ||||
|     bool load(handle src, bool convert) { | ||||
|         if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) | ||||
|         if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) { | ||||
|             return false; | ||||
|         } | ||||
|         auto s = reinterpret_borrow<sequence>(src); | ||||
|         value.clear(); | ||||
|         reserve_maybe(s, &value); | ||||
|         for (auto it : s) { | ||||
|             value_conv conv; | ||||
|             if (!conv.load(it, convert)) | ||||
|             if (!conv.load(it, convert)) { | ||||
|                 return false; | ||||
|             } | ||||
|             value.push_back(cast_op<Value &&>(std::move(conv))); | ||||
|         } | ||||
|         return true; | ||||
|  | @ -153,14 +171,17 @@ private: | |||
| public: | ||||
|     template <typename T> | ||||
|     static handle cast(T &&src, return_value_policy policy, handle parent) { | ||||
|         if (!std::is_lvalue_reference<T>::value) | ||||
|         if (!std::is_lvalue_reference<T>::value) { | ||||
|             policy = return_value_policy_override<Value>::policy(policy); | ||||
|         } | ||||
|         list l(src.size()); | ||||
|         ssize_t index = 0; | ||||
|         for (auto &&value : src) { | ||||
|             auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); | ||||
|             if (!value_) | ||||
|             auto value_ = reinterpret_steal<object>( | ||||
|                 value_conv::cast(forward_like<T>(value), policy, parent)); | ||||
|             if (!value_) { | ||||
|                 return handle(); | ||||
|             } | ||||
|             PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
 | ||||
|         } | ||||
|         return l.release(); | ||||
|  | @ -169,23 +190,25 @@ public: | |||
|     PYBIND11_TYPE_CASTER(Type, const_name("List[") + value_conv::name + const_name("]")); | ||||
| }; | ||||
| 
 | ||||
| template <typename Type, typename Alloc> struct type_caster<std::vector<Type, Alloc>> | ||||
|  : list_caster<std::vector<Type, Alloc>, Type> { }; | ||||
| template <typename Type, typename Alloc> | ||||
| struct type_caster<std::vector<Type, Alloc>> : list_caster<std::vector<Type, Alloc>, Type> {}; | ||||
| 
 | ||||
| template <typename Type, typename Alloc> struct type_caster<std::deque<Type, Alloc>> | ||||
|  : list_caster<std::deque<Type, Alloc>, Type> { }; | ||||
| template <typename Type, typename Alloc> | ||||
| struct type_caster<std::deque<Type, Alloc>> : list_caster<std::deque<Type, Alloc>, Type> {}; | ||||
| 
 | ||||
| template <typename Type, typename Alloc> struct type_caster<std::list<Type, Alloc>> | ||||
|  : list_caster<std::list<Type, Alloc>, Type> { }; | ||||
| template <typename Type, typename Alloc> | ||||
| struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {}; | ||||
| 
 | ||||
| template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> struct array_caster { | ||||
| template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> | ||||
| struct array_caster { | ||||
|     using value_conv = make_caster<Value>; | ||||
| 
 | ||||
| private: | ||||
|     template <bool R = Resizable> | ||||
|     bool require_size(enable_if_t<R, size_t> size) { | ||||
|         if (value.size() != size) | ||||
|         if (value.size() != size) { | ||||
|             value.resize(size); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|     template <bool R = Resizable> | ||||
|  | @ -195,16 +218,19 @@ private: | |||
| 
 | ||||
| public: | ||||
|     bool load(handle src, bool convert) { | ||||
|         if (!isinstance<sequence>(src)) | ||||
|         if (!isinstance<sequence>(src)) { | ||||
|             return false; | ||||
|         } | ||||
|         auto l = reinterpret_borrow<sequence>(src); | ||||
|         if (!require_size(l.size())) | ||||
|         if (!require_size(l.size())) { | ||||
|             return false; | ||||
|         } | ||||
|         size_t ctr = 0; | ||||
|         for (auto it : l) { | ||||
|             value_conv conv; | ||||
|             if (!conv.load(it, convert)) | ||||
|             if (!conv.load(it, convert)) { | ||||
|                 return false; | ||||
|             } | ||||
|             value[ctr++] = cast_op<Value &&>(std::move(conv)); | ||||
|         } | ||||
|         return true; | ||||
|  | @ -215,43 +241,57 @@ public: | |||
|         list l(src.size()); | ||||
|         ssize_t index = 0; | ||||
|         for (auto &&value : src) { | ||||
|             auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); | ||||
|             if (!value_) | ||||
|             auto value_ = reinterpret_steal<object>( | ||||
|                 value_conv::cast(forward_like<T>(value), policy, parent)); | ||||
|             if (!value_) { | ||||
|                 return handle(); | ||||
|             } | ||||
|             PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
 | ||||
|         } | ||||
|         return l.release(); | ||||
|     } | ||||
| 
 | ||||
|     PYBIND11_TYPE_CASTER(ArrayType, const_name("List[") + value_conv::name + const_name<Resizable>(const_name(""), const_name("[") + const_name<Size>() + const_name("]")) + const_name("]")); | ||||
|     PYBIND11_TYPE_CASTER(ArrayType, | ||||
|                          const_name("List[") + value_conv::name | ||||
|                              + const_name<Resizable>(const_name(""), | ||||
|                                                      const_name("[") + const_name<Size>() | ||||
|                                                          + const_name("]")) | ||||
|                              + const_name("]")); | ||||
| }; | ||||
| 
 | ||||
| template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>> | ||||
| template <typename Type, size_t Size> | ||||
| struct type_caster<std::array<Type, Size>> | ||||
|     : array_caster<std::array<Type, Size>, Type, false, Size> {}; | ||||
| 
 | ||||
| template <typename Type> struct type_caster<std::valarray<Type>> | ||||
|  : array_caster<std::valarray<Type>, Type, true> { }; | ||||
| template <typename Type> | ||||
| struct type_caster<std::valarray<Type>> : array_caster<std::valarray<Type>, Type, true> {}; | ||||
| 
 | ||||
| template <typename Key, typename Compare, typename Alloc> struct type_caster<std::set<Key, Compare, Alloc>> | ||||
| template <typename Key, typename Compare, typename Alloc> | ||||
| struct type_caster<std::set<Key, Compare, Alloc>> | ||||
|     : set_caster<std::set<Key, Compare, Alloc>, Key> {}; | ||||
| 
 | ||||
| template <typename Key, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>> | ||||
| template <typename Key, typename Hash, typename Equal, typename Alloc> | ||||
| struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>> | ||||
|     : set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> {}; | ||||
| 
 | ||||
| template <typename Key, typename Value, typename Compare, typename Alloc> struct type_caster<std::map<Key, Value, Compare, Alloc>> | ||||
| template <typename Key, typename Value, typename Compare, typename Alloc> | ||||
| struct type_caster<std::map<Key, Value, Compare, Alloc>> | ||||
|     : map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> {}; | ||||
| 
 | ||||
| template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>> | ||||
| template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc> | ||||
| struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>> | ||||
|     : map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> {}; | ||||
| 
 | ||||
| // This type caster is intended to be used for std::optional and std::experimental::optional
 | ||||
| template<typename Type, typename Value = typename Type::value_type> struct optional_caster { | ||||
| template <typename Type, typename Value = typename Type::value_type> | ||||
| struct optional_caster { | ||||
|     using value_conv = make_caster<Value>; | ||||
| 
 | ||||
|     template <typename T> | ||||
|     static handle cast(T &&src, return_value_policy policy, handle parent) { | ||||
|         if (!src) | ||||
|         if (!src) { | ||||
|             return none().inc_ref(); | ||||
|         } | ||||
|         if (!std::is_lvalue_reference<T>::value) { | ||||
|             policy = return_value_policy_override<Value>::policy(policy); | ||||
|         } | ||||
|  | @ -266,8 +306,9 @@ template<typename Type, typename Value = typename Type::value_type> struct optio | |||
|             return true; // default-constructed value is already empty
 | ||||
|         } | ||||
|         value_conv inner_caster; | ||||
|         if (!inner_caster.load(src, convert)) | ||||
|         if (!inner_caster.load(src, convert)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         value.emplace(cast_op<Value &&>(std::move(inner_caster))); | ||||
|         return true; | ||||
|  | @ -277,18 +318,20 @@ template<typename Type, typename Value = typename Type::value_type> struct optio | |||
| }; | ||||
| 
 | ||||
| #if defined(PYBIND11_HAS_OPTIONAL) | ||||
| template<typename T> struct type_caster<std::optional<T>> | ||||
|     : public optional_caster<std::optional<T>> {}; | ||||
| template <typename T> | ||||
| struct type_caster<std::optional<T>> : public optional_caster<std::optional<T>> {}; | ||||
| 
 | ||||
| template<> struct type_caster<std::nullopt_t> | ||||
|     : public void_caster<std::nullopt_t> {}; | ||||
| template <> | ||||
| struct type_caster<std::nullopt_t> : public void_caster<std::nullopt_t> {}; | ||||
| #endif | ||||
| 
 | ||||
| #if defined(PYBIND11_HAS_EXP_OPTIONAL) | ||||
| template<typename T> struct type_caster<std::experimental::optional<T>> | ||||
| template <typename T> | ||||
| struct type_caster<std::experimental::optional<T>> | ||||
|     : public optional_caster<std::experimental::optional<T>> {}; | ||||
| 
 | ||||
| template<> struct type_caster<std::experimental::nullopt_t> | ||||
| template <> | ||||
| struct type_caster<std::experimental::nullopt_t> | ||||
|     : public void_caster<std::experimental::nullopt_t> {}; | ||||
| #endif | ||||
| 
 | ||||
|  | @ -318,7 +361,8 @@ struct visit_helper { | |||
| }; | ||||
| 
 | ||||
| /// Generic variant caster
 | ||||
| template <typename Variant> struct variant_caster; | ||||
| template <typename Variant> | ||||
| struct variant_caster; | ||||
| 
 | ||||
| template <template <typename...> class V, typename... Ts> | ||||
| struct variant_caster<V<Ts...>> { | ||||
|  | @ -328,7 +372,7 @@ struct variant_caster<V<Ts...>> { | |||
|     bool load_alternative(handle src, bool convert, type_list<U, Us...>) { | ||||
|         auto caster = make_caster<U>(); | ||||
|         if (caster.load(src, convert)) { | ||||
|             value = cast_op<U>(caster); | ||||
|             value = cast_op<U>(std::move(caster)); | ||||
|             return true; | ||||
|         } | ||||
|         return load_alternative(src, convert, type_list<Us...>{}); | ||||
|  | @ -341,8 +385,9 @@ struct variant_caster<V<Ts...>> { | |||
|         // E.g. `py::int_(1).cast<variant<double, int>>()` needs to fill the `int`
 | ||||
|         // slot of the variant. Without two-pass loading `double` would be filled
 | ||||
|         // because it appears first and a conversion is possible.
 | ||||
|         if (convert && load_alternative(src, false, type_list<Ts...>{})) | ||||
|         if (convert && load_alternative(src, false, type_list<Ts...>{})) { | ||||
|             return true; | ||||
|         } | ||||
|         return load_alternative(src, convert, type_list<Ts...>{}); | ||||
|     } | ||||
| 
 | ||||
|  | @ -353,12 +398,17 @@ struct variant_caster<V<Ts...>> { | |||
|     } | ||||
| 
 | ||||
|     using Type = V<Ts...>; | ||||
|     PYBIND11_TYPE_CASTER(Type, const_name("Union[") + detail::concat(make_caster<Ts>::name...) + const_name("]")); | ||||
|     PYBIND11_TYPE_CASTER(Type, | ||||
|                          const_name("Union[") + detail::concat(make_caster<Ts>::name...) | ||||
|                              + const_name("]")); | ||||
| }; | ||||
| 
 | ||||
| #if defined(PYBIND11_HAS_VARIANT) | ||||
| template <typename... Ts> | ||||
| struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {}; | ||||
| 
 | ||||
| template <> | ||||
| struct type_caster<std::monostate> : public void_caster<std::monostate> {}; | ||||
| #endif | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
|  |  | |||
|  | @ -4,33 +4,39 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "../cast.h" | ||||
| #include "../pybind11.h" | ||||
| #include "../pytypes.h" | ||||
| 
 | ||||
| #include "../detail/common.h" | ||||
| #include "../detail/descr.h" | ||||
| #include "../cast.h" | ||||
| #include "../pytypes.h" | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #ifdef __has_include | ||||
| #  if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \ | ||||
| #    if defined(PYBIND11_CPP17) | ||||
| #        if __has_include(<filesystem>) && \ | ||||
|           PY_VERSION_HEX >= 0x03060000 | ||||
| #            include <filesystem> | ||||
| #            define PYBIND11_HAS_FILESYSTEM 1 | ||||
| #        elif __has_include(<experimental/filesystem>) | ||||
| #            include <experimental/filesystem> | ||||
| #            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 <filesystem> is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)" | ||||
|         "Neither #include <filesystem> nor #include <experimental/filesystem is available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)" | ||||
| #endif | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| #if defined(PYBIND11_HAS_FILESYSTEM) | ||||
| template<typename T> struct path_caster { | ||||
| #if defined(PYBIND11_HAS_FILESYSTEM) || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) | ||||
| template <typename T> | ||||
| struct path_caster { | ||||
| 
 | ||||
| private: | ||||
|     static PyObject *unicode_from_fs_native(const std::string &w) { | ||||
|  | @ -38,8 +44,7 @@ private: | |||
|         return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size())); | ||||
| #    else | ||||
|         // PyPy mistakenly declares the first parameter as non-const.
 | ||||
|         return PyUnicode_DecodeFSDefaultAndSize( | ||||
|             const_cast<char*>(w.c_str()), ssize_t(w.size())); | ||||
|         return PyUnicode_DecodeFSDefaultAndSize(const_cast<char *>(w.c_str()), ssize_t(w.size())); | ||||
| #    endif | ||||
|     } | ||||
| 
 | ||||
|  | @ -50,7 +55,8 @@ private: | |||
| public: | ||||
|     static handle cast(const T &path, return_value_policy, handle) { | ||||
|         if (auto py_str = unicode_from_fs_native(path.native())) { | ||||
|             return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str)) | ||||
|             return module_::import("pathlib") | ||||
|                 .attr("Path")(reinterpret_steal<object>(py_str)) | ||||
|                 .release(); | ||||
|         } | ||||
|         return nullptr; | ||||
|  | @ -68,7 +74,7 @@ public: | |||
|         PyObject *native = nullptr; | ||||
|         if constexpr (std::is_same_v<typename T::value_type, char>) { | ||||
|             if (PyUnicode_FSConverter(buf, &native) != 0) { | ||||
|                 if (auto c_str = PyBytes_AsString(native)) { | ||||
|                 if (auto *c_str = PyBytes_AsString(native)) { | ||||
|                     // AsString returns a pointer to the internal buffer, which
 | ||||
|                     // must not be free'd.
 | ||||
|                     value = c_str; | ||||
|  | @ -76,7 +82,7 @@ public: | |||
|             } | ||||
|         } else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) { | ||||
|             if (PyUnicode_FSDecoder(buf, &native) != 0) { | ||||
|                 if (auto c_str = PyUnicode_AsWideCharString(native, nullptr)) { | ||||
|                 if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) { | ||||
|                     // AsWideCharString returns a new string that must be free'd.
 | ||||
|                     value = c_str; // Copies the string.
 | ||||
|                     PyMem_Free(c_str); | ||||
|  | @ -95,9 +101,16 @@ public: | |||
|     PYBIND11_TYPE_CASTER(T, const_name("os.PathLike")); | ||||
| }; | ||||
| 
 | ||||
| template<> struct type_caster<std::filesystem::path> | ||||
|     : public path_caster<std::filesystem::path> {}; | ||||
| #endif // PYBIND11_HAS_FILESYSTEM
 | ||||
| #endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
 | ||||
| 
 | ||||
| #if defined(PYBIND11_HAS_FILESYSTEM) | ||||
| template <> | ||||
| struct type_caster<std::filesystem::path> : public path_caster<std::filesystem::path> {}; | ||||
| #elif defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) | ||||
| template <> | ||||
| struct type_caster<std::experimental::filesystem::path> | ||||
|     : public path_caster<std::experimental::filesystem::path> {}; | ||||
| #endif | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) | ||||
|  |  | |||
|  | @ -19,17 +19,28 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | |||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| /* SFINAE helper class used by 'is_comparable */ | ||||
| template <typename T>  struct container_traits { | ||||
|     template <typename T2> static std::true_type test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>())*); | ||||
|     template <typename T2> static std::false_type test_comparable(...); | ||||
|     template <typename T2> static std::true_type test_value(typename T2::value_type *); | ||||
|     template <typename T2> static std::false_type test_value(...); | ||||
|     template <typename T2> static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); | ||||
|     template <typename T2> static std::false_type test_pair(...); | ||||
| template <typename T> | ||||
| struct container_traits { | ||||
|     template <typename T2> | ||||
|     static std::true_type | ||||
|     test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>()) *); | ||||
|     template <typename T2> | ||||
|     static std::false_type test_comparable(...); | ||||
|     template <typename T2> | ||||
|     static std::true_type test_value(typename T2::value_type *); | ||||
|     template <typename T2> | ||||
|     static std::false_type test_value(...); | ||||
|     template <typename T2> | ||||
|     static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); | ||||
|     template <typename T2> | ||||
|     static std::false_type test_pair(...); | ||||
| 
 | ||||
|     static constexpr const bool is_comparable = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value; | ||||
|     static constexpr const bool is_pair = std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value; | ||||
|     static constexpr const bool is_vector = std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value; | ||||
|     static constexpr const bool is_comparable | ||||
|         = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value; | ||||
|     static constexpr const bool is_pair | ||||
|         = std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value; | ||||
|     static constexpr const bool is_vector | ||||
|         = std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value; | ||||
|     static constexpr const bool is_element = !is_pair && !is_vector; | ||||
| }; | ||||
| 
 | ||||
|  | @ -40,30 +51,33 @@ struct is_comparable : std::false_type { }; | |||
| /* For non-map data structures, check whether operator== can be instantiated */ | ||||
| template <typename T> | ||||
| struct is_comparable< | ||||
|     T, enable_if_t<container_traits<T>::is_element && | ||||
|                    container_traits<T>::is_comparable>> | ||||
|     T, | ||||
|     enable_if_t<container_traits<T>::is_element && container_traits<T>::is_comparable>> | ||||
|     : std::true_type {}; | ||||
| 
 | ||||
| /* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ | ||||
| /* For a vector/map data structure, recursively check the value type
 | ||||
|    (which is std::pair for maps) */ | ||||
| template <typename T> | ||||
| struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> { | ||||
|     static constexpr const bool value = | ||||
|         is_comparable<typename T::value_type>::value; | ||||
|     static constexpr const bool value = is_comparable<typename T::value_type>::value; | ||||
| }; | ||||
| 
 | ||||
| /* For pairs, recursively check the two data types */ | ||||
| template <typename T> | ||||
| struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> { | ||||
|     static constexpr const bool value = | ||||
|         is_comparable<typename T::first_type>::value && | ||||
|         is_comparable<typename T::second_type>::value; | ||||
|     static constexpr const bool value = is_comparable<typename T::first_type>::value | ||||
|                                         && is_comparable<typename T::second_type>::value; | ||||
| }; | ||||
| 
 | ||||
| /* Fallback functions */ | ||||
| template <typename, typename, typename... Args> void vector_if_copy_constructible(const Args &...) { } | ||||
| template <typename, typename, typename... Args> void vector_if_equal_operator(const Args &...) { } | ||||
| template <typename, typename, typename... Args> void vector_if_insertion_operator(const Args &...) { } | ||||
| template <typename, typename, typename... Args> void vector_modifiers(const Args &...) { } | ||||
| template <typename, typename, typename... Args> | ||||
| void vector_if_copy_constructible(const Args &...) {} | ||||
| template <typename, typename, typename... Args> | ||||
| void vector_if_equal_operator(const Args &...) {} | ||||
| template <typename, typename, typename... Args> | ||||
| void vector_if_insertion_operator(const Args &...) {} | ||||
| template <typename, typename, typename... Args> | ||||
| void vector_modifiers(const Args &...) {} | ||||
| 
 | ||||
| template <typename Vector, typename Class_> | ||||
| void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) { | ||||
|  | @ -77,53 +91,55 @@ void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> | |||
|     cl.def(self == self); | ||||
|     cl.def(self != self); | ||||
| 
 | ||||
|     cl.def("count", | ||||
|         [](const Vector &v, const T &x) { | ||||
|             return std::count(v.begin(), v.end(), x); | ||||
|         }, | ||||
|     cl.def( | ||||
|         "count", | ||||
|         [](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), x); }, | ||||
|         arg("x"), | ||||
|         "Return the number of times ``x`` appears in the list" | ||||
|     ); | ||||
|         "Return the number of times ``x`` appears in the list"); | ||||
| 
 | ||||
|     cl.def("remove", [](Vector &v, const T &x) { | ||||
|     cl.def( | ||||
|         "remove", | ||||
|         [](Vector &v, const T &x) { | ||||
|             auto p = std::find(v.begin(), v.end(), x); | ||||
|             if (p != v.end()) | ||||
|             if (p != v.end()) { | ||||
|                 v.erase(p); | ||||
|             else | ||||
|             } else { | ||||
|                 throw value_error(); | ||||
|             } | ||||
|         }, | ||||
|         arg("x"), | ||||
|         "Remove the first item from the list whose value is x. " | ||||
|         "It is an error if there is no such item." | ||||
|     ); | ||||
|         "It is an error if there is no such item."); | ||||
| 
 | ||||
|     cl.def("__contains__", | ||||
|         [](const Vector &v, const T &x) { | ||||
|             return std::find(v.begin(), v.end(), x) != v.end(); | ||||
|         }, | ||||
|     cl.def( | ||||
|         "__contains__", | ||||
|         [](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); }, | ||||
|         arg("x"), | ||||
|         "Return true the container contains ``x``" | ||||
|     ); | ||||
|         "Return true the container contains ``x``"); | ||||
| } | ||||
| 
 | ||||
| // Vector modifiers -- requires a copyable vector_type:
 | ||||
| // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems
 | ||||
| // silly to allow deletion but not insertion, so include them here too.)
 | ||||
| // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it
 | ||||
| // seems silly to allow deletion but not insertion, so include them here too.)
 | ||||
| template <typename Vector, typename Class_> | ||||
| void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) { | ||||
| void vector_modifiers( | ||||
|     enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) { | ||||
|     using T = typename Vector::value_type; | ||||
|     using SizeType = typename Vector::size_type; | ||||
|     using DiffType = typename Vector::difference_type; | ||||
| 
 | ||||
|     auto wrap_i = [](DiffType i, SizeType n) { | ||||
|         if (i < 0) | ||||
|         if (i < 0) { | ||||
|             i += n; | ||||
|         if (i < 0 || (SizeType)i >= n) | ||||
|         } | ||||
|         if (i < 0 || (SizeType) i >= n) { | ||||
|             throw index_error(); | ||||
|         } | ||||
|         return i; | ||||
|     }; | ||||
| 
 | ||||
|     cl.def("append", | ||||
|     cl.def( | ||||
|         "append", | ||||
|         [](Vector &v, const T &value) { v.push_back(value); }, | ||||
|         arg("x"), | ||||
|         "Add an item to the end of the list"); | ||||
|  | @ -131,25 +147,20 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t | |||
|     cl.def(init([](const iterable &it) { | ||||
|         auto v = std::unique_ptr<Vector>(new Vector()); | ||||
|         v->reserve(len_hint(it)); | ||||
|         for (handle h : it) | ||||
|         for (handle h : it) { | ||||
|             v->push_back(h.cast<T>()); | ||||
|         } | ||||
|         return v.release(); | ||||
|     })); | ||||
| 
 | ||||
|     cl.def("clear", | ||||
|         [](Vector &v) { | ||||
|             v.clear(); | ||||
|         }, | ||||
|         "Clear the contents" | ||||
|     ); | ||||
|     cl.def( | ||||
|         "clear", [](Vector &v) { v.clear(); }, "Clear the contents"); | ||||
| 
 | ||||
|     cl.def("extend", | ||||
|        [](Vector &v, const Vector &src) { | ||||
|            v.insert(v.end(), src.begin(), src.end()); | ||||
|        }, | ||||
|     cl.def( | ||||
|         "extend", | ||||
|         [](Vector &v, const Vector &src) { v.insert(v.end(), src.begin(), src.end()); }, | ||||
|         arg("L"), | ||||
|        "Extend the list by appending all the items in the given list" | ||||
|     ); | ||||
|         "Extend the list by appending all the items in the given list"); | ||||
| 
 | ||||
|     cl.def( | ||||
|         "extend", | ||||
|  | @ -174,31 +185,36 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t | |||
|         arg("L"), | ||||
|         "Extend the list by appending all the items in the given list"); | ||||
| 
 | ||||
|     cl.def("insert", | ||||
|     cl.def( | ||||
|         "insert", | ||||
|         [](Vector &v, DiffType i, const T &x) { | ||||
|             // Can't use wrap_i; i == v.size() is OK
 | ||||
|             if (i < 0) | ||||
|             if (i < 0) { | ||||
|                 i += v.size(); | ||||
|             if (i < 0 || (SizeType)i > v.size()) | ||||
|             } | ||||
|             if (i < 0 || (SizeType) i > v.size()) { | ||||
|                 throw index_error(); | ||||
|             } | ||||
|             v.insert(v.begin() + i, x); | ||||
|         }, | ||||
|         arg("i") , arg("x"), | ||||
|         "Insert an item at a given position." | ||||
|     ); | ||||
|         arg("i"), | ||||
|         arg("x"), | ||||
|         "Insert an item at a given position."); | ||||
| 
 | ||||
|     cl.def("pop", | ||||
|     cl.def( | ||||
|         "pop", | ||||
|         [](Vector &v) { | ||||
|             if (v.empty()) | ||||
|             if (v.empty()) { | ||||
|                 throw index_error(); | ||||
|             } | ||||
|             T t = std::move(v.back()); | ||||
|             v.pop_back(); | ||||
|             return t; | ||||
|         }, | ||||
|         "Remove and return the last item" | ||||
|     ); | ||||
|         "Remove and return the last item"); | ||||
| 
 | ||||
|     cl.def("pop", | ||||
|     cl.def( | ||||
|         "pop", | ||||
|         [wrap_i](Vector &v, DiffType i) { | ||||
|             i = wrap_i(i, v.size()); | ||||
|             T t = std::move(v[(SizeType) i]); | ||||
|  | @ -206,24 +222,22 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t | |||
|             return t; | ||||
|         }, | ||||
|         arg("i"), | ||||
|         "Remove and return the item at index ``i``" | ||||
|     ); | ||||
|         "Remove and return the item at index ``i``"); | ||||
| 
 | ||||
|     cl.def("__setitem__", | ||||
|         [wrap_i](Vector &v, DiffType i, const T &t) { | ||||
|     cl.def("__setitem__", [wrap_i](Vector &v, DiffType i, const T &t) { | ||||
|         i = wrap_i(i, v.size()); | ||||
|         v[(SizeType) i] = t; | ||||
|         } | ||||
|     ); | ||||
|     }); | ||||
| 
 | ||||
|     /// 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)) | ||||
|             if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { | ||||
|                 throw error_already_set(); | ||||
|             } | ||||
| 
 | ||||
|             auto *seq = new Vector(); | ||||
|             seq->reserve((size_t) slicelength); | ||||
|  | @ -239,13 +253,16 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t | |||
| 
 | ||||
|     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)) | ||||
|             if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { | ||||
|                 throw error_already_set(); | ||||
|             } | ||||
| 
 | ||||
|             if (slicelength != value.size()) | ||||
|                 throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); | ||||
|             if (slicelength != value.size()) { | ||||
|                 throw std::runtime_error( | ||||
|                     "Left and right hand size of slice assignment have different sizes!"); | ||||
|             } | ||||
| 
 | ||||
|             for (size_t i = 0; i < slicelength; ++i) { | ||||
|                 v[start] = value[i]; | ||||
|  | @ -254,21 +271,22 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t | |||
|         }, | ||||
|         "Assign list elements using a slice object"); | ||||
| 
 | ||||
|     cl.def("__delitem__", | ||||
|     cl.def( | ||||
|         "__delitem__", | ||||
|         [wrap_i](Vector &v, DiffType i) { | ||||
|             i = wrap_i(i, v.size()); | ||||
|             v.erase(v.begin() + i); | ||||
|         }, | ||||
|         "Delete the list elements at index ``i``" | ||||
|     ); | ||||
|         "Delete the list elements at index ``i``"); | ||||
| 
 | ||||
|     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)) | ||||
|             if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { | ||||
|                 throw error_already_set(); | ||||
|             } | ||||
| 
 | ||||
|             if (step == 1 && false) { | ||||
|                 v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); | ||||
|  | @ -284,8 +302,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t | |||
| 
 | ||||
| // If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
 | ||||
| // we have to access by copying; otherwise we return by reference.
 | ||||
| template <typename Vector> using vector_needs_copy = negation< | ||||
|     std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>; | ||||
| template <typename Vector> | ||||
| using vector_needs_copy | ||||
|     = negation<std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), | ||||
|                             typename Vector::value_type &>>; | ||||
| 
 | ||||
| // The usual case: access and iterate by reference
 | ||||
| template <typename Vector, typename Class_> | ||||
|  | @ -296,14 +316,17 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl) | |||
|     using ItType = typename Vector::iterator; | ||||
| 
 | ||||
|     auto wrap_i = [](DiffType i, SizeType n) { | ||||
|         if (i < 0) | ||||
|         if (i < 0) { | ||||
|             i += n; | ||||
|         if (i < 0 || (SizeType)i >= n) | ||||
|         } | ||||
|         if (i < 0 || (SizeType) i >= n) { | ||||
|             throw index_error(); | ||||
|         } | ||||
|         return i; | ||||
|     }; | ||||
| 
 | ||||
|     cl.def("__getitem__", | ||||
|     cl.def( | ||||
|         "__getitem__", | ||||
|         [wrap_i](Vector &v, DiffType i) -> T & { | ||||
|             i = wrap_i(i, v.size()); | ||||
|             return v[(SizeType) i]; | ||||
|  | @ -311,10 +334,10 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl) | |||
|         return_value_policy::reference_internal // ref + keepalive
 | ||||
|     ); | ||||
| 
 | ||||
|     cl.def("__iter__", | ||||
|     cl.def( | ||||
|         "__iter__", | ||||
|         [](Vector &v) { | ||||
|                return make_iterator< | ||||
|                    return_value_policy::reference_internal, ItType, ItType, T&>( | ||||
|             return make_iterator<return_value_policy::reference_internal, ItType, ItType, T &>( | ||||
|                 v.begin(), v.end()); | ||||
|         }, | ||||
|         keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ | ||||
|  | @ -328,52 +351,59 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl) | |||
|     using SizeType = typename Vector::size_type; | ||||
|     using DiffType = typename Vector::difference_type; | ||||
|     using ItType = typename Vector::iterator; | ||||
|     cl.def("__getitem__", | ||||
|         [](const Vector &v, DiffType i) -> T { | ||||
|             if (i < 0 && (i += v.size()) < 0) | ||||
|     cl.def("__getitem__", [](const Vector &v, DiffType i) -> T { | ||||
|         if (i < 0 && (i += v.size()) < 0) { | ||||
|             throw index_error(); | ||||
|             if ((SizeType)i >= v.size()) | ||||
|                 throw index_error(); | ||||
|             return v[(SizeType)i]; | ||||
|         } | ||||
|     ); | ||||
|         if ((SizeType) i >= v.size()) { | ||||
|             throw index_error(); | ||||
|         } | ||||
|         return v[(SizeType) i]; | ||||
|     }); | ||||
| 
 | ||||
|     cl.def("__iter__", | ||||
|     cl.def( | ||||
|         "__iter__", | ||||
|         [](Vector &v) { | ||||
|                return make_iterator< | ||||
|                    return_value_policy::copy, ItType, ItType, T>( | ||||
|                    v.begin(), v.end()); | ||||
|             return make_iterator<return_value_policy::copy, ItType, ItType, T>(v.begin(), v.end()); | ||||
|         }, | ||||
|         keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| template <typename Vector, typename Class_> auto vector_if_insertion_operator(Class_ &cl, std::string const &name) | ||||
|     -> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) { | ||||
| template <typename Vector, typename Class_> | ||||
| auto vector_if_insertion_operator(Class_ &cl, std::string const &name) | ||||
|     -> decltype(std::declval<std::ostream &>() << std::declval<typename Vector::value_type>(), | ||||
|                 void()) { | ||||
|     using size_type = typename Vector::size_type; | ||||
| 
 | ||||
|     cl.def("__repr__", | ||||
|     cl.def( | ||||
|         "__repr__", | ||||
|         [name](Vector &v) { | ||||
|             std::ostringstream s; | ||||
|             s << name << '['; | ||||
|             for (size_type i = 0; i < v.size(); ++i) { | ||||
|                 s << v[i]; | ||||
|                 if (i != v.size() - 1) | ||||
|                 if (i != v.size() - 1) { | ||||
|                     s << ", "; | ||||
|                 } | ||||
|             } | ||||
|             s << ']'; | ||||
|             return s.str(); | ||||
|         }, | ||||
|         "Return the canonical string representation of this list." | ||||
|     ); | ||||
|         "Return the canonical string representation of this list."); | ||||
| } | ||||
| 
 | ||||
| // Provide the buffer interface for vectors if we have data() and we have a format for it
 | ||||
| // GCC seems to have "void std::vector<bool>::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer
 | ||||
| // GCC seems to have "void std::vector<bool>::data()" - doing SFINAE on the existence of data()
 | ||||
| // is insufficient, we need to check it returns an appropriate pointer
 | ||||
| template <typename Vector, typename = void> | ||||
| struct vector_has_data_and_format : std::false_type {}; | ||||
| template <typename Vector> | ||||
| struct vector_has_data_and_format<Vector, enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(), std::declval<Vector>().data()), typename Vector::value_type*>::value>> : std::true_type {}; | ||||
| struct vector_has_data_and_format< | ||||
|     Vector, | ||||
|     enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(), | ||||
|                                       std::declval<Vector>().data()), | ||||
|                              typename Vector::value_type *>::value>> : std::true_type {}; | ||||
| 
 | ||||
| // [workaround(intel)] Separate function required here
 | ||||
| // Workaround as the Intel compiler does not compile the enable_if_t part below
 | ||||
|  | @ -391,21 +421,32 @@ template <typename Vector, typename Class_, typename... Args> | |||
| void vector_buffer_impl(Class_ &cl, std::true_type) { | ||||
|     using T = typename Vector::value_type; | ||||
| 
 | ||||
|     static_assert(vector_has_data_and_format<Vector>::value, "There is not an appropriate format descriptor for this vector"); | ||||
|     static_assert(vector_has_data_and_format<Vector>::value, | ||||
|                   "There is not an appropriate format descriptor for this vector"); | ||||
| 
 | ||||
|     // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here
 | ||||
|     // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard
 | ||||
|     // at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here
 | ||||
|     format_descriptor<T>::format(); | ||||
| 
 | ||||
|     cl.def_buffer([](Vector &v) -> buffer_info { | ||||
|         return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)}); | ||||
|         return buffer_info(v.data(), | ||||
|                            static_cast<ssize_t>(sizeof(T)), | ||||
|                            format_descriptor<T>::format(), | ||||
|                            1, | ||||
|                            {v.size()}, | ||||
|                            {sizeof(T)}); | ||||
|     }); | ||||
| 
 | ||||
|     cl.def(init([](const buffer &buf) { | ||||
|         auto info = buf.request(); | ||||
|         if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) | ||||
|         if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) { | ||||
|             throw type_error("Only valid 1D buffers can be copied to a vector"); | ||||
|         if (!detail::compare_buffer_info<T>::compare(info) || (ssize_t) sizeof(T) != info.itemsize) | ||||
|             throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")"); | ||||
|         } | ||||
|         if (!detail::compare_buffer_info<T>::compare(info) | ||||
|             || (ssize_t) sizeof(T) != info.itemsize) { | ||||
|             throw type_error("Format mismatch (Python: " + info.format | ||||
|                              + " C++: " + format_descriptor<T>::format() + ")"); | ||||
|         } | ||||
| 
 | ||||
|         T *p = static_cast<T *>(info.ptr); | ||||
|         ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T)); | ||||
|  | @ -415,10 +456,10 @@ void vector_buffer_impl(Class_& cl, std::true_type) { | |||
|         } | ||||
|         Vector vec; | ||||
|         vec.reserve((size_t) info.shape[0]); | ||||
|         for (; p != end; p += step) | ||||
|         for (; p != end; p += step) { | ||||
|             vec.push_back(*p); | ||||
|         } | ||||
|         return vec; | ||||
| 
 | ||||
|     })); | ||||
| 
 | ||||
|     return; | ||||
|  | @ -429,7 +470,8 @@ void vector_buffer_impl(Class_&, std::false_type) {} | |||
| 
 | ||||
| template <typename Vector, typename Class_, typename... Args> | ||||
| void vector_buffer(Class_ &cl) { | ||||
|     vector_buffer_impl<Vector, Class_, Args...>(cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{}); | ||||
|     vector_buffer_impl<Vector, Class_, Args...>( | ||||
|         cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{}); | ||||
| } | ||||
| 
 | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
|  | @ -444,7 +486,7 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A | |||
|     // If the value_type is unregistered (e.g. a converting type) or is itself registered
 | ||||
|     // module-local then make the vector binding module-local as well:
 | ||||
|     using vtype = typename Vector::value_type; | ||||
|     auto vtype_info = detail::get_type_info(typeid(vtype)); | ||||
|     auto *vtype_info = detail::get_type_info(typeid(vtype)); | ||||
|     bool local = !vtype_info || vtype_info->module_local; | ||||
| 
 | ||||
|     Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); | ||||
|  | @ -469,18 +511,13 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A | |||
|     // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
 | ||||
|     detail::vector_accessor<Vector, Class_>(cl); | ||||
| 
 | ||||
|     cl.def("__bool__", | ||||
|         [](const Vector &v) -> bool { | ||||
|             return !v.empty(); | ||||
|         }, | ||||
|         "Check whether the list is nonempty" | ||||
|     ); | ||||
|     cl.def( | ||||
|         "__bool__", | ||||
|         [](const Vector &v) -> bool { return !v.empty(); }, | ||||
|         "Check whether the list is nonempty"); | ||||
| 
 | ||||
|     cl.def("__len__", &Vector::size); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #if 0 | ||||
|     // C++ style functions deprecated, leaving it here as an example
 | ||||
|     cl.def(init<size_type>()); | ||||
|  | @ -524,8 +561,6 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A | |||
|     return cl; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //
 | ||||
| // std::map, std::unordered_map
 | ||||
| //
 | ||||
|  | @ -533,83 +568,86 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A | |||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| 
 | ||||
| /* Fallback functions */ | ||||
| template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { } | ||||
| template <typename, typename, typename... Args> void map_assignment(const Args &...) { } | ||||
| template <typename, typename, typename... Args> | ||||
| void map_if_insertion_operator(const Args &...) {} | ||||
| template <typename, typename, typename... Args> | ||||
| void map_assignment(const Args &...) {} | ||||
| 
 | ||||
| // Map assignment when copy-assignable: just copy the value
 | ||||
| template <typename Map, typename Class_> | ||||
| void map_assignment(enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) { | ||||
| void map_assignment( | ||||
|     enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) { | ||||
|     using KeyType = typename Map::key_type; | ||||
|     using MappedType = typename Map::mapped_type; | ||||
| 
 | ||||
|     cl.def("__setitem__", | ||||
|            [](Map &m, const KeyType &k, const MappedType &v) { | ||||
|     cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) { | ||||
|         auto it = m.find(k); | ||||
|                if (it != m.end()) it->second = v; | ||||
|                else m.emplace(k, v); | ||||
|         if (it != m.end()) { | ||||
|             it->second = v; | ||||
|         } else { | ||||
|             m.emplace(k, v); | ||||
|         } | ||||
|     ); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| // Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
 | ||||
| // Not copy-assignable, but still copy-constructible: we can update the value by erasing and
 | ||||
| // reinserting
 | ||||
| template <typename Map, typename Class_> | ||||
| void map_assignment(enable_if_t< | ||||
|         !is_copy_assignable<typename Map::mapped_type>::value && | ||||
|         is_copy_constructible<typename Map::mapped_type>::value, | ||||
| void map_assignment(enable_if_t<!is_copy_assignable<typename Map::mapped_type>::value | ||||
|                                     && is_copy_constructible<typename Map::mapped_type>::value, | ||||
|                                 Class_> &cl) { | ||||
|     using KeyType = typename Map::key_type; | ||||
|     using MappedType = typename Map::mapped_type; | ||||
| 
 | ||||
|     cl.def("__setitem__", | ||||
|            [](Map &m, const KeyType &k, const MappedType &v) { | ||||
|     cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) { | ||||
|         // We can't use m[k] = v; because value type might not be default constructable
 | ||||
|         auto r = m.emplace(k, v); | ||||
|         if (!r.second) { | ||||
|                    // value type is not copy assignable so the only way to insert it is to erase it first...
 | ||||
|             // value type is not copy assignable so the only way to insert it is to erase it
 | ||||
|             // first...
 | ||||
|             m.erase(r.first); | ||||
|             m.emplace(k, v); | ||||
|         } | ||||
|            } | ||||
|     ); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| template <typename Map, typename Class_> | ||||
| auto map_if_insertion_operator(Class_ &cl, std::string const &name) | ||||
|     -> decltype(std::declval<std::ostream &>() << std::declval<typename Map::key_type>() | ||||
|                                                << std::declval<typename Map::mapped_type>(), | ||||
|                 void()) { | ||||
| 
 | ||||
| template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &cl, std::string const &name) | ||||
| -> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) { | ||||
| 
 | ||||
|     cl.def("__repr__", | ||||
|     cl.def( | ||||
|         "__repr__", | ||||
|         [name](Map &m) { | ||||
|             std::ostringstream s; | ||||
|             s << name << '{'; | ||||
|             bool f = false; | ||||
|             for (auto const &kv : m) { | ||||
|                 if (f) | ||||
|                 if (f) { | ||||
|                     s << ", "; | ||||
|                 } | ||||
|                 s << kv.first << ": " << kv.second; | ||||
|                 f = true; | ||||
|             } | ||||
|             s << '}'; | ||||
|             return s.str(); | ||||
|         }, | ||||
|         "Return the canonical string representation of this map." | ||||
|     ); | ||||
|         "Return the canonical string representation of this map."); | ||||
| } | ||||
| 
 | ||||
| template <typename Map> | ||||
| struct keys_view | ||||
| { | ||||
| struct keys_view { | ||||
|     Map ↦ | ||||
| }; | ||||
| 
 | ||||
| template <typename Map> | ||||
| struct values_view | ||||
| { | ||||
| struct values_view { | ||||
|     Map ↦ | ||||
| }; | ||||
| 
 | ||||
| template <typename Map> | ||||
| struct items_view | ||||
| { | ||||
| struct items_view { | ||||
|     Map ↦ | ||||
| }; | ||||
| 
 | ||||
|  | @ -627,7 +665,7 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. | |||
|     // If either type is a non-module-local bound type then make the map binding non-local as well;
 | ||||
|     // otherwise (e.g. both types are either module-local or converting) the map will be
 | ||||
|     // module-local.
 | ||||
|     auto tinfo = detail::get_type_info(typeid(MappedType)); | ||||
|     auto *tinfo = detail::get_type_info(typeid(MappedType)); | ||||
|     bool local = !tinfo || tinfo->module_local; | ||||
|     if (local) { | ||||
|         tinfo = detail::get_type_info(typeid(KeyType)); | ||||
|  | @ -647,97 +685,97 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. | |||
|     // Register stream insertion operator (if possible)
 | ||||
|     detail::map_if_insertion_operator<Map, Class_>(cl, name); | ||||
| 
 | ||||
|     cl.def("__bool__", | ||||
|     cl.def( | ||||
|         "__bool__", | ||||
|         [](const Map &m) -> bool { return !m.empty(); }, | ||||
|         "Check whether the map is nonempty" | ||||
|     ); | ||||
|         "Check whether the map is nonempty"); | ||||
| 
 | ||||
|     cl.def("__iter__", | ||||
|     cl.def( | ||||
|         "__iter__", | ||||
|         [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, | ||||
|         keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */ | ||||
|     ); | ||||
| 
 | ||||
|     cl.def("keys", | ||||
|     cl.def( | ||||
|         "keys", | ||||
|         [](Map &m) { return KeysView{m}; }, | ||||
|         keep_alive<0, 1>() /* Essential: keep map alive while view exists */ | ||||
|     ); | ||||
| 
 | ||||
|     cl.def("values", | ||||
|     cl.def( | ||||
|         "values", | ||||
|         [](Map &m) { return ValuesView{m}; }, | ||||
|         keep_alive<0, 1>() /* Essential: keep map alive while view exists */ | ||||
|     ); | ||||
| 
 | ||||
|     cl.def("items", | ||||
|     cl.def( | ||||
|         "items", | ||||
|         [](Map &m) { return ItemsView{m}; }, | ||||
|         keep_alive<0, 1>() /* Essential: keep map alive while view exists */ | ||||
|     ); | ||||
| 
 | ||||
|     cl.def("__getitem__", | ||||
|     cl.def( | ||||
|         "__getitem__", | ||||
|         [](Map &m, const KeyType &k) -> MappedType & { | ||||
|             auto it = m.find(k); | ||||
|             if (it == m.end()) | ||||
|             if (it == m.end()) { | ||||
|                 throw key_error(); | ||||
|             } | ||||
|             return it->second; | ||||
|         }, | ||||
|         return_value_policy::reference_internal // ref + keepalive
 | ||||
|     ); | ||||
| 
 | ||||
|     cl.def("__contains__", | ||||
|         [](Map &m, const KeyType &k) -> bool { | ||||
|     cl.def("__contains__", [](Map &m, const KeyType &k) -> bool { | ||||
|         auto it = m.find(k); | ||||
|             if (it == m.end()) | ||||
|         if (it == m.end()) { | ||||
|             return false; | ||||
|            return true; | ||||
|         } | ||||
|     ); | ||||
|         return true; | ||||
|     }); | ||||
|     // Fallback for when the object is not of the key type
 | ||||
|     cl.def("__contains__", [](Map &, const object &) -> bool { return false; }); | ||||
| 
 | ||||
|     // Assignment provided only if the type is copyable
 | ||||
|     detail::map_assignment<Map, Class_>(cl); | ||||
| 
 | ||||
|     cl.def("__delitem__", | ||||
|            [](Map &m, const KeyType &k) { | ||||
|     cl.def("__delitem__", [](Map &m, const KeyType &k) { | ||||
|         auto it = m.find(k); | ||||
|                if (it == m.end()) | ||||
|         if (it == m.end()) { | ||||
|             throw key_error(); | ||||
|                m.erase(it); | ||||
|         } | ||||
|     ); | ||||
|         m.erase(it); | ||||
|     }); | ||||
| 
 | ||||
|     cl.def("__len__", &Map::size); | ||||
| 
 | ||||
|     keys_view.def("__len__", [](KeysView &view) { return view.map.size(); }); | ||||
|     keys_view.def("__iter__", | ||||
|         [](KeysView &view) { | ||||
|             return make_key_iterator(view.map.begin(), view.map.end()); | ||||
|         }, | ||||
|     keys_view.def( | ||||
|         "__iter__", | ||||
|         [](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); }, | ||||
|         keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ | ||||
|     ); | ||||
|     keys_view.def("__contains__", | ||||
|         [](KeysView &view, const KeyType &k) -> bool { | ||||
|     keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool { | ||||
|         auto it = view.map.find(k); | ||||
|             if (it == view.map.end()) | ||||
|         if (it == view.map.end()) { | ||||
|             return false; | ||||
|             return true; | ||||
|         } | ||||
|     ); | ||||
|         return true; | ||||
|     }); | ||||
|     // Fallback for when the object is not of the key type
 | ||||
|     keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; }); | ||||
| 
 | ||||
|     values_view.def("__len__", [](ValuesView &view) { return view.map.size(); }); | ||||
|     values_view.def("__iter__", | ||||
|         [](ValuesView &view) { | ||||
|             return make_value_iterator(view.map.begin(), view.map.end()); | ||||
|         }, | ||||
|     values_view.def( | ||||
|         "__iter__", | ||||
|         [](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); }, | ||||
|         keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ | ||||
|     ); | ||||
| 
 | ||||
|     items_view.def("__len__", [](ItemsView &view) { return view.map.size(); }); | ||||
|     items_view.def("__iter__", | ||||
|         [](ItemsView &view) { | ||||
|             return make_iterator(view.map.begin(), view.map.end()); | ||||
|         }, | ||||
|     items_view.def( | ||||
|         "__iter__", | ||||
|         [](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); }, | ||||
|         keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ | ||||
|     ); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,14 @@ | |||
| import os | ||||
| 
 | ||||
| import nox | ||||
| 
 | ||||
| nox.needs_version = ">=2022.1.7" | ||||
| nox.options.sessions = ["lint", "tests", "tests_packaging"] | ||||
| 
 | ||||
| PYTHON_VERISONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] | ||||
| PYTHON_VERISONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.7", "pypy3.8"] | ||||
| 
 | ||||
| if os.environ.get("CI", None): | ||||
|     nox.options.error_on_missing_interpreters = True | ||||
| 
 | ||||
| 
 | ||||
| @nox.session(reuse_venv=True) | ||||
|  | @ -24,14 +30,12 @@ def tests(session: nox.Session) -> None: | |||
|     session.install("-r", "tests/requirements.txt") | ||||
|     session.run( | ||||
|         "cmake", | ||||
|         "-S", | ||||
|         ".", | ||||
|         "-B", | ||||
|         tmpdir, | ||||
|         "-S.", | ||||
|         f"-B{tmpdir}", | ||||
|         "-DPYBIND11_WERROR=ON", | ||||
|         "-DDOWNLOAD_CATCH=ON", | ||||
|         "-DDOWNLOAD_EIGEN=ON", | ||||
|         *session.posargs | ||||
|         *session.posargs, | ||||
|     ) | ||||
|     session.run("cmake", "--build", tmpdir) | ||||
|     session.run("cmake", "--build", tmpdir, "--config=Release", "--target", "check") | ||||
|  | @ -57,10 +61,10 @@ def docs(session: nox.Session) -> None: | |||
|     session.chdir("docs") | ||||
| 
 | ||||
|     if "pdf" in session.posargs: | ||||
|         session.run("sphinx-build", "-b", "latexpdf", ".", "_build") | ||||
|         session.run("sphinx-build", "-M", "latexpdf", ".", "_build") | ||||
|         return | ||||
| 
 | ||||
|     session.run("sphinx-build", "-b", "html", ".", "_build") | ||||
|     session.run("sphinx-build", "-M", "html", ".", "_build") | ||||
| 
 | ||||
|     if "serve" in session.posargs: | ||||
|         session.log("Launching docs at http://localhost:8000/ - use Ctrl-C to quit") | ||||
|  |  | |||
|  | @ -1,4 +1,9 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import sys | ||||
| 
 | ||||
| if sys.version_info < (3, 6): | ||||
|     msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5." | ||||
|     raise ImportError(msg) | ||||
| 
 | ||||
| 
 | ||||
| from ._version import __version__, version_info | ||||
| from .commands import get_cmake_dir, get_include | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from __future__ import print_function | ||||
| # pylint: disable=missing-function-docstring | ||||
| 
 | ||||
| import argparse | ||||
| import sys | ||||
|  | @ -8,8 +7,7 @@ import sysconfig | |||
| from .commands import get_cmake_dir, get_include | ||||
| 
 | ||||
| 
 | ||||
| def print_includes(): | ||||
|     # type: () -> None | ||||
| def print_includes() -> None: | ||||
|     dirs = [ | ||||
|         sysconfig.get_path("include"), | ||||
|         sysconfig.get_path("platinclude"), | ||||
|  | @ -25,8 +23,7 @@ def print_includes(): | |||
|     print(" ".join("-I" + d for d in unique_dirs)) | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     # type: () -> None | ||||
| def main() -> None: | ||||
| 
 | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument( | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from typing import Union | ||||
| 
 | ||||
| 
 | ||||
| def _to_int(s): | ||||
| def _to_int(s: str) -> Union[int, str]: | ||||
|     try: | ||||
|         return int(s) | ||||
|     except ValueError: | ||||
|         return s | ||||
| 
 | ||||
| 
 | ||||
| __version__ = "2.9.1" | ||||
| __version__ = "2.10.0" | ||||
| version_info = tuple(_to_int(s) for s in __version__.split(".")) | ||||
|  |  | |||
|  | @ -1,6 +0,0 @@ | |||
| from typing import Tuple, Union | ||||
| 
 | ||||
| def _to_int(s: str) -> Union[int, str]: ... | ||||
| 
 | ||||
| __version__: str | ||||
| version_info: Tuple[Union[int, str], ...] | ||||
|  | @ -1,21 +1,25 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import os | ||||
| 
 | ||||
| DIR = os.path.abspath(os.path.dirname(__file__)) | ||||
| 
 | ||||
| 
 | ||||
| def get_include(user=False): | ||||
|     # type: (bool) -> str | ||||
| def get_include(user: bool = False) -> str:  # pylint: disable=unused-argument | ||||
|     """ | ||||
|     Return the path to the pybind11 include directory. The historical "user" | ||||
|     argument is unused, and may be removed. | ||||
|     """ | ||||
|     installed_path = os.path.join(DIR, "include") | ||||
|     source_path = os.path.join(os.path.dirname(DIR), "include") | ||||
|     return installed_path if os.path.exists(installed_path) else source_path | ||||
| 
 | ||||
| 
 | ||||
| def get_cmake_dir(): | ||||
|     # type: () -> str | ||||
| def get_cmake_dir() -> str: | ||||
|     """ | ||||
|     Return the path to the pybind11 CMake module directory. | ||||
|     """ | ||||
|     cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11") | ||||
|     if os.path.exists(cmake_installed_path): | ||||
|         return cmake_installed_path | ||||
|     else: | ||||
| 
 | ||||
|     msg = "pybind11 not installed, installation required to access the CMake files" | ||||
|     raise ImportError(msg) | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| """ | ||||
| This module provides helpers for C++11+ projects using pybind11. | ||||
| 
 | ||||
|  | @ -49,6 +47,20 @@ import sysconfig | |||
| import tempfile | ||||
| import threading | ||||
| import warnings | ||||
| from functools import lru_cache | ||||
| from pathlib import Path | ||||
| from typing import ( | ||||
|     Any, | ||||
|     Callable, | ||||
|     Dict, | ||||
|     Iterable, | ||||
|     Iterator, | ||||
|     List, | ||||
|     Optional, | ||||
|     Tuple, | ||||
|     TypeVar, | ||||
|     Union, | ||||
| ) | ||||
| 
 | ||||
| try: | ||||
|     from setuptools import Extension as _Extension | ||||
|  | @ -61,7 +73,6 @@ import distutils.ccompiler | |||
| import distutils.errors | ||||
| 
 | ||||
| WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform() | ||||
| PY2 = sys.version_info[0] < 3 | ||||
| MACOS = sys.platform.startswith("darwin") | ||||
| STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" | ||||
| 
 | ||||
|  | @ -73,7 +84,7 @@ STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" | |||
| # directory into your path if it sits beside your setup.py. | ||||
| 
 | ||||
| 
 | ||||
| class Pybind11Extension(_Extension): | ||||
| class Pybind11Extension(_Extension):  # type: ignore[misc] | ||||
|     """ | ||||
|     Build a C++11+ Extension module with pybind11. This automatically adds the | ||||
|     recommended flags when you init the extension and assumes C++ sources - you | ||||
|  | @ -95,21 +106,18 @@ class Pybind11Extension(_Extension): | |||
| 
 | ||||
|     If you want to add pybind11 headers manually, for example for an exact | ||||
|     git checkout, then set ``include_pybind11=False``. | ||||
| 
 | ||||
|     Warning: do not use property-based access to the instance on Python 2 - | ||||
|     this is an ugly old-style class due to Distutils. | ||||
|     """ | ||||
| 
 | ||||
|     # flags are prepended, so that they can be further overridden, e.g. by | ||||
|     # ``extra_compile_args=["-g"]``. | ||||
| 
 | ||||
|     def _add_cflags(self, flags): | ||||
|     def _add_cflags(self, flags: List[str]) -> None: | ||||
|         self.extra_compile_args[:0] = flags | ||||
| 
 | ||||
|     def _add_ldflags(self, flags): | ||||
|     def _add_ldflags(self, flags: List[str]) -> None: | ||||
|         self.extra_link_args[:0] = flags | ||||
| 
 | ||||
|     def __init__(self, *args, **kwargs): | ||||
|     def __init__(self, *args: Any, **kwargs: Any) -> None: | ||||
| 
 | ||||
|         self._cxx_level = 0 | ||||
|         cxx_std = kwargs.pop("cxx_std", 0) | ||||
|  | @ -119,9 +127,7 @@ class Pybind11Extension(_Extension): | |||
| 
 | ||||
|         include_pybind11 = kwargs.pop("include_pybind11", True) | ||||
| 
 | ||||
|         # Can't use super here because distutils has old-style classes in | ||||
|         # Python 2! | ||||
|         _Extension.__init__(self, *args, **kwargs) | ||||
|         super().__init__(*args, **kwargs) | ||||
| 
 | ||||
|         # Include the installed package pybind11 headers | ||||
|         if include_pybind11: | ||||
|  | @ -133,11 +139,10 @@ class Pybind11Extension(_Extension): | |||
| 
 | ||||
|                 if pyinc not in self.include_dirs: | ||||
|                     self.include_dirs.append(pyinc) | ||||
|             except ImportError: | ||||
|             except ModuleNotFoundError: | ||||
|                 pass | ||||
| 
 | ||||
|         # Have to use the accessor manually to support Python 2 distutils | ||||
|         Pybind11Extension.cxx_std.__set__(self, cxx_std) | ||||
|         self.cxx_std = cxx_std | ||||
| 
 | ||||
|         cflags = [] | ||||
|         ldflags = [] | ||||
|  | @ -157,18 +162,18 @@ class Pybind11Extension(_Extension): | |||
|         self._add_ldflags(ldflags) | ||||
| 
 | ||||
|     @property | ||||
|     def cxx_std(self): | ||||
|     def cxx_std(self) -> int: | ||||
|         """ | ||||
|         The CXX standard level. If set, will add the required flags. If left | ||||
|         at 0, it will trigger an automatic search when pybind11's build_ext | ||||
|         is used. If None, will have no effect.  Besides just the flags, this | ||||
|         may add a register warning/error fix for Python 2 or macos-min 10.9 | ||||
|         or 10.14. | ||||
|         The CXX standard level. If set, will add the required flags. If left at | ||||
|         0, it will trigger an automatic search when pybind11's build_ext is | ||||
|         used. If None, will have no effect.  Besides just the flags, this may | ||||
|         add a macos-min 10.9 or 10.14 flag if MACOSX_DEPLOYMENT_TARGET is | ||||
|         unset. | ||||
|         """ | ||||
|         return self._cxx_level | ||||
| 
 | ||||
|     @cxx_std.setter | ||||
|     def cxx_std(self, level): | ||||
|     def cxx_std(self, level: int) -> None: | ||||
| 
 | ||||
|         if self._cxx_level: | ||||
|             warnings.warn("You cannot safely change the cxx_level after setting it!") | ||||
|  | @ -195,31 +200,20 @@ class Pybind11Extension(_Extension): | |||
|             current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2]) | ||||
|             desired_macos = (10, 9) if level < 17 else (10, 14) | ||||
|             macos_string = ".".join(str(x) for x in min(current_macos, desired_macos)) | ||||
|             macosx_min = "-mmacosx-version-min=" + macos_string | ||||
|             macosx_min = f"-mmacosx-version-min={macos_string}" | ||||
|             cflags += [macosx_min] | ||||
|             ldflags += [macosx_min] | ||||
| 
 | ||||
|         if PY2: | ||||
|             if WIN: | ||||
|                 # Will be ignored on MSVC 2015, where C++17 is not supported so | ||||
|                 # this flag is not valid. | ||||
|                 cflags += ["/wd5033"] | ||||
|             elif level >= 17: | ||||
|                 cflags += ["-Wno-register"] | ||||
|             elif level >= 14: | ||||
|                 cflags += ["-Wno-deprecated-register"] | ||||
| 
 | ||||
|         self._add_cflags(cflags) | ||||
|         self._add_ldflags(ldflags) | ||||
| 
 | ||||
| 
 | ||||
| # Just in case someone clever tries to multithread | ||||
| tmp_chdir_lock = threading.Lock() | ||||
| cpp_cache_lock = threading.Lock() | ||||
| 
 | ||||
| 
 | ||||
| @contextlib.contextmanager | ||||
| def tmp_chdir(): | ||||
| def tmp_chdir() -> Iterator[str]: | ||||
|     "Prepare and enter a temporary directory, cleanup when done" | ||||
| 
 | ||||
|     # Threadsafe | ||||
|  | @ -235,7 +229,7 @@ def tmp_chdir(): | |||
| 
 | ||||
| 
 | ||||
| # cf http://bugs.python.org/issue26689 | ||||
| def has_flag(compiler, flag): | ||||
| def has_flag(compiler: Any, flag: str) -> bool: | ||||
|     """ | ||||
|     Return the flag if a flag name is supported on the | ||||
|     specified compiler, otherwise None (can be used as a boolean). | ||||
|  | @ -243,13 +237,12 @@ def has_flag(compiler, flag): | |||
|     """ | ||||
| 
 | ||||
|     with tmp_chdir(): | ||||
|         fname = "flagcheck.cpp" | ||||
|         with open(fname, "w") as f: | ||||
|         fname = Path("flagcheck.cpp") | ||||
|         # Don't trigger -Wunused-parameter. | ||||
|             f.write("int main (int, char **) { return 0; }") | ||||
|         fname.write_text("int main (int, char **) { return 0; }", encoding="utf-8") | ||||
| 
 | ||||
|         try: | ||||
|             compiler.compile([fname], extra_postargs=[flag]) | ||||
|             compiler.compile([str(fname)], extra_postargs=[flag]) | ||||
|         except distutils.errors.CompileError: | ||||
|             return False | ||||
|         return True | ||||
|  | @ -259,7 +252,8 @@ def has_flag(compiler, flag): | |||
| cpp_flag_cache = None | ||||
| 
 | ||||
| 
 | ||||
| def auto_cpp_level(compiler): | ||||
| @lru_cache() | ||||
| def auto_cpp_level(compiler: Any) -> Union[str, int]: | ||||
|     """ | ||||
|     Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows. | ||||
|     """ | ||||
|  | @ -267,48 +261,38 @@ def auto_cpp_level(compiler): | |||
|     if WIN: | ||||
|         return "latest" | ||||
| 
 | ||||
|     global cpp_flag_cache | ||||
| 
 | ||||
|     # If this has been previously calculated with the same args, return that | ||||
|     with cpp_cache_lock: | ||||
|         if cpp_flag_cache: | ||||
|             return cpp_flag_cache | ||||
| 
 | ||||
|     levels = [17, 14, 11] | ||||
| 
 | ||||
|     for level in levels: | ||||
|         if has_flag(compiler, STD_TMPL.format(level)): | ||||
|             with cpp_cache_lock: | ||||
|                 cpp_flag_cache = level | ||||
|             return level | ||||
| 
 | ||||
|     msg = "Unsupported compiler -- at least C++11 support is needed!" | ||||
|     raise RuntimeError(msg) | ||||
| 
 | ||||
| 
 | ||||
| class build_ext(_build_ext):  # noqa: N801 | ||||
| class build_ext(_build_ext):  # type: ignore[misc] # noqa: N801 | ||||
|     """ | ||||
|     Customized build_ext that allows an auto-search for the highest supported | ||||
|     C++ level for Pybind11Extension. This is only needed for the auto-search | ||||
|     for now, and is completely optional otherwise. | ||||
|     """ | ||||
| 
 | ||||
|     def build_extensions(self): | ||||
|     def build_extensions(self) -> None: | ||||
|         """ | ||||
|         Build extensions, injecting C++ std for Pybind11Extension if needed. | ||||
|         """ | ||||
| 
 | ||||
|         for ext in self.extensions: | ||||
|             if hasattr(ext, "_cxx_level") and ext._cxx_level == 0: | ||||
|                 # Python 2 syntax - old-style distutils class | ||||
|                 ext.__class__.cxx_std.__set__(ext, auto_cpp_level(self.compiler)) | ||||
|                 ext.cxx_std = auto_cpp_level(self.compiler) | ||||
| 
 | ||||
|         # Python 2 doesn't allow super here, since distutils uses old-style | ||||
|         # classes! | ||||
|         _build_ext.build_extensions(self) | ||||
|         super().build_extensions() | ||||
| 
 | ||||
| 
 | ||||
| def intree_extensions(paths, package_dir=None): | ||||
| def intree_extensions( | ||||
|     paths: Iterable[str], package_dir: Optional[Dict[str, str]] = None | ||||
| ) -> List[Pybind11Extension]: | ||||
|     """ | ||||
|     Generate Pybind11Extensions from source files directly located in a Python | ||||
|     source tree. | ||||
|  | @ -318,33 +302,37 @@ def intree_extensions(paths, package_dir=None): | |||
|     not contain an ``__init__.py`` file. | ||||
|     """ | ||||
|     exts = [] | ||||
|     for path in paths: | ||||
| 
 | ||||
|     if package_dir is None: | ||||
|         for path in paths: | ||||
|             parent, _ = os.path.split(path) | ||||
|             while os.path.exists(os.path.join(parent, "__init__.py")): | ||||
|                 parent, _ = os.path.split(parent) | ||||
|             relname, _ = os.path.splitext(os.path.relpath(path, parent)) | ||||
|             qualified_name = relname.replace(os.path.sep, ".") | ||||
|             exts.append(Pybind11Extension(qualified_name, [path])) | ||||
|         else: | ||||
|             found = False | ||||
|         return exts | ||||
| 
 | ||||
|     for path in paths: | ||||
|         for prefix, parent in package_dir.items(): | ||||
|             if path.startswith(parent): | ||||
|                     found = True | ||||
|                 relname, _ = os.path.splitext(os.path.relpath(path, parent)) | ||||
|                 qualified_name = relname.replace(os.path.sep, ".") | ||||
|                 if prefix: | ||||
|                     qualified_name = prefix + "." + qualified_name | ||||
|                 exts.append(Pybind11Extension(qualified_name, [path])) | ||||
|             if not found: | ||||
|                 raise ValueError( | ||||
|                     "path {} is not a child of any of the directories listed " | ||||
|                     "in 'package_dir' ({})".format(path, package_dir) | ||||
|                 break | ||||
|         else: | ||||
|             msg = ( | ||||
|                 f"path {path} is not a child of any of the directories listed " | ||||
|                 f"in 'package_dir' ({package_dir})" | ||||
|             ) | ||||
|             raise ValueError(msg) | ||||
| 
 | ||||
|     return exts | ||||
| 
 | ||||
| 
 | ||||
| def naive_recompile(obj, src): | ||||
| def naive_recompile(obj: str, src: str) -> bool: | ||||
|     """ | ||||
|     This will recompile only if the source file changes. It does not check | ||||
|     header files, so a more advanced function or Ccache is better if you have | ||||
|  | @ -353,7 +341,7 @@ def naive_recompile(obj, src): | |||
|     return os.stat(obj).st_mtime < os.stat(src).st_mtime | ||||
| 
 | ||||
| 
 | ||||
| def no_recompile(obg, src): | ||||
| def no_recompile(obg: str, src: str) -> bool:  # pylint: disable=unused-argument | ||||
|     """ | ||||
|     This is the safest but slowest choice (and is the default) - will always | ||||
|     recompile sources. | ||||
|  | @ -361,15 +349,33 @@ def no_recompile(obg, src): | |||
|     return True | ||||
| 
 | ||||
| 
 | ||||
| S = TypeVar("S", bound="ParallelCompile") | ||||
| 
 | ||||
| CCompilerMethod = Callable[ | ||||
|     [ | ||||
|         distutils.ccompiler.CCompiler, | ||||
|         List[str], | ||||
|         Optional[str], | ||||
|         Optional[Union[Tuple[str], Tuple[str, Optional[str]]]], | ||||
|         Optional[List[str]], | ||||
|         bool, | ||||
|         Optional[List[str]], | ||||
|         Optional[List[str]], | ||||
|         Optional[List[str]], | ||||
|     ], | ||||
|     List[str], | ||||
| ] | ||||
| 
 | ||||
| 
 | ||||
| # Optional parallel compile utility | ||||
| # inspired by: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils | ||||
| # and: https://github.com/tbenthompson/cppimport/blob/stable/cppimport/build_module.py | ||||
| # and NumPy's parallel distutils module: | ||||
| #              https://github.com/numpy/numpy/blob/master/numpy/distutils/ccompiler.py | ||||
| class ParallelCompile(object): | ||||
| class ParallelCompile: | ||||
|     """ | ||||
|     Make a parallel compile function. Inspired by | ||||
|     numpy.distutils.ccompiler.CCompiler_compile and cppimport. | ||||
|     numpy.distutils.ccompiler.CCompiler.compile and cppimport. | ||||
| 
 | ||||
|     This takes several arguments that allow you to customize the compile | ||||
|     function created: | ||||
|  | @ -404,35 +410,41 @@ class ParallelCompile(object): | |||
| 
 | ||||
|     __slots__ = ("envvar", "default", "max", "_old", "needs_recompile") | ||||
| 
 | ||||
|     def __init__(self, envvar=None, default=0, max=0, needs_recompile=no_recompile): | ||||
|     def __init__( | ||||
|         self, | ||||
|         envvar: Optional[str] = None, | ||||
|         default: int = 0, | ||||
|         max: int = 0,  # pylint: disable=redefined-builtin | ||||
|         needs_recompile: Callable[[str, str], bool] = no_recompile, | ||||
|     ) -> None: | ||||
|         self.envvar = envvar | ||||
|         self.default = default | ||||
|         self.max = max | ||||
|         self.needs_recompile = needs_recompile | ||||
|         self._old = [] | ||||
|         self._old: List[CCompilerMethod] = [] | ||||
| 
 | ||||
|     def function(self): | ||||
|     def function(self) -> CCompilerMethod: | ||||
|         """ | ||||
|         Builds a function object usable as distutils.ccompiler.CCompiler.compile. | ||||
|         """ | ||||
| 
 | ||||
|         def compile_function( | ||||
|             compiler, | ||||
|             sources, | ||||
|             output_dir=None, | ||||
|             macros=None, | ||||
|             include_dirs=None, | ||||
|             debug=0, | ||||
|             extra_preargs=None, | ||||
|             extra_postargs=None, | ||||
|             depends=None, | ||||
|         ): | ||||
|             compiler: distutils.ccompiler.CCompiler, | ||||
|             sources: List[str], | ||||
|             output_dir: Optional[str] = None, | ||||
|             macros: Optional[Union[Tuple[str], Tuple[str, Optional[str]]]] = None, | ||||
|             include_dirs: Optional[List[str]] = None, | ||||
|             debug: bool = False, | ||||
|             extra_preargs: Optional[List[str]] = None, | ||||
|             extra_postargs: Optional[List[str]] = None, | ||||
|             depends: Optional[List[str]] = None, | ||||
|         ) -> Any: | ||||
| 
 | ||||
|             # These lines are directly from distutils.ccompiler.CCompiler | ||||
|             macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( | ||||
|             macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile(  # type: ignore[attr-defined] | ||||
|                 output_dir, macros, include_dirs, sources, depends, extra_postargs | ||||
|             ) | ||||
|             cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs) | ||||
|             cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs)  # type: ignore[attr-defined] | ||||
| 
 | ||||
|             # The number of threads; start with default. | ||||
|             threads = self.default | ||||
|  | @ -441,14 +453,14 @@ class ParallelCompile(object): | |||
|             if self.envvar is not None: | ||||
|                 threads = int(os.environ.get(self.envvar, self.default)) | ||||
| 
 | ||||
|             def _single_compile(obj): | ||||
|             def _single_compile(obj: Any) -> None: | ||||
|                 try: | ||||
|                     src, ext = build[obj] | ||||
|                 except KeyError: | ||||
|                     return | ||||
| 
 | ||||
|                 if not os.path.exists(obj) or self.needs_recompile(obj, src): | ||||
|                     compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) | ||||
|                     compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)  # type: ignore[attr-defined] | ||||
| 
 | ||||
|             try: | ||||
|                 # Importing .synchronize checks for platforms that have some multiprocessing | ||||
|  | @ -466,14 +478,9 @@ class ParallelCompile(object): | |||
|                     threads = 1 | ||||
| 
 | ||||
|             if threads > 1: | ||||
|                 pool = ThreadPool(threads) | ||||
|                 # In Python 2, ThreadPool can't be used as a context manager. | ||||
|                 # Once we are no longer supporting it, this can be 'with pool:' | ||||
|                 try: | ||||
|                 with ThreadPool(threads) as pool: | ||||
|                     for _ in pool.imap_unordered(_single_compile, objects): | ||||
|                         pass | ||||
|                 finally: | ||||
|                     pool.terminate() | ||||
|             else: | ||||
|                 for ob in objects: | ||||
|                     _single_compile(ob) | ||||
|  | @ -482,13 +489,16 @@ class ParallelCompile(object): | |||
| 
 | ||||
|         return compile_function | ||||
| 
 | ||||
|     def install(self): | ||||
|         distutils.ccompiler.CCompiler.compile = self.function() | ||||
|     def install(self: S) -> S: | ||||
|         """ | ||||
|         Installs the compile function into distutils.ccompiler.CCompiler.compile. | ||||
|         """ | ||||
|         distutils.ccompiler.CCompiler.compile = self.function()  # type: ignore[assignment] | ||||
|         return self | ||||
| 
 | ||||
|     def __enter__(self): | ||||
|     def __enter__(self: S) -> S: | ||||
|         self._old.append(distutils.ccompiler.CCompiler.compile) | ||||
|         return self.install() | ||||
| 
 | ||||
|     def __exit__(self, *args): | ||||
|         distutils.ccompiler.CCompiler.compile = self._old.pop() | ||||
|     def __exit__(self, *args: Any) -> None: | ||||
|         distutils.ccompiler.CCompiler.compile = self._old.pop()  # type: ignore[assignment] | ||||
|  |  | |||
|  | @ -1,63 +0,0 @@ | |||
| # IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / | ||||
| # pre-commit). | ||||
| 
 | ||||
| import contextlib | ||||
| import distutils.ccompiler | ||||
| from distutils.command.build_ext import build_ext as _build_ext  # type: ignore | ||||
| from distutils.extension import Extension as _Extension | ||||
| from types import TracebackType | ||||
| from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union | ||||
| 
 | ||||
| WIN: bool | ||||
| PY2: bool | ||||
| MACOS: bool | ||||
| STD_TMPL: str | ||||
| 
 | ||||
| class Pybind11Extension(_Extension): | ||||
|     def _add_cflags(self, *flags: str) -> None: ... | ||||
|     def _add_lflags(self, *flags: str) -> None: ... | ||||
|     def __init__( | ||||
|         self, *args: Any, cxx_std: int = 0, language: str = "c++", **kwargs: Any | ||||
|     ) -> None: ... | ||||
|     @property | ||||
|     def cxx_std(self) -> int: ... | ||||
|     @cxx_std.setter | ||||
|     def cxx_std(self, level: int) -> None: ... | ||||
| 
 | ||||
| @contextlib.contextmanager | ||||
| def tmp_chdir() -> Iterator[str]: ... | ||||
| def has_flag(compiler: distutils.ccompiler.CCompiler, flag: str) -> bool: ... | ||||
| def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]: ... | ||||
| 
 | ||||
| class build_ext(_build_ext):  # type: ignore | ||||
|     def build_extensions(self) -> None: ... | ||||
| 
 | ||||
| def intree_extensions( | ||||
|     paths: Iterator[str], package_dir: Optional[Dict[str, str]] = None | ||||
| ) -> List[Pybind11Extension]: ... | ||||
| def no_recompile(obj: str, src: str) -> bool: ... | ||||
| def naive_recompile(obj: str, src: str) -> bool: ... | ||||
| 
 | ||||
| T = TypeVar("T", bound="ParallelCompile") | ||||
| 
 | ||||
| class ParallelCompile: | ||||
|     envvar: Optional[str] | ||||
|     default: int | ||||
|     max: int | ||||
|     needs_recompile: Callable[[str, str], bool] | ||||
|     def __init__( | ||||
|         self, | ||||
|         envvar: Optional[str] = None, | ||||
|         default: int = 0, | ||||
|         max: int = 0, | ||||
|         needs_recompile: Callable[[str, str], bool] = no_recompile, | ||||
|     ) -> None: ... | ||||
|     def function(self) -> Any: ... | ||||
|     def install(self: T) -> T: ... | ||||
|     def __enter__(self: T) -> T: ... | ||||
|     def __exit__( | ||||
|         self, | ||||
|         exc_type: Optional[Type[BaseException]], | ||||
|         exc_value: Optional[BaseException], | ||||
|         traceback: Optional[TracebackType], | ||||
|     ) -> None: ... | ||||
|  | @ -1,5 +1,5 @@ | |||
| [build-system] | ||||
| requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"] | ||||
| requires = ["setuptools>=42", "cmake>=3.18", "ninja"] | ||||
| build-backend = "setuptools.build_meta" | ||||
| 
 | ||||
| [tool.check-manifest] | ||||
|  | @ -22,20 +22,40 @@ known_first_party = "env,pybind11_cross_module_tests,pybind11_tests," | |||
| profile = "black" | ||||
| 
 | ||||
| [tool.mypy] | ||||
| files = "pybind11" | ||||
| python_version = "2.7" | ||||
| warn_unused_configs = true | ||||
| files = ["pybind11"] | ||||
| python_version = "3.6" | ||||
| strict = true | ||||
| show_error_codes = true | ||||
| enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] | ||||
| warn_unreachable = true | ||||
| 
 | ||||
| disallow_any_generics = true | ||||
| disallow_subclassing_any = true | ||||
| disallow_untyped_calls = true | ||||
| disallow_untyped_defs = true | ||||
| disallow_incomplete_defs = true | ||||
| check_untyped_defs = true | ||||
| disallow_untyped_decorators = true | ||||
| no_implicit_optional = true | ||||
| warn_redundant_casts = true | ||||
| warn_unused_ignores = true | ||||
| warn_return_any = true | ||||
| no_implicit_reexport = true | ||||
| strict_equality = true | ||||
| [[tool.mypy.overrides]] | ||||
| module = ["ghapi.*", "setuptools.*"] | ||||
| ignore_missing_imports = true | ||||
| 
 | ||||
| 
 | ||||
| [tool.pytest.ini_options] | ||||
| minversion = "6.0" | ||||
| addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] | ||||
| xfail_strict = true | ||||
| filterwarnings = ["error"] | ||||
| log_cli_level = "info" | ||||
| testpaths = [ | ||||
|     "tests", | ||||
| ] | ||||
| timeout=300 | ||||
| 
 | ||||
| 
 | ||||
| [tool.pylint] | ||||
| master.py-version = "3.6" | ||||
| reports.output-format = "colorized" | ||||
| messages_control.disable = [ | ||||
|   "design", | ||||
|   "fixme", | ||||
|   "imports", | ||||
|   "line-too-long", | ||||
|   "imports", | ||||
|   "invalid-name", | ||||
|   "protected-access", | ||||
|   "missing-module-docstring", | ||||
| ] | ||||
|  |  | |||
|  | @ -13,14 +13,13 @@ classifiers = | |||
|     Topic :: Software Development :: Libraries :: Python Modules | ||||
|     Topic :: Utilities | ||||
|     Programming Language :: C++ | ||||
|     Programming Language :: Python :: 2.7 | ||||
|     Programming Language :: Python :: 3 | ||||
|     Programming Language :: Python :: 3.5 | ||||
|     Programming Language :: Python :: 3 :: Only | ||||
|     Programming Language :: Python :: 3.6 | ||||
|     Programming Language :: Python :: 3.7 | ||||
|     Programming Language :: Python :: 3.8 | ||||
|     Programming Language :: Python :: 3.9 | ||||
|     Programming Language :: Python :: 3.10 | ||||
|     Programming Language :: Python :: 3.11 | ||||
|     License :: OSI Approved :: BSD License | ||||
|     Programming Language :: Python :: Implementation :: PyPy | ||||
|     Programming Language :: Python :: Implementation :: CPython | ||||
|  | @ -39,25 +38,13 @@ project_urls = | |||
|     Chat = https://gitter.im/pybind/Lobby | ||||
| 
 | ||||
| [options] | ||||
| python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* | ||||
| python_requires = >=3.6 | ||||
| zip_safe = False | ||||
| 
 | ||||
| [bdist_wheel] | ||||
| universal=1 | ||||
| 
 | ||||
| 
 | ||||
| [flake8] | ||||
| max-line-length = 99 | ||||
| max-line-length = 120 | ||||
| show_source = True | ||||
| exclude = .git, __pycache__, build, dist, docs, tools, venv | ||||
| ignore = | ||||
|     # required for pretty matrix formatting: multiple spaces after `,` and `[` | ||||
|     E201, E241, W504, | ||||
|     # camelcase 'cPickle' imported as lowercase 'pickle' | ||||
|     N813 | ||||
|     # Black conflict | ||||
|     W503, E203 | ||||
| 
 | ||||
| 
 | ||||
| [tool:pytest] | ||||
| timeout = 300 | ||||
| extend-ignore = E203, E722, B950 | ||||
| extend-select = B9 | ||||
|  |  | |||
|  | @ -1,36 +1,36 @@ | |||
| #!/usr/bin/env python | ||||
| # -*- coding: utf-8 -*- | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Setup script for PyPI; use CMakeFile.txt to build extension modules | ||||
| 
 | ||||
| import contextlib | ||||
| import io | ||||
| import os | ||||
| import re | ||||
| import shutil | ||||
| import string | ||||
| import subprocess | ||||
| import sys | ||||
| import tempfile | ||||
| from pathlib import Path | ||||
| from tempfile import TemporaryDirectory | ||||
| from typing import Dict, Iterator, List, Union | ||||
| 
 | ||||
| import setuptools.command.sdist | ||||
| 
 | ||||
| DIR = os.path.abspath(os.path.dirname(__file__)) | ||||
| DIR = Path(__file__).parent.absolute() | ||||
| VERSION_REGEX = re.compile( | ||||
|     r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE | ||||
| ) | ||||
| VERSION_FILE = Path("pybind11/_version.py") | ||||
| COMMON_FILE = Path("include/pybind11/detail/common.h") | ||||
| 
 | ||||
| 
 | ||||
| def build_expected_version_hex(matches): | ||||
| def build_expected_version_hex(matches: Dict[str, str]) -> str: | ||||
|     patch_level_serial = matches["PATCH"] | ||||
|     serial = None | ||||
|     try: | ||||
|     major = int(matches["MAJOR"]) | ||||
|     minor = int(matches["MINOR"]) | ||||
|     flds = patch_level_serial.split(".") | ||||
|     if flds: | ||||
|         patch = int(flds[0]) | ||||
|             level = None | ||||
|         if len(flds) == 1: | ||||
|             level = "0" | ||||
|             serial = 0 | ||||
|  | @ -40,14 +40,11 @@ def build_expected_version_hex(matches): | |||
|                 if level_serial.startswith(level): | ||||
|                     serial = int(level_serial[len(level) :]) | ||||
|                     break | ||||
|     except ValueError: | ||||
|         pass | ||||
|     if serial is None: | ||||
|         msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial) | ||||
|         msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"' | ||||
|         raise RuntimeError(msg) | ||||
|     return "0x{:02x}{:02x}{:02x}{}{:x}".format( | ||||
|         major, minor, patch, level[:1].upper(), serial | ||||
|     ) | ||||
|     version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}" | ||||
|     return f"0x{version_hex_str.upper()}" | ||||
| 
 | ||||
| 
 | ||||
| # PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers | ||||
|  | @ -55,82 +52,67 @@ def build_expected_version_hex(matches): | |||
| 
 | ||||
| global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False) | ||||
| 
 | ||||
| setup_py = "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in" | ||||
| setup_py = Path( | ||||
|     "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in" | ||||
| ) | ||||
| extra_cmd = 'cmdclass["sdist"] = SDist\n' | ||||
| 
 | ||||
| to_src = ( | ||||
|     ("pyproject.toml", "tools/pyproject.toml"), | ||||
|     ("setup.py", setup_py), | ||||
|     (Path("pyproject.toml"), Path("tools/pyproject.toml")), | ||||
|     (Path("setup.py"), setup_py), | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| # Read the listed version | ||||
| with open("pybind11/_version.py") as f: | ||||
|     code = compile(f.read(), "pybind11/_version.py", "exec") | ||||
| loc = {} | ||||
| loc: Dict[str, str] = {} | ||||
| code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec") | ||||
| exec(code, loc) | ||||
| version = loc["__version__"] | ||||
| 
 | ||||
| # Verify that the version matches the one in C++ | ||||
| with io.open("include/pybind11/detail/common.h", encoding="utf8") as f: | ||||
|     matches = dict(VERSION_REGEX.findall(f.read())) | ||||
| matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8"))) | ||||
| cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches) | ||||
| if version != cpp_version: | ||||
|     msg = "Python version {} does not match C++ version {}!".format( | ||||
|         version, cpp_version | ||||
|     ) | ||||
|     msg = f"Python version {version} does not match C++ version {cpp_version}!" | ||||
|     raise RuntimeError(msg) | ||||
| 
 | ||||
| version_hex = matches.get("HEX", "MISSING") | ||||
| expected_version_hex = build_expected_version_hex(matches) | ||||
| if version_hex != expected_version_hex: | ||||
|     msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format( | ||||
|         version_hex, | ||||
|         expected_version_hex, | ||||
|     ) | ||||
| exp_version_hex = build_expected_version_hex(matches) | ||||
| if version_hex != exp_version_hex: | ||||
|     msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!" | ||||
|     raise RuntimeError(msg) | ||||
| 
 | ||||
| 
 | ||||
| def get_and_replace(filename, binary=False, **opts): | ||||
|     with open(filename, "rb" if binary else "r") as f: | ||||
|         contents = f.read() | ||||
|     # Replacement has to be done on text in Python 3 (both work in Python 2) | ||||
| # TODO: use literals & overload (typing extensions or Python 3.8) | ||||
| def get_and_replace( | ||||
|     filename: Path, binary: bool = False, **opts: str | ||||
| ) -> Union[bytes, str]: | ||||
|     if binary: | ||||
|         contents = filename.read_bytes() | ||||
|         return string.Template(contents.decode()).substitute(opts).encode() | ||||
|     else: | ||||
|         return string.Template(contents).substitute(opts) | ||||
| 
 | ||||
|     return string.Template(filename.read_text()).substitute(opts) | ||||
| 
 | ||||
| 
 | ||||
| # Use our input files instead when making the SDist (and anything that depends | ||||
| # on it, like a wheel) | ||||
| class SDist(setuptools.command.sdist.sdist): | ||||
|     def make_release_tree(self, base_dir, files): | ||||
|         setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files) | ||||
| class SDist(setuptools.command.sdist.sdist):  # type: ignore[misc] | ||||
|     def make_release_tree(self, base_dir: str, files: List[str]) -> None: | ||||
|         super().make_release_tree(base_dir, files) | ||||
| 
 | ||||
|         for to, src in to_src: | ||||
|             txt = get_and_replace(src, binary=True, version=version, extra_cmd="") | ||||
| 
 | ||||
|             dest = os.path.join(base_dir, to) | ||||
|             dest = Path(base_dir) / to | ||||
| 
 | ||||
|             # This is normally linked, so unlink before writing! | ||||
|             os.unlink(dest) | ||||
|             with open(dest, "wb") as f: | ||||
|                 f.write(txt) | ||||
| 
 | ||||
| 
 | ||||
| # Backport from Python 3 | ||||
| @contextlib.contextmanager | ||||
| def TemporaryDirectory():  # noqa: N802 | ||||
|     "Prepare a temporary directory, cleanup when done" | ||||
|     try: | ||||
|         tmpdir = tempfile.mkdtemp() | ||||
|         yield tmpdir | ||||
|     finally: | ||||
|         shutil.rmtree(tmpdir) | ||||
|             dest.unlink() | ||||
|             dest.write_bytes(txt)  # type: ignore[arg-type] | ||||
| 
 | ||||
| 
 | ||||
| # Remove the CMake install directory when done | ||||
| @contextlib.contextmanager | ||||
| def remove_output(*sources): | ||||
| def remove_output(*sources: str) -> Iterator[None]: | ||||
|     try: | ||||
|         yield | ||||
|     finally: | ||||
|  | @ -153,9 +135,14 @@ with remove_output("pybind11/include", "pybind11/share"): | |||
|                 if "DCMAKE_INSTALL_PREFIX" not in c | ||||
|             ] | ||||
|             cmd += fcommand | ||||
|         cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) | ||||
|         subprocess.check_call(cmd, **cmake_opts) | ||||
|         subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts) | ||||
|         subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) | ||||
|         subprocess.run( | ||||
|             ["cmake", "--install", tmpdir], | ||||
|             check=True, | ||||
|             cwd=DIR, | ||||
|             stdout=sys.stdout, | ||||
|             stderr=sys.stderr, | ||||
|         ) | ||||
| 
 | ||||
|     txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd) | ||||
|     code = compile(txt, setup_py, "exec") | ||||
|  |  | |||
|  | @ -179,11 +179,6 @@ if(PYBIND11_TEST_FILTER) | |||
|   pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER}) | ||||
| endif() | ||||
| 
 | ||||
| if(PYTHON_VERSION VERSION_LESS 3.5) | ||||
|   pybind11_filter_tests(PYBIND11_TEST_FILES test_async.cpp MESSAGE | ||||
|                         "Skipping test_async on Python 2") | ||||
| endif() | ||||
| 
 | ||||
| # Skip tests for CUDA check: | ||||
| # /pybind11/tests/test_constants_and_functions.cpp(125): | ||||
| #   error: incompatible exception specifications | ||||
|  | @ -220,6 +215,7 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_ | |||
|                     "pybind11_cross_module_tests") | ||||
| 
 | ||||
| # And add additional targets for other tests. | ||||
| tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set") | ||||
| tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils") | ||||
| 
 | ||||
| set(PYBIND11_EIGEN_REPO | ||||
|  | @ -356,7 +352,7 @@ endif() | |||
| # Compile with compiler warnings turned on | ||||
| function(pybind11_enable_warnings target_name) | ||||
|   if(MSVC) | ||||
|     target_compile_options(${target_name} PRIVATE /W4) | ||||
|     target_compile_options(${target_name} PRIVATE /W4 /wd4189) | ||||
|   elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS) | ||||
|     target_compile_options( | ||||
|       ${target_name} | ||||
|  | @ -388,17 +384,6 @@ function(pybind11_enable_warnings target_name) | |||
|           -diag-disable 11074,11076) | ||||
|     endif() | ||||
|   endif() | ||||
| 
 | ||||
|   # Needs to be re-added since the ordering requires these to be after the ones above | ||||
|   if(CMAKE_CXX_STANDARD | ||||
|      AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" | ||||
|      AND PYTHON_VERSION VERSION_LESS 3.0) | ||||
|     if(CMAKE_CXX_STANDARD LESS 17) | ||||
|       target_compile_options(${target_name} PUBLIC -Wno-deprecated-register) | ||||
|     else() | ||||
|       target_compile_options(${target_name} PUBLIC -Wno-register) | ||||
|     endif() | ||||
|   endif() | ||||
| endfunction() | ||||
| 
 | ||||
| set(test_targets pybind11_tests) | ||||
|  |  | |||
|  | @ -1,8 +1,7 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| """pytest configuration | ||||
| 
 | ||||
| Extends output capture as needed by pybind11: ignore constructors, optional unordered lines. | ||||
| Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences. | ||||
| Adds docstring and exceptions message sanitizers. | ||||
| """ | ||||
| 
 | ||||
| import contextlib | ||||
|  | @ -13,19 +12,14 @@ import textwrap | |||
| 
 | ||||
| import pytest | ||||
| 
 | ||||
| import env | ||||
| 
 | ||||
| # Early diagnostic for failed imports | ||||
| import pybind11_tests  # noqa: F401 | ||||
| import pybind11_tests | ||||
| 
 | ||||
| _unicode_marker = re.compile(r"u(\'[^\']*\')") | ||||
| _long_marker = re.compile(r"([0-9])L") | ||||
| _hexadecimal = re.compile(r"0x[0-9a-fA-F]+") | ||||
| 
 | ||||
| # Avoid collecting Python3 only files | ||||
| collect_ignore = [] | ||||
| if env.PY2: | ||||
|     collect_ignore.append("test_async.py") | ||||
| 
 | ||||
| 
 | ||||
| def _strip_and_dedent(s): | ||||
|  | @ -45,7 +39,7 @@ def _make_explanation(a, b): | |||
|     ] | ||||
| 
 | ||||
| 
 | ||||
| class Output(object): | ||||
| class Output: | ||||
|     """Basic output post-processing and comparison""" | ||||
| 
 | ||||
|     def __init__(self, string): | ||||
|  | @ -83,7 +77,7 @@ class Unordered(Output): | |||
|             return False | ||||
| 
 | ||||
| 
 | ||||
| class Capture(object): | ||||
| class Capture: | ||||
|     def __init__(self, capfd): | ||||
|         self.capfd = capfd | ||||
|         self.out = "" | ||||
|  | @ -126,7 +120,7 @@ def capture(capsys): | |||
|     return Capture(capsys) | ||||
| 
 | ||||
| 
 | ||||
| class SanitizedString(object): | ||||
| class SanitizedString: | ||||
|     def __init__(self, sanitizer): | ||||
|         self.sanitizer = sanitizer | ||||
|         self.string = "" | ||||
|  | @ -149,9 +143,7 @@ class SanitizedString(object): | |||
| def _sanitize_general(s): | ||||
|     s = s.strip() | ||||
|     s = s.replace("pybind11_tests.", "m.") | ||||
|     s = s.replace("unicode", "str") | ||||
|     s = _long_marker.sub(r"\1", s) | ||||
|     s = _unicode_marker.sub(r"\1", s) | ||||
|     return s | ||||
| 
 | ||||
| 
 | ||||
|  | @ -206,3 +198,16 @@ def gc_collect(): | |||
| def pytest_configure(): | ||||
|     pytest.suppress = suppress | ||||
|     pytest.gc_collect = gc_collect | ||||
| 
 | ||||
| 
 | ||||
| def pytest_report_header(config): | ||||
|     del config  # Unused. | ||||
|     assert ( | ||||
|         pybind11_tests.compiler_info is not None | ||||
|     ), "Please update pybind11_tests.cpp if this assert fails." | ||||
|     return ( | ||||
|         "C++ Info:" | ||||
|         f" {pybind11_tests.compiler_info}" | ||||
|         f" {pybind11_tests.cpp_std}" | ||||
|         f" {pybind11_tests.PYBIND11_INTERNALS_ID}" | ||||
|     ) | ||||
|  |  | |||
|  | @ -56,7 +56,8 @@ from the ConstructorStats instance `.values()` method. | |||
| In some cases, when you need to track instances of a C++ class not registered with pybind11, you | ||||
| need to add a function returning the ConstructorStats for the C++ class; this can be done with: | ||||
| 
 | ||||
|     m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>, py::return_value_policy::reference) | ||||
|     m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>, | ||||
| py::return_value_policy::reference) | ||||
| 
 | ||||
| Finally, you can suppress the output messages, but keep the constructor tracking (for | ||||
| inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g. | ||||
|  | @ -65,15 +66,18 @@ inspection/testing in python) by using the functions with `print_` replaced with | |||
| */ | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| #include <list> | ||||
| #include <typeindex> | ||||
| #include <sstream> | ||||
| #include <typeindex> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| class ConstructorStats { | ||||
| protected: | ||||
|     std::unordered_map<void*, int> _instances; // Need a map rather than set because members can shared address with parents
 | ||||
|     std::list<std::string> _values; // Used to track values (e.g. of value constructors)
 | ||||
|     std::unordered_map<void *, int> _instances; // Need a map rather than set because members can
 | ||||
|                                                 // shared address with parents
 | ||||
|     std::list<std::string> _values;             // Used to track values
 | ||||
|                                                 // (e.g. of value constructors)
 | ||||
| public: | ||||
|     int default_constructions = 0; | ||||
|     int copy_constructions = 0; | ||||
|  | @ -96,26 +100,26 @@ public: | |||
|         default_constructions++; | ||||
|     } | ||||
| 
 | ||||
|     void created(void *inst) { | ||||
|         ++_instances[inst]; | ||||
|     } | ||||
|     void created(void *inst) { ++_instances[inst]; } | ||||
| 
 | ||||
|     void destroyed(void *inst) { | ||||
|         if (--_instances[inst] < 0) | ||||
|         if (--_instances[inst] < 0) { | ||||
|             throw std::runtime_error("cstats.destroyed() called with unknown " | ||||
|                                      "instance; potential double-destruction " | ||||
|                                      "or a missing cstats.created()"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     static void gc() { | ||||
|         // Force garbage collection to ensure any pending destructors are invoked:
 | ||||
| #if defined(PYPY_VERSION) | ||||
|         PyObject *globals = PyEval_GetGlobals(); | ||||
|         PyObject *result = PyRun_String( | ||||
|             "import gc\n" | ||||
|         PyObject *result = PyRun_String("import gc\n" | ||||
|                                         "for i in range(2):" | ||||
|                                         "    gc.collect()\n", | ||||
|             Py_file_input, globals, globals); | ||||
|                                         Py_file_input, | ||||
|                                         globals, | ||||
|                                         globals); | ||||
|         if (result == nullptr) | ||||
|             throw py::error_already_set(); | ||||
|         Py_DECREF(result); | ||||
|  | @ -127,15 +131,18 @@ public: | |||
|     int alive() { | ||||
|         gc(); | ||||
|         int total = 0; | ||||
|         for (const auto &p : _instances) | ||||
|             if (p.second > 0) | ||||
|         for (const auto &p : _instances) { | ||||
|             if (p.second > 0) { | ||||
|                 total += p.second; | ||||
|             } | ||||
|         } | ||||
|         return total; | ||||
|     } | ||||
| 
 | ||||
|     void value() {} // Recursion terminator
 | ||||
|     // Takes one or more values, converts them to strings, then stores them.
 | ||||
|     template <typename T, typename... Tmore> void value(const T &v, Tmore &&...args) { | ||||
|     template <typename T, typename... Tmore> | ||||
|     void value(const T &v, Tmore &&...args) { | ||||
|         std::ostringstream oss; | ||||
|         oss << v; | ||||
|         _values.push_back(oss.str()); | ||||
|  | @ -145,7 +152,9 @@ public: | |||
|     // Move out stored values
 | ||||
|     py::list values() { | ||||
|         py::list l; | ||||
|         for (const auto &v : _values) l.append(py::cast(v)); | ||||
|         for (const auto &v : _values) { | ||||
|             l.append(py::cast(v)); | ||||
|         } | ||||
|         _values.clear(); | ||||
|         return l; | ||||
|     } | ||||
|  | @ -157,7 +166,8 @@ public: | |||
|     } | ||||
| 
 | ||||
|     // Gets constructor stats from a C++ type
 | ||||
|     template <typename T> static ConstructorStats& get() { | ||||
|     template <typename T> | ||||
|     static ConstructorStats &get() { | ||||
| #if defined(PYPY_VERSION) | ||||
|         gc(); | ||||
| #endif | ||||
|  | @ -169,7 +179,8 @@ public: | |||
|         auto &internals = py::detail::get_internals(); | ||||
|         const std::type_index *t1 = nullptr, *t2 = nullptr; | ||||
|         try { | ||||
|             auto *type_info = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0); | ||||
|             auto *type_info | ||||
|                 = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0); | ||||
|             for (auto &p : internals.registered_types_cpp) { | ||||
|                 if (p.second == type_info) { | ||||
|                     if (t1) { | ||||
|  | @ -179,17 +190,23 @@ public: | |||
|                     t1 = &p.first; | ||||
|                 } | ||||
|             } | ||||
|         } catch (const std::out_of_range &) { | ||||
|         } | ||||
|         if (!t1) { | ||||
|             throw std::runtime_error("Unknown class passed to ConstructorStats::get()"); | ||||
|         } | ||||
|         catch (const std::out_of_range&) {} | ||||
|         if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()"); | ||||
|         auto &cs1 = get(*t1); | ||||
|         // If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
 | ||||
|         // has more constructions (typically one or the other will be 0)
 | ||||
|         // If we have both a t1 and t2 match, one is probably the trampoline class; return
 | ||||
|         // whichever has more constructions (typically one or the other will be 0)
 | ||||
|         if (t2) { | ||||
|             auto &cs2 = get(*t2); | ||||
|             int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size(); | ||||
|             int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size(); | ||||
|             if (cs2_total > cs1_total) return cs2; | ||||
|             int cs1_total = cs1.default_constructions + cs1.copy_constructions | ||||
|                             + cs1.move_constructions + (int) cs1._values.size(); | ||||
|             int cs2_total = cs2.default_constructions + cs2.copy_constructions | ||||
|                             + cs2.move_constructions + (int) cs2._values.size(); | ||||
|             if (cs2_total > cs1_total) { | ||||
|                 return cs2; | ||||
|             } | ||||
|         } | ||||
|         return cs1; | ||||
|     } | ||||
|  | @ -198,78 +215,108 @@ public: | |||
| // To track construction/destruction, you need to call these methods from the various
 | ||||
| // constructors/operators.  The ones that take extra values record the given values in the
 | ||||
| // constructor stats values for later inspection.
 | ||||
| template <class T> void track_copy_created(T *inst) { ConstructorStats::get<T>().copy_created(inst); } | ||||
| template <class T> void track_move_created(T *inst) { ConstructorStats::get<T>().move_created(inst); } | ||||
| template <class T, typename... Values> void track_copy_assigned(T *, Values &&...values) { | ||||
| template <class T> | ||||
| void track_copy_created(T *inst) { | ||||
|     ConstructorStats::get<T>().copy_created(inst); | ||||
| } | ||||
| template <class T> | ||||
| void track_move_created(T *inst) { | ||||
|     ConstructorStats::get<T>().move_created(inst); | ||||
| } | ||||
| template <class T, typename... Values> | ||||
| void track_copy_assigned(T *, Values &&...values) { | ||||
|     auto &cst = ConstructorStats::get<T>(); | ||||
|     cst.copy_assignments++; | ||||
|     cst.value(std::forward<Values>(values)...); | ||||
| } | ||||
| template <class T, typename... Values> void track_move_assigned(T *, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void track_move_assigned(T *, Values &&...values) { | ||||
|     auto &cst = ConstructorStats::get<T>(); | ||||
|     cst.move_assignments++; | ||||
|     cst.value(std::forward<Values>(values)...); | ||||
| } | ||||
| template <class T, typename... Values> void track_default_created(T *inst, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void track_default_created(T *inst, Values &&...values) { | ||||
|     auto &cst = ConstructorStats::get<T>(); | ||||
|     cst.default_created(inst); | ||||
|     cst.value(std::forward<Values>(values)...); | ||||
| } | ||||
| template <class T, typename... Values> void track_created(T *inst, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void track_created(T *inst, Values &&...values) { | ||||
|     auto &cst = ConstructorStats::get<T>(); | ||||
|     cst.created(inst); | ||||
|     cst.value(std::forward<Values>(values)...); | ||||
| } | ||||
| template <class T, typename... Values> void track_destroyed(T *inst) { | ||||
| template <class T, typename... Values> | ||||
| void track_destroyed(T *inst) { | ||||
|     ConstructorStats::get<T>().destroyed(inst); | ||||
| } | ||||
| template <class T, typename... Values> void track_values(T *, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void track_values(T *, Values &&...values) { | ||||
|     ConstructorStats::get<T>().value(std::forward<Values>(values)...); | ||||
| } | ||||
| 
 | ||||
| /// Don't cast pointers to Python, print them as strings
 | ||||
| inline const char *format_ptrs(const char *p) { return p; } | ||||
| template <typename T> | ||||
| py::str format_ptrs(T *p) { return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p)); } | ||||
| py::str format_ptrs(T *p) { | ||||
|     return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p)); | ||||
| } | ||||
| template <typename T> | ||||
| auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) { return std::forward<T>(x); } | ||||
| auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) { | ||||
|     return std::forward<T>(x); | ||||
| } | ||||
| 
 | ||||
| template <class T, typename... Output> | ||||
| void print_constr_details(T *inst, const std::string &action, Output &&...output) { | ||||
|     py::print("###", py::type_id<T>(), "@", format_ptrs(inst), action, | ||||
|     py::print("###", | ||||
|               py::type_id<T>(), | ||||
|               "@", | ||||
|               format_ptrs(inst), | ||||
|               action, | ||||
|               format_ptrs(std::forward<Output>(output))...); | ||||
| } | ||||
| 
 | ||||
| // Verbose versions of the above:
 | ||||
| template <class T, typename... Values> void print_copy_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
 | ||||
| template <class T, typename... Values> | ||||
| void print_copy_created(T *inst, | ||||
|                         Values &&...values) { // NB: this prints, but doesn't store, given values
 | ||||
|     print_constr_details(inst, "created via copy constructor", values...); | ||||
|     track_copy_created(inst); | ||||
| } | ||||
| template <class T, typename... Values> void print_move_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
 | ||||
| template <class T, typename... Values> | ||||
| void print_move_created(T *inst, | ||||
|                         Values &&...values) { // NB: this prints, but doesn't store, given values
 | ||||
|     print_constr_details(inst, "created via move constructor", values...); | ||||
|     track_move_created(inst); | ||||
| } | ||||
| template <class T, typename... Values> void print_copy_assigned(T *inst, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void print_copy_assigned(T *inst, Values &&...values) { | ||||
|     print_constr_details(inst, "assigned via copy assignment", values...); | ||||
|     track_copy_assigned(inst, values...); | ||||
| } | ||||
| template <class T, typename... Values> void print_move_assigned(T *inst, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void print_move_assigned(T *inst, Values &&...values) { | ||||
|     print_constr_details(inst, "assigned via move assignment", values...); | ||||
|     track_move_assigned(inst, values...); | ||||
| } | ||||
| template <class T, typename... Values> void print_default_created(T *inst, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void print_default_created(T *inst, Values &&...values) { | ||||
|     print_constr_details(inst, "created via default constructor", values...); | ||||
|     track_default_created(inst, values...); | ||||
| } | ||||
| template <class T, typename... Values> void print_created(T *inst, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void print_created(T *inst, Values &&...values) { | ||||
|     print_constr_details(inst, "created", values...); | ||||
|     track_created(inst, values...); | ||||
| } | ||||
| template <class T, typename... Values> void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
 | ||||
| template <class T, typename... Values> | ||||
| void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
 | ||||
|     print_constr_details(inst, "destroyed", values...); | ||||
|     track_destroyed(inst); | ||||
| } | ||||
| template <class T, typename... Values> void print_values(T *inst, Values &&...values) { | ||||
| template <class T, typename... Values> | ||||
| void print_values(T *inst, Values &&...values) { | ||||
|     print_constr_details(inst, ":", values...); | ||||
|     track_values(inst, values...); | ||||
| } | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
|     BSD-style license that can be found in the LICENSE file. | ||||
| */ | ||||
| #include <pybind11/pybind11.h> | ||||
| 
 | ||||
| #include <cstdint> | ||||
| 
 | ||||
| // This file mimics a DSO that makes pybind11 calls but does not define a
 | ||||
|  | @ -24,50 +25,21 @@ void gil_acquire() { py::gil_scoped_acquire gil; } | |||
| 
 | ||||
| constexpr char kModuleName[] = "cross_module_gil_utils"; | ||||
| 
 | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
| struct PyModuleDef moduledef = { | ||||
|     PyModuleDef_HEAD_INIT, | ||||
|     kModuleName, | ||||
|     NULL, | ||||
|     0, | ||||
|     NULL, | ||||
|     NULL, | ||||
|     NULL, | ||||
|     NULL, | ||||
|     NULL | ||||
| }; | ||||
| #else | ||||
| PyMethodDef module_methods[] = { | ||||
|     {NULL, NULL, 0, NULL} | ||||
| }; | ||||
| #endif | ||||
|     PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr}; | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| extern "C" PYBIND11_EXPORT | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
| PyObject* PyInit_cross_module_gil_utils() | ||||
| #else | ||||
| void initcross_module_gil_utils() | ||||
| #endif | ||||
| { | ||||
| extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() { | ||||
| 
 | ||||
|     PyObject* m = | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|         PyModule_Create(&moduledef); | ||||
| #else | ||||
|         Py_InitModule(kModuleName, module_methods); | ||||
| #endif | ||||
|     PyObject *m = PyModule_Create(&moduledef); | ||||
| 
 | ||||
|     if (m != NULL) { | ||||
|         static_assert( | ||||
|             sizeof(&gil_acquire) == sizeof(void*), | ||||
|     if (m != nullptr) { | ||||
|         static_assert(sizeof(&gil_acquire) == sizeof(void *), | ||||
|                       "Function pointer must have the same size as void*"); | ||||
|         PyModule_AddObject(m, "gil_acquire_funcaddr", | ||||
|                            PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire))); | ||||
|         PyModule_AddObject( | ||||
|             m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire))); | ||||
|     } | ||||
| 
 | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|     return m; | ||||
| #endif | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,51 @@ | |||
| /*
 | ||||
|     Copyright (c) 2022 Google LLC | ||||
| 
 | ||||
|     All rights reserved. Use of this source code is governed by a | ||||
|     BSD-style license that can be found in the LICENSE file. | ||||
| */ | ||||
| 
 | ||||
| #include <pybind11/pybind11.h> | ||||
| 
 | ||||
| // This file mimics a DSO that makes pybind11 calls but does not define a PYBIND11_MODULE,
 | ||||
| // so that the first call of cross_module_error_already_set() triggers the first call of
 | ||||
| // pybind11::detail::get_internals().
 | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| namespace py = pybind11; | ||||
| 
 | ||||
| void interleaved_error_already_set() { | ||||
|     PyErr_SetString(PyExc_RuntimeError, "1st error."); | ||||
|     try { | ||||
|         throw py::error_already_set(); | ||||
|     } catch (const py::error_already_set &) { | ||||
|         // The 2nd error could be conditional in a real application.
 | ||||
|         PyErr_SetString(PyExc_RuntimeError, "2nd error."); | ||||
|     } // Here the 1st error is destroyed before the 2nd error is fetched.
 | ||||
|     // The error_already_set dtor triggers a pybind11::detail::get_internals()
 | ||||
|     // call via pybind11::gil_scoped_acquire.
 | ||||
|     if (PyErr_Occurred()) { | ||||
|         throw py::error_already_set(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| constexpr char kModuleName[] = "cross_module_interleaved_error_already_set"; | ||||
| 
 | ||||
| struct PyModuleDef moduledef = { | ||||
|     PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr}; | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_already_set() { | ||||
|     PyObject *m = PyModule_Create(&moduledef); | ||||
|     if (m != nullptr) { | ||||
|         static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *), | ||||
|                       "Function pointer must have the same size as void *"); | ||||
|         PyModule_AddObject( | ||||
|             m, | ||||
|             "funcaddr", | ||||
|             PyLong_FromVoidPtr(reinterpret_cast<void *>(&interleaved_error_already_set))); | ||||
|     } | ||||
|     return m; | ||||
| } | ||||
|  | @ -1,4 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import platform | ||||
| import sys | ||||
| 
 | ||||
|  | @ -11,10 +10,6 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") | |||
| CPYTHON = platform.python_implementation() == "CPython" | ||||
| PYPY = platform.python_implementation() == "PyPy" | ||||
| 
 | ||||
| PY2 = sys.version_info.major == 2 | ||||
| 
 | ||||
| PY = sys.version_info | ||||
| 
 | ||||
| 
 | ||||
| def deprecated_call(): | ||||
|     """ | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import contextlib | ||||
| import os | ||||
| import string | ||||
|  | @ -64,11 +63,9 @@ py_files = { | |||
|     "__init__.py", | ||||
|     "__main__.py", | ||||
|     "_version.py", | ||||
|     "_version.pyi", | ||||
|     "commands.py", | ||||
|     "py.typed", | ||||
|     "setup_helpers.py", | ||||
|     "setup_helpers.pyi", | ||||
| } | ||||
| 
 | ||||
| headers = main_headers | detail_headers | stl_headers | ||||
|  | @ -111,19 +108,19 @@ def test_build_sdist(monkeypatch, tmpdir): | |||
|     out = subprocess.check_output( | ||||
|         [ | ||||
|             sys.executable, | ||||
|             "setup.py", | ||||
|             "sdist", | ||||
|             "--formats=tar", | ||||
|             "--dist-dir", | ||||
|             "-m", | ||||
|             "build", | ||||
|             "--sdist", | ||||
|             "--outdir", | ||||
|             str(tmpdir), | ||||
|         ] | ||||
|     ) | ||||
|     if hasattr(out, "decode"): | ||||
|         out = out.decode() | ||||
| 
 | ||||
|     (sdist,) = tmpdir.visit("*.tar") | ||||
|     (sdist,) = tmpdir.visit("*.tar.gz") | ||||
| 
 | ||||
|     with tarfile.open(str(sdist)) as tar: | ||||
|     with tarfile.open(str(sdist), "r:gz") as tar: | ||||
|         start = tar.getnames()[0] + "/" | ||||
|         version = start[9:-1] | ||||
|         simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} | ||||
|  | @ -148,9 +145,9 @@ def test_build_sdist(monkeypatch, tmpdir): | |||
|             contents = f.read().decode("utf8") | ||||
|         assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents | ||||
| 
 | ||||
|     files = {"pybind11/{}".format(n) for n in all_files} | ||||
|     files = {f"pybind11/{n}" for n in all_files} | ||||
|     files |= sdist_files | ||||
|     files |= {"pybind11{}".format(n) for n in local_sdist_files} | ||||
|     files |= {f"pybind11{n}" for n in local_sdist_files} | ||||
|     files.add("pybind11.egg-info/entry_points.txt") | ||||
|     files.add("pybind11.egg-info/requires.txt") | ||||
|     assert simpler == files | ||||
|  | @ -172,23 +169,23 @@ def test_build_global_dist(monkeypatch, tmpdir): | |||
| 
 | ||||
|     monkeypatch.chdir(MAIN_DIR) | ||||
|     monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1") | ||||
| 
 | ||||
|     out = subprocess.check_output( | ||||
|         [ | ||||
|             sys.executable, | ||||
|             "setup.py", | ||||
|             "sdist", | ||||
|             "--formats=tar", | ||||
|             "--dist-dir", | ||||
|             "-m", | ||||
|             "build", | ||||
|             "--sdist", | ||||
|             "--outdir", | ||||
|             str(tmpdir), | ||||
|         ] | ||||
|     ) | ||||
| 
 | ||||
|     if hasattr(out, "decode"): | ||||
|         out = out.decode() | ||||
| 
 | ||||
|     (sdist,) = tmpdir.visit("*.tar") | ||||
|     (sdist,) = tmpdir.visit("*.tar.gz") | ||||
| 
 | ||||
|     with tarfile.open(str(sdist)) as tar: | ||||
|     with tarfile.open(str(sdist), "r:gz") as tar: | ||||
|         start = tar.getnames()[0] + "/" | ||||
|         version = start[16:-1] | ||||
|         simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]} | ||||
|  | @ -203,9 +200,9 @@ def test_build_global_dist(monkeypatch, tmpdir): | |||
|         ) as f: | ||||
|             pyproject_toml = f.read() | ||||
| 
 | ||||
|     files = {"pybind11/{}".format(n) for n in all_files} | ||||
|     files = {f"pybind11/{n}" for n in all_files} | ||||
|     files |= sdist_files | ||||
|     files |= {"pybind11_global{}".format(n) for n in local_sdist_files} | ||||
|     files |= {f"pybind11_global{n}" for n in local_sdist_files} | ||||
|     assert simpler == files | ||||
| 
 | ||||
|     with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f: | ||||
|  | @ -230,7 +227,7 @@ def tests_build_wheel(monkeypatch, tmpdir): | |||
| 
 | ||||
|     (wheel,) = tmpdir.visit("*.whl") | ||||
| 
 | ||||
|     files = {"pybind11/{}".format(n) for n in all_files} | ||||
|     files = {f"pybind11/{n}" for n in all_files} | ||||
|     files |= { | ||||
|         "dist-info/LICENSE", | ||||
|         "dist-info/METADATA", | ||||
|  | @ -244,9 +241,7 @@ def tests_build_wheel(monkeypatch, tmpdir): | |||
|         names = z.namelist() | ||||
| 
 | ||||
|     trimmed = {n for n in names if "dist-info" not in n} | ||||
|     trimmed |= { | ||||
|         "dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n | ||||
|     } | ||||
|     trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n} | ||||
|     assert files == trimmed | ||||
| 
 | ||||
| 
 | ||||
|  | @ -260,8 +255,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir): | |||
| 
 | ||||
|     (wheel,) = tmpdir.visit("*.whl") | ||||
| 
 | ||||
|     files = {"data/data/{}".format(n) for n in src_files} | ||||
|     files |= {"data/headers/{}".format(n[8:]) for n in headers} | ||||
|     files = {f"data/data/{n}" for n in src_files} | ||||
|     files |= {f"data/headers/{n[8:]}" for n in headers} | ||||
|     files |= { | ||||
|         "dist-info/LICENSE", | ||||
|         "dist-info/METADATA", | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import os | ||||
| import subprocess | ||||
| import sys | ||||
|  | @ -19,7 +18,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): | |||
| 
 | ||||
|     (tmpdir / "setup.py").write_text( | ||||
|         dedent( | ||||
|             u"""\ | ||||
|             f"""\ | ||||
|             import sys | ||||
|             sys.path.append({MAIN_DIR!r}) | ||||
| 
 | ||||
|  | @ -52,13 +51,13 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): | |||
|                 ext_modules=ext_modules, | ||||
|             ) | ||||
|             """ | ||||
|         ).format(MAIN_DIR=MAIN_DIR, std=std, parallel=parallel), | ||||
|         ), | ||||
|         encoding="ascii", | ||||
|     ) | ||||
| 
 | ||||
|     (tmpdir / "main.cpp").write_text( | ||||
|         dedent( | ||||
|             u"""\ | ||||
|             """\ | ||||
|             #include <pybind11/pybind11.h> | ||||
| 
 | ||||
|             int f(int x) { | ||||
|  | @ -96,7 +95,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): | |||
| 
 | ||||
|     (tmpdir / "test.py").write_text( | ||||
|         dedent( | ||||
|             u"""\ | ||||
|             """\ | ||||
|             import simple_setup | ||||
|             assert simple_setup.f(3) == 9 | ||||
|             """ | ||||
|  | @ -121,10 +120,11 @@ def test_intree_extensions(monkeypatch, tmpdir): | |||
|     subdir.ensure_dir() | ||||
|     src = subdir / "ext.cpp" | ||||
|     src.ensure() | ||||
|     (ext,) = intree_extensions([src.relto(tmpdir)]) | ||||
|     relpath = src.relto(tmpdir) | ||||
|     (ext,) = intree_extensions([relpath]) | ||||
|     assert ext.name == "ext" | ||||
|     subdir.ensure("__init__.py") | ||||
|     (ext,) = intree_extensions([src.relto(tmpdir)]) | ||||
|     (ext,) = intree_extensions([relpath]) | ||||
|     assert ext.name == "dir.ext" | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,10 +1,11 @@ | |||
| #pragma once | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| /// Simple class used to test py::local:
 | ||||
| template <int> class LocalBase { | ||||
| template <int> | ||||
| class LocalBase { | ||||
| public: | ||||
|     explicit LocalBase(int i) : i(i) {} | ||||
|     int i = -1; | ||||
|  | @ -35,12 +36,12 @@ using NonLocalVec2 = std::vector<NonLocal2>; | |||
| using NonLocalMap = std::unordered_map<std::string, NonLocalType>; | ||||
| using NonLocalMap2 = std::unordered_map<std::string, uint8_t>; | ||||
| 
 | ||||
| 
 | ||||
| // Exception that will be caught via the module local translator.
 | ||||
| class LocalException : public std::exception { | ||||
| public: | ||||
|     explicit LocalException(const char *m) : message{m} {} | ||||
|     const char *what() const noexcept override { return message.c_str(); } | ||||
| 
 | ||||
| private: | ||||
|     std::string message = ""; | ||||
| }; | ||||
|  | @ -50,6 +51,7 @@ class LocalSimpleException : public std::exception { | |||
| public: | ||||
|     explicit LocalSimpleException(const char *m) : message{m} {} | ||||
|     const char *what() const noexcept override { return message.c_str(); } | ||||
| 
 | ||||
| private: | ||||
|     std::string message = ""; | ||||
| }; | ||||
|  | @ -62,13 +64,12 @@ PYBIND11_MAKE_OPAQUE(NonLocalVec); | |||
| PYBIND11_MAKE_OPAQUE(NonLocalMap); | ||||
| PYBIND11_MAKE_OPAQUE(NonLocalMap2); | ||||
| 
 | ||||
| 
 | ||||
| // Simple bindings (used with the above):
 | ||||
| template <typename T, int Adjust = 0, typename... Args> | ||||
| py::class_<T> bind_local(Args &&...args) { | ||||
|     return py::class_<T>(std::forward<Args>(args)...) | ||||
|         .def(py::init<int>()) | ||||
|         .def("get", [](T &i) { return i.i + Adjust; }); | ||||
|     return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) { | ||||
|         return i.i + Adjust; | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| // Simulate a foreign library base class (to match the example in the docs):
 | ||||
|  | @ -81,5 +82,11 @@ public: | |||
| }; | ||||
| } // namespace pets
 | ||||
| 
 | ||||
| struct MixGL { int i; explicit MixGL(int i) : i{i} {} }; | ||||
| struct MixGL2 { int i; explicit MixGL2(int i) : i{i} {} }; | ||||
| struct MixGL { | ||||
|     int i; | ||||
|     explicit MixGL(int i) : i{i} {} | ||||
| }; | ||||
| struct MixGL2 { | ||||
|     int i; | ||||
|     explicit MixGL2(int i) : i{i} {} | ||||
| }; | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| #pragma once | ||||
| #if !defined(__OBJECT_H) | ||||
| #    define __OBJECT_H | ||||
| 
 | ||||
| #    include "constructor_stats.h" | ||||
| 
 | ||||
| #    include <atomic> | ||||
| #include "constructor_stats.h" | ||||
| 
 | ||||
| /// Reference counted object base class
 | ||||
| class Object { | ||||
|  | @ -26,18 +28,21 @@ public: | |||
|      */ | ||||
|     void decRef(bool dealloc = true) const { | ||||
|         --m_refCount; | ||||
|         if (m_refCount == 0 && dealloc) | ||||
|         if (m_refCount == 0 && dealloc) { | ||||
|             delete this; | ||||
|         else if (m_refCount < 0) | ||||
|         } else if (m_refCount < 0) { | ||||
|             throw std::runtime_error("Internal error: reference count < 0!"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     virtual std::string toString() const = 0; | ||||
| 
 | ||||
| protected: | ||||
|     /** \brief Virtual protected deconstructor.
 | ||||
|      * (Will only be called by \ref ref) | ||||
|      */ | ||||
|     virtual ~Object() { print_destroyed(this); } | ||||
| 
 | ||||
| private: | ||||
|     mutable std::atomic<int> m_refCount{0}; | ||||
| }; | ||||
|  | @ -58,50 +63,64 @@ class ref_tag {}; | |||
|  * | ||||
|  * \ingroup libcore | ||||
|  */ | ||||
| template <typename T> class ref { | ||||
| template <typename T> | ||||
| class ref { | ||||
| public: | ||||
|     /// Create a nullptr reference
 | ||||
|     ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); } | ||||
|     ref() : m_ptr(nullptr) { | ||||
|         print_default_created(this); | ||||
|         track_default_created((ref_tag *) this); | ||||
|     } | ||||
| 
 | ||||
|     /// Construct a reference from a pointer
 | ||||
|     explicit ref(T *ptr) : m_ptr(ptr) { | ||||
|         if (m_ptr) ((Object *) m_ptr)->incRef(); | ||||
| 
 | ||||
|         print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer"); | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->incRef(); | ||||
|         } | ||||
| 
 | ||||
|         print_created(this, "from pointer", m_ptr); | ||||
|         track_created((ref_tag *) this, "from pointer"); | ||||
|     } | ||||
| 
 | ||||
|     /// Copy constructor
 | ||||
|     ref(const ref &r) : m_ptr(r.m_ptr) { | ||||
|         if (m_ptr) | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->incRef(); | ||||
|         } | ||||
| 
 | ||||
|         print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this); | ||||
|         print_copy_created(this, "with pointer", m_ptr); | ||||
|         track_copy_created((ref_tag *) this); | ||||
|     } | ||||
| 
 | ||||
|     /// Move constructor
 | ||||
|     ref(ref &&r) noexcept : m_ptr(r.m_ptr) { | ||||
|         r.m_ptr = nullptr; | ||||
| 
 | ||||
|         print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this); | ||||
|         print_move_created(this, "with pointer", m_ptr); | ||||
|         track_move_created((ref_tag *) this); | ||||
|     } | ||||
| 
 | ||||
|     /// Destroy this reference
 | ||||
|     ~ref() { | ||||
|         if (m_ptr) | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->decRef(); | ||||
|         } | ||||
| 
 | ||||
|         print_destroyed(this); track_destroyed((ref_tag*) this); | ||||
|         print_destroyed(this); | ||||
|         track_destroyed((ref_tag *) this); | ||||
|     } | ||||
| 
 | ||||
|     /// Move another reference into the current one
 | ||||
|     ref &operator=(ref &&r) noexcept { | ||||
|         print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this); | ||||
|         print_move_assigned(this, "pointer", r.m_ptr); | ||||
|         track_move_assigned((ref_tag *) this); | ||||
| 
 | ||||
|         if (*this == r) | ||||
|         if (*this == r) { | ||||
|             return *this; | ||||
|         if (m_ptr) | ||||
|         } | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->decRef(); | ||||
|         } | ||||
|         m_ptr = r.m_ptr; | ||||
|         r.m_ptr = nullptr; | ||||
|         return *this; | ||||
|  | @ -115,27 +134,34 @@ public: | |||
|         print_copy_assigned(this, "pointer", r.m_ptr); | ||||
|         track_copy_assigned((ref_tag *) this); | ||||
| 
 | ||||
|         if (m_ptr == r.m_ptr) | ||||
|         if (m_ptr == r.m_ptr) { | ||||
|             return *this; | ||||
|         if (m_ptr) | ||||
|         } | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->decRef(); | ||||
|         } | ||||
|         m_ptr = r.m_ptr; | ||||
|         if (m_ptr) | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->incRef(); | ||||
|         } | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     /// Overwrite this reference with a pointer to another object
 | ||||
|     ref &operator=(T *ptr) { | ||||
|         print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer"); | ||||
|         print_values(this, "assigned pointer"); | ||||
|         track_values((ref_tag *) this, "assigned pointer"); | ||||
| 
 | ||||
|         if (m_ptr == ptr) | ||||
|         if (m_ptr == ptr) { | ||||
|             return *this; | ||||
|         if (m_ptr) | ||||
|         } | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->decRef(); | ||||
|         } | ||||
|         m_ptr = ptr; | ||||
|         if (m_ptr) | ||||
|         if (m_ptr) { | ||||
|             ((Object *) m_ptr)->incRef(); | ||||
|         } | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|  | @ -171,6 +197,7 @@ public: | |||
| 
 | ||||
|     /// Return a pointer to the referenced object
 | ||||
|     const T *get_ptr() const { return m_ptr; } | ||||
| 
 | ||||
| private: | ||||
|     T *m_ptr; | ||||
| }; | ||||
|  |  | |||
|  | @ -7,12 +7,12 @@ | |||
|     BSD-style license that can be found in the LICENSE file. | ||||
| */ | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| #include "local_bindings.h" | ||||
| #include "test_exceptions.h" | ||||
| 
 | ||||
| #include <pybind11/stl_bind.h> | ||||
| 
 | ||||
| #include "local_bindings.h" | ||||
| #include "pybind11_tests.h" | ||||
| #include "test_exceptions.h" | ||||
| 
 | ||||
| #include <numeric> | ||||
| #include <utility> | ||||
| 
 | ||||
|  | @ -30,8 +30,14 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { | |||
| 
 | ||||
|     // test_exceptions.py
 | ||||
|     py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException"); | ||||
|     m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); }); | ||||
|     m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); }); | ||||
|     m.def("raise_runtime_error", []() { | ||||
|         PyErr_SetString(PyExc_RuntimeError, "My runtime error"); | ||||
|         throw py::error_already_set(); | ||||
|     }); | ||||
|     m.def("raise_value_error", []() { | ||||
|         PyErr_SetString(PyExc_ValueError, "My value error"); | ||||
|         throw py::error_already_set(); | ||||
|     }); | ||||
|     m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); }); | ||||
|     m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); }); | ||||
|     m.def("throw_stop_iteration", []() { throw py::stop_iteration(); }); | ||||
|  | @ -39,7 +45,9 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { | |||
|     m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); }); | ||||
|     py::register_exception_translator([](std::exception_ptr p) { | ||||
|         try { | ||||
|           if (p) std::rethrow_exception(p); | ||||
|             if (p) { | ||||
|                 std::rethrow_exception(p); | ||||
|             } | ||||
|         } catch (const shared_exception &e) { | ||||
|             PyErr_SetString(PyExc_KeyError, e.what()); | ||||
|         } | ||||
|  | @ -58,9 +66,9 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { | |||
| 
 | ||||
|     // test_local_bindings.py
 | ||||
|     // Local to both:
 | ||||
|     bind_local<LocalType, 1>(m, "LocalType", py::module_local()) | ||||
|         .def("get2", [](LocalType &t) { return t.i + 2; }) | ||||
|         ; | ||||
|     bind_local<LocalType, 1>(m, "LocalType", py::module_local()).def("get2", [](LocalType &t) { | ||||
|         return t.i + 2; | ||||
|     }); | ||||
| 
 | ||||
|     // Can only be called with our python type:
 | ||||
|     m.def("local_value", [](LocalType &l) { return l.i; }); | ||||
|  | @ -68,9 +76,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { | |||
|     // test_nonlocal_failure
 | ||||
|     // This registration will fail (global registration when LocalFail is already registered
 | ||||
|     // globally in the main test module):
 | ||||
|     m.def("register_nonlocal", [m]() { | ||||
|         bind_local<NonLocalType, 0>(m, "NonLocalType"); | ||||
|     }); | ||||
|     m.def("register_nonlocal", [m]() { bind_local<NonLocalType, 0>(m, "NonLocalType"); }); | ||||
| 
 | ||||
|     // test_stl_bind_local
 | ||||
|     // stl_bind.h binders defaults to py::module_local if the types are local or converting:
 | ||||
|  | @ -80,27 +86,21 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { | |||
|     // test_stl_bind_global
 | ||||
|     // and global if the type (or one of the types, for the map) is global (so these will fail,
 | ||||
|     // assuming pybind11_tests is already loaded):
 | ||||
|     m.def("register_nonlocal_vec", [m]() { | ||||
|         py::bind_vector<NonLocalVec>(m, "NonLocalVec"); | ||||
|     }); | ||||
|     m.def("register_nonlocal_map", [m]() { | ||||
|         py::bind_map<NonLocalMap>(m, "NonLocalMap"); | ||||
|     }); | ||||
|     m.def("register_nonlocal_vec", [m]() { py::bind_vector<NonLocalVec>(m, "NonLocalVec"); }); | ||||
|     m.def("register_nonlocal_map", [m]() { py::bind_map<NonLocalMap>(m, "NonLocalMap"); }); | ||||
|     // The default can, however, be overridden to global using `py::module_local()` or
 | ||||
|     // `py::module_local(false)`.
 | ||||
|     // Explicitly made local:
 | ||||
|     py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local()); | ||||
|     // Explicitly made global (and so will fail to bind):
 | ||||
|     m.def("register_nonlocal_map2", [m]() { | ||||
|         py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); | ||||
|     }); | ||||
|     m.def("register_nonlocal_map2", | ||||
|           [m]() { py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); }); | ||||
| 
 | ||||
|     // test_mixed_local_global
 | ||||
|     // We try this both with the global type registered first and vice versa (the order shouldn't
 | ||||
|     // matter).
 | ||||
|     m.def("register_mixed_global_local", [m]() { | ||||
|         bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); | ||||
|     }); | ||||
|     m.def("register_mixed_global_local", | ||||
|           [m]() { bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); }); | ||||
|     m.def("register_mixed_local_global", [m]() { | ||||
|         bind_local<MixedLocalGlobal, 2000>(m, "MixedLocalGlobal", py::module_local(false)); | ||||
|     }); | ||||
|  | @ -108,14 +108,14 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { | |||
|     m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); | ||||
| 
 | ||||
|     // test_internal_locals_differ
 | ||||
|     m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; }); | ||||
|     m.def("local_cpp_types_addr", | ||||
|           []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; }); | ||||
| 
 | ||||
|     // test_stl_caster_vs_stl_bind
 | ||||
|     py::bind_vector<std::vector<int>>(m, "VectorInt"); | ||||
| 
 | ||||
|     m.def("load_vector_via_binding", [](std::vector<int> &v) { | ||||
|         return std::accumulate(v.begin(), v.end(), 0); | ||||
|     }); | ||||
|     m.def("load_vector_via_binding", | ||||
|           [](std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); }); | ||||
| 
 | ||||
|     // test_cross_module_calls
 | ||||
|     m.def("return_self", [](LocalVec *v) { return v; }); | ||||
|  | @ -125,11 +125,9 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { | |||
|     public: | ||||
|         explicit Dog(std::string name) : Pet(std::move(name)) {} | ||||
|     }; | ||||
|     py::class_<pets::Pet>(m, "Pet", py::module_local()) | ||||
|         .def("name", &pets::Pet::name); | ||||
|     py::class_<pets::Pet>(m, "Pet", py::module_local()).def("name", &pets::Pet::name); | ||||
|     // Binding for local extending class:
 | ||||
|     py::class_<Dog, pets::Pet>(m, "Dog") | ||||
|         .def(py::init<std::string>()); | ||||
|     py::class_<Dog, pets::Pet>(m, "Dog").def(py::init<std::string>()); | ||||
|     m.def("pet_name", [](pets::Pet &p) { return p.name(); }); | ||||
| 
 | ||||
|     py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>()); | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| */ | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| 
 | ||||
| #include "constructor_stats.h" | ||||
| 
 | ||||
| #include <functional> | ||||
|  | @ -31,9 +32,7 @@ std::list<std::function<void(py::module_ &)>> &initializers() { | |||
|     return inits; | ||||
| } | ||||
| 
 | ||||
| test_initializer::test_initializer(Initializer init) { | ||||
|     initializers().emplace_back(init); | ||||
| } | ||||
| test_initializer::test_initializer(Initializer init) { initializers().emplace_back(init); } | ||||
| 
 | ||||
| test_initializer::test_initializer(const char *submodule_name, Initializer init) { | ||||
|     initializers().emplace_back([=](py::module_ &parent) { | ||||
|  | @ -51,26 +50,52 @@ void bind_ConstructorStats(py::module_ &m) { | |||
|         .def_readwrite("move_assignments", &ConstructorStats::move_assignments) | ||||
|         .def_readwrite("copy_constructions", &ConstructorStats::copy_constructions) | ||||
|         .def_readwrite("move_constructions", &ConstructorStats::move_constructions) | ||||
|         .def_static("get", (ConstructorStats &(*)(py::object)) &ConstructorStats::get, py::return_value_policy::reference_internal) | ||||
|         .def_static("get", | ||||
|                     (ConstructorStats & (*) (py::object)) & ConstructorStats::get, | ||||
|                     py::return_value_policy::reference_internal) | ||||
| 
 | ||||
|         // Not exactly ConstructorStats, but related: expose the internal pybind number of registered instances
 | ||||
|         // to allow instance cleanup checks (invokes a GC first)
 | ||||
|         // Not exactly ConstructorStats, but related: expose the internal pybind number of
 | ||||
|         // registered instances to allow instance cleanup checks (invokes a GC first)
 | ||||
|         .def_static("detail_reg_inst", []() { | ||||
|             ConstructorStats::gc(); | ||||
|             return py::detail::get_internals().registered_instances.size(); | ||||
|         }) | ||||
|         ; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| const char *cpp_std() { | ||||
|     return | ||||
| #if defined(PYBIND11_CPP20) | ||||
|         "C++20"; | ||||
| #elif defined(PYBIND11_CPP17) | ||||
|         "C++17"; | ||||
| #elif defined(PYBIND11_CPP14) | ||||
|         "C++14"; | ||||
| #else | ||||
|         "C++11"; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| PYBIND11_MODULE(pybind11_tests, m) { | ||||
|     m.doc() = "pybind11 test module"; | ||||
| 
 | ||||
|     // Intentionally kept minimal to not create a maintenance chore
 | ||||
|     // ("just enough" to be conclusive).
 | ||||
| #if defined(_MSC_FULL_VER) | ||||
|     m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER); | ||||
| #elif defined(__VERSION__) | ||||
|     m.attr("compiler_info") = __VERSION__; | ||||
| #else | ||||
|     m.attr("compiler_info") = py::none(); | ||||
| #endif | ||||
|     m.attr("cpp_std") = cpp_std(); | ||||
|     m.attr("PYBIND11_INTERNALS_ID") = PYBIND11_INTERNALS_ID; | ||||
| 
 | ||||
|     bind_ConstructorStats(m); | ||||
| 
 | ||||
| #if !defined(NDEBUG) | ||||
|     m.attr("debug_enabled") = true; | ||||
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) | ||||
|     m.attr("detailed_error_messages_enabled") = true; | ||||
| #else | ||||
|     m.attr("debug_enabled") = false; | ||||
|     m.attr("detailed_error_messages_enabled") = false; | ||||
| #endif | ||||
| 
 | ||||
|     py::class_<UserType>(m, "UserType", "A `py::class_` type for testing") | ||||
|  | @ -86,6 +111,7 @@ PYBIND11_MODULE(pybind11_tests, m) { | |||
|         .def(py::init<int>()) | ||||
|         .def("__repr__", [](const IncType &u) { return "IncType({})"_s.format(u.value()); }); | ||||
| 
 | ||||
|     for (const auto &initializer : initializers()) | ||||
|     for (const auto &initializer : initializers()) { | ||||
|         initializer(m); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <pybind11/pybind11.h> | ||||
| #include <pybind11/eval.h> | ||||
| 
 | ||||
| #if defined(_MSC_VER) && _MSC_VER < 1910 | ||||
| // We get some really long type names here which causes MSVC 2015 to emit warnings
 | ||||
| #    pragma warning(                                                                              \ | ||||
|         disable : 4503) // warning C4503: decorated name length exceeded, name was truncated
 | ||||
| #endif | ||||
| #include <pybind11/pybind11.h> | ||||
| 
 | ||||
| namespace py = pybind11; | ||||
| using namespace pybind11::literals; | ||||
|  | @ -58,16 +52,21 @@ union IntFloat { | |||
|     float f; | ||||
| }; | ||||
| 
 | ||||
| /// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context.
 | ||||
| /// Used to test recursive casters (e.g. std::tuple, stl containers).
 | ||||
| /// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast
 | ||||
| /// context. Used to test recursive casters (e.g. std::tuple, stl containers).
 | ||||
| struct RValueCaster {}; | ||||
| PYBIND11_NAMESPACE_BEGIN(pybind11) | ||||
| PYBIND11_NAMESPACE_BEGIN(detail) | ||||
| template<> class type_caster<RValueCaster> { | ||||
| template <> | ||||
| class type_caster<RValueCaster> { | ||||
| public: | ||||
|     PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster")); | ||||
|     static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); } | ||||
|     static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); } | ||||
|     static handle cast(RValueCaster &&, return_value_policy, handle) { | ||||
|         return py::str("rvalue").release(); | ||||
|     } | ||||
|     static handle cast(const RValueCaster &, return_value_policy, handle) { | ||||
|         return py::str("lvalue").release(); | ||||
|     } | ||||
| }; | ||||
| PYBIND11_NAMESPACE_END(detail) | ||||
| PYBIND11_NAMESPACE_END(pybind11) | ||||
|  | @ -81,5 +80,6 @@ void ignoreOldStyleInitWarnings(F &&body) { | |||
|     with warnings.catch_warnings(): | ||||
|         warnings.filterwarnings("ignore", message=message, category=FutureWarning) | ||||
|         body() | ||||
|     )", py::dict(py::arg("body") = py::cpp_function(body))); | ||||
|     )", | ||||
|              py::dict(py::arg("body") = py::cpp_function(body))); | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +1,15 @@ | |||
| [pytest] | ||||
| minversion = 3.1 | ||||
| minversion = 3.10 | ||||
| norecursedirs = test_* extra_* | ||||
| xfail_strict = True | ||||
| addopts = | ||||
|     # show summary of skipped tests | ||||
|     -rs | ||||
|     # show summary of tests | ||||
|     -ra | ||||
|     # capture only Python print and C++ py::print, but not C output (low-level Python errors) | ||||
|     --capture=sys | ||||
|     # Show local info when a failure occurs | ||||
|     --showlocals | ||||
| log_cli_level = info | ||||
| filterwarnings = | ||||
|     # make warnings into errors but ignore certain third-party extension issues | ||||
|     error | ||||
|  |  | |||
|  | @ -1,12 +1,9 @@ | |||
| numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy" | ||||
| numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6" | ||||
| numpy==1.20.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7" | ||||
| build==0.8.0 | ||||
| numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7" | ||||
| numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6" | ||||
| numpy==1.21.3; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.11" | ||||
| py @ git+https://github.com/pytest-dev/py; python_version>="3.11" | ||||
| pytest==4.6.9; python_version<"3.5" | ||||
| pytest==6.1.2; python_version=="3.5" | ||||
| pytest==6.2.4; python_version>="3.6" | ||||
| numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10" | ||||
| numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11" | ||||
| pytest==7.0.0 | ||||
| pytest-timeout | ||||
| scipy==1.2.3; platform_python_implementation!="PyPy" and python_version<"3.6" | ||||
| scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10" | ||||
| scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10" | ||||
| scipy==1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10" | ||||
|  |  | |||
|  | @ -11,8 +11,7 @@ | |||
| 
 | ||||
| TEST_SUBMODULE(async_module, m) { | ||||
|     struct DoesNotSupportAsync {}; | ||||
|     py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync") | ||||
|         .def(py::init<>()); | ||||
|     py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync").def(py::init<>()); | ||||
|     struct SupportsAsync {}; | ||||
|     py::class_<SupportsAsync>(m, "SupportsAsync") | ||||
|         .def(py::init<>()) | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import pytest | ||||
| 
 | ||||
| asyncio = pytest.importorskip("asyncio") | ||||
|  |  | |||
|  | @ -7,22 +7,26 @@ | |||
|     BSD-style license that can be found in the LICENSE file. | ||||
| */ | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| #include "constructor_stats.h" | ||||
| #include <pybind11/stl.h> | ||||
| 
 | ||||
| #include "constructor_stats.h" | ||||
| #include "pybind11_tests.h" | ||||
| 
 | ||||
| TEST_SUBMODULE(buffers, m) { | ||||
|     // test_from_python / test_to_python:
 | ||||
|     class Matrix { | ||||
|     public: | ||||
|         Matrix(py::ssize_t rows, py::ssize_t cols) : m_rows(rows), m_cols(cols) { | ||||
|             print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); | ||||
|             // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
 | ||||
|             m_data = new float[(size_t) (rows * cols)]; | ||||
|             memset(m_data, 0, sizeof(float) * (size_t) (rows * cols)); | ||||
|         } | ||||
| 
 | ||||
|         Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) { | ||||
|             print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); | ||||
|             print_copy_created(this, | ||||
|                                std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); | ||||
|             // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
 | ||||
|             m_data = new float[(size_t) (m_rows * m_cols)]; | ||||
|             memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols)); | ||||
|         } | ||||
|  | @ -35,7 +39,8 @@ TEST_SUBMODULE(buffers, m) { | |||
|         } | ||||
| 
 | ||||
|         ~Matrix() { | ||||
|             print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); | ||||
|             print_destroyed(this, | ||||
|                             std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); | ||||
|             delete[] m_data; | ||||
|         } | ||||
| 
 | ||||
|  | @ -54,11 +59,16 @@ TEST_SUBMODULE(buffers, m) { | |||
|         } | ||||
| 
 | ||||
|         Matrix &operator=(Matrix &&s) noexcept { | ||||
|             print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); | ||||
|             print_move_assigned(this, | ||||
|                                 std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); | ||||
|             if (&s != this) { | ||||
|                 delete[] m_data; | ||||
|                 m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data; | ||||
|                 s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr; | ||||
|                 m_rows = s.m_rows; | ||||
|                 m_cols = s.m_cols; | ||||
|                 m_data = s.m_data; | ||||
|                 s.m_rows = 0; | ||||
|                 s.m_cols = 0; | ||||
|                 s.m_data = nullptr; | ||||
|             } | ||||
|             return *this; | ||||
|         } | ||||
|  | @ -75,6 +85,7 @@ TEST_SUBMODULE(buffers, m) { | |||
| 
 | ||||
|         py::ssize_t rows() const { return m_rows; } | ||||
|         py::ssize_t cols() const { return m_cols; } | ||||
| 
 | ||||
|     private: | ||||
|         py::ssize_t m_rows; | ||||
|         py::ssize_t m_cols; | ||||
|  | @ -85,10 +96,11 @@ TEST_SUBMODULE(buffers, m) { | |||
|         /// Construct from a buffer
 | ||||
|         .def(py::init([](const py::buffer &b) { | ||||
|             py::buffer_info info = b.request(); | ||||
|             if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) | ||||
|             if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) { | ||||
|                 throw std::runtime_error("Incompatible buffer format!"); | ||||
|             } | ||||
| 
 | ||||
|             auto v = new Matrix(info.shape[0], info.shape[1]); | ||||
|             auto *v = new Matrix(info.shape[0], info.shape[1]); | ||||
|             memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols())); | ||||
|             return v; | ||||
|         })) | ||||
|  | @ -99,14 +111,16 @@ TEST_SUBMODULE(buffers, m) { | |||
|         /// Bare bones interface
 | ||||
|         .def("__getitem__", | ||||
|              [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) { | ||||
|                  if (i.first >= m.rows() || i.second >= m.cols()) | ||||
|                  if (i.first >= m.rows() || i.second >= m.cols()) { | ||||
|                      throw py::index_error(); | ||||
|                  } | ||||
|                  return m(i.first, i.second); | ||||
|              }) | ||||
|         .def("__setitem__", | ||||
|              [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) { | ||||
|                  if (i.first >= m.rows() || i.second >= m.cols()) | ||||
|                  if (i.first >= m.rows() || i.second >= m.cols()) { | ||||
|                      throw py::index_error(); | ||||
|                  } | ||||
|                  m(i.first, i.second) = v; | ||||
|              }) | ||||
|         /// Provide buffer access
 | ||||
|  | @ -115,8 +129,7 @@ TEST_SUBMODULE(buffers, m) { | |||
|                 m.data(),                          /* Pointer to buffer */ | ||||
|                 {m.rows(), m.cols()},              /* Buffer dimensions */ | ||||
|                 {sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */ | ||||
|                   sizeof(float) } | ||||
|             ); | ||||
|                  sizeof(float)}); | ||||
|         }); | ||||
| 
 | ||||
|     // test_inherited_protocol
 | ||||
|  | @ -125,9 +138,7 @@ TEST_SUBMODULE(buffers, m) { | |||
|         explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {} | ||||
|     }; | ||||
|     // Derived classes inherit the buffer protocol and the buffer access function
 | ||||
|     py::class_<SquareMatrix, Matrix>(m, "SquareMatrix") | ||||
|         .def(py::init<py::ssize_t>()); | ||||
| 
 | ||||
|     py::class_<SquareMatrix, Matrix>(m, "SquareMatrix").def(py::init<py::ssize_t>()); | ||||
| 
 | ||||
|     // test_pointer_to_member_fn
 | ||||
|     // Tests that passing a pointer to member to the base class works in
 | ||||
|  | @ -136,8 +147,8 @@ TEST_SUBMODULE(buffers, m) { | |||
|         int32_t value = 0; | ||||
| 
 | ||||
|         py::buffer_info get_buffer_info() { | ||||
|             return py::buffer_info(&value, sizeof(value), | ||||
|                                    py::format_descriptor<int32_t>::format(), 1); | ||||
|             return py::buffer_info( | ||||
|                 &value, sizeof(value), py::format_descriptor<int32_t>::format(), 1); | ||||
|         } | ||||
|     }; | ||||
|     py::class_<Buffer>(m, "Buffer", py::buffer_protocol()) | ||||
|  | @ -145,7 +156,6 @@ TEST_SUBMODULE(buffers, m) { | |||
|         .def_readwrite("value", &Buffer::value) | ||||
|         .def_buffer(&Buffer::get_buffer_info); | ||||
| 
 | ||||
| 
 | ||||
|     class ConstBuffer { | ||||
|         std::unique_ptr<int32_t> value; | ||||
| 
 | ||||
|  | @ -154,8 +164,8 @@ TEST_SUBMODULE(buffers, m) { | |||
|         void set_value(int32_t v) { *value = v; } | ||||
| 
 | ||||
|         py::buffer_info get_buffer_info() const { | ||||
|             return py::buffer_info(value.get(), sizeof(*value), | ||||
|                                    py::format_descriptor<int32_t>::format(), 1); | ||||
|             return py::buffer_info( | ||||
|                 value.get(), sizeof(*value), py::format_descriptor<int32_t>::format(), 1); | ||||
|         } | ||||
| 
 | ||||
|         ConstBuffer() : value(new int32_t{0}) {} | ||||
|  | @ -175,9 +185,7 @@ TEST_SUBMODULE(buffers, m) { | |||
|         const uint8_t value = 0; | ||||
|         explicit BufferReadOnly(uint8_t value) : value(value) {} | ||||
| 
 | ||||
|         py::buffer_info get_buffer_info() { | ||||
|             return py::buffer_info(&value, 1); | ||||
|         } | ||||
|         py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); } | ||||
|     }; | ||||
|     py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol()) | ||||
|         .def(py::init<uint8_t>()) | ||||
|  | @ -187,9 +195,7 @@ TEST_SUBMODULE(buffers, m) { | |||
|         uint8_t value = 0; | ||||
|         bool readonly = false; | ||||
| 
 | ||||
|         py::buffer_info get_buffer_info() { | ||||
|             return py::buffer_info(&value, 1, readonly); | ||||
|         } | ||||
|         py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); } | ||||
|     }; | ||||
|     py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol()) | ||||
|         .def(py::init<>()) | ||||
|  | @ -208,9 +214,11 @@ TEST_SUBMODULE(buffers, m) { | |||
|         .def_readonly("strides", &py::buffer_info::strides) | ||||
|         .def_readonly("readonly", &py::buffer_info::readonly) | ||||
|         .def("__repr__", [](py::handle self) { | ||||
|              return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, readonly={0.readonly!r}").format(self); | ||||
|         }) | ||||
|         ; | ||||
|             return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, " | ||||
|                            "ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, " | ||||
|                            "readonly={0.readonly!r}") | ||||
|                 .format(self); | ||||
|         }); | ||||
| 
 | ||||
|     m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); }); | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import ctypes | ||||
| import io | ||||
| import struct | ||||
|  | @ -93,16 +92,16 @@ def test_pointer_to_member_fn(): | |||
| def test_readonly_buffer(): | ||||
|     buf = m.BufferReadOnly(0x64) | ||||
|     view = memoryview(buf) | ||||
|     assert view[0] == b"d" if env.PY2 else 0x64 | ||||
|     assert view[0] == 0x64 | ||||
|     assert view.readonly | ||||
|     with pytest.raises(TypeError): | ||||
|         view[0] = b"\0" if env.PY2 else 0 | ||||
|         view[0] = 0 | ||||
| 
 | ||||
| 
 | ||||
| def test_selective_readonly_buffer(): | ||||
|     buf = m.BufferReadOnlySelect() | ||||
| 
 | ||||
|     memoryview(buf)[0] = b"d" if env.PY2 else 0x64 | ||||
|     memoryview(buf)[0] = 0x64 | ||||
|     assert buf.value == 0x64 | ||||
| 
 | ||||
|     io.BytesIO(b"A").readinto(buf) | ||||
|  | @ -110,7 +109,7 @@ def test_selective_readonly_buffer(): | |||
| 
 | ||||
|     buf.readonly = True | ||||
|     with pytest.raises(TypeError): | ||||
|         memoryview(buf)[0] = b"\0" if env.PY2 else 0 | ||||
|         memoryview(buf)[0] = 0 | ||||
|     with pytest.raises(TypeError): | ||||
|         io.BytesIO(b"1").readinto(buf) | ||||
| 
 | ||||
|  | @ -145,9 +144,6 @@ def test_ctypes_array_2d(): | |||
|         assert not info.readonly | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.skipif( | ||||
|     "env.PYPY and env.PY2", reason="PyPy2 bytes buffer not reported as readonly" | ||||
| ) | ||||
| def test_ctypes_from_buffer(): | ||||
|     test_pystr = b"0123456789" | ||||
|     for pyarray in (test_pystr, bytearray(test_pystr)): | ||||
|  |  | |||
|  | @ -7,9 +7,10 @@ | |||
|     BSD-style license that can be found in the LICENSE file. | ||||
| */ | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| #include <pybind11/complex.h> | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| 
 | ||||
| struct ConstRefCasted { | ||||
|     int tag; | ||||
| }; | ||||
|  | @ -53,14 +54,16 @@ class type_caster<ConstRefCasted> { | |||
|     using cast_op_type = | ||||
|         /// const
 | ||||
|         conditional_t< | ||||
|           std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*, | ||||
|             std::is_same<remove_reference_t<T_>, const ConstRefCasted *>::value, | ||||
|             const ConstRefCasted *, | ||||
|             conditional_t< | ||||
|           std::is_same<T_, const ConstRefCasted&>::value, const ConstRefCasted&, | ||||
|                 std::is_same<T_, const ConstRefCasted &>::value, | ||||
|                 const ConstRefCasted &, | ||||
|                 /// non-const
 | ||||
|       conditional_t< | ||||
|           std::is_same<remove_reference_t<T_>, ConstRefCasted*>::value, ConstRefCasted*, | ||||
|       conditional_t< | ||||
|           std::is_same<T_, ConstRefCasted&>::value, ConstRefCasted&, | ||||
|                 conditional_t<std::is_same<remove_reference_t<T_>, ConstRefCasted *>::value, | ||||
|                               ConstRefCasted *, | ||||
|                               conditional_t<std::is_same<T_, ConstRefCasted &>::value, | ||||
|                                             ConstRefCasted &, | ||||
|                                             /* else */ ConstRefCasted &&>>>>; | ||||
| 
 | ||||
| private: | ||||
|  | @ -74,28 +77,47 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
|     m.def("string_roundtrip", [](const char *s) { return s; }); | ||||
| 
 | ||||
|     // test_unicode_conversion
 | ||||
|     // Some test characters in utf16 and utf32 encodings.  The last one (the 𝐀) contains a null byte
 | ||||
|     char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/,              mathbfA32 = 0x1d400 /*𝐀*/; | ||||
|     char16_t b16 = 0x62 /*b*/, z16 = 0x7a,       ib16 = 0x203d,       cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; | ||||
|     // Some test characters in utf16 and utf32 encodings.  The last one (the 𝐀) contains a null
 | ||||
|     // byte
 | ||||
|     char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, | ||||
|              mathbfA32 = 0x1d400 /*𝐀*/; | ||||
|     char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, | ||||
|              mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; | ||||
|     std::wstring wstr; | ||||
|     wstr.push_back(0x61);   // a
 | ||||
|     wstr.push_back(0x2e18); // ⸘
 | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16
 | ||||
|     else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32
 | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { | ||||
|         wstr.push_back(mathbfA16_1); | ||||
|         wstr.push_back(mathbfA16_2); | ||||
|     } // 𝐀, utf16
 | ||||
|     else { | ||||
|         wstr.push_back((wchar_t) mathbfA32); | ||||
|     }                     // 𝐀, utf32
 | ||||
|     wstr.push_back(0x7a); // z
 | ||||
| 
 | ||||
|     m.def("good_utf8_string", []() { return std::string((const char*)u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀
 | ||||
|     m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // b‽🎂𝐀z
 | ||||
|     m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // a𝐀🎂‽z
 | ||||
|     m.def("good_utf8_string", []() { | ||||
|         return std::string((const char *) u8"Say utf8\u203d \U0001f382 \U0001d400"); | ||||
|     }); // Say utf8‽ 🎂 𝐀
 | ||||
|     m.def("good_utf16_string", [=]() { | ||||
|         return std::u16string({b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16}); | ||||
|     }); // b‽🎂𝐀z
 | ||||
|     m.def("good_utf32_string", [=]() { | ||||
|         return std::u32string({a32, mathbfA32, cake32, ib32, z32}); | ||||
|     });                                                 // a𝐀🎂‽z
 | ||||
|     m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
 | ||||
|     m.def("bad_utf8_string", []()  { return std::string("abc\xd0" "def"); }); | ||||
|     m.def("bad_utf8_string", []() { | ||||
|         return std::string("abc\xd0" | ||||
|                            "def"); | ||||
|     }); | ||||
|     m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); }); | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|     // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError
 | ||||
|     // Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger
 | ||||
|     // UnicodeDecodeError
 | ||||
|     m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); }); | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) | ||||
|         m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); }); | ||||
| #endif | ||||
|     if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { | ||||
|         m.def("bad_wchar_string", [=]() { | ||||
|             return std::wstring({wchar_t(0x61), wchar_t(0xd800)}); | ||||
|         }); | ||||
|     } | ||||
|     m.def("u8_Z", []() -> char { return 'Z'; }); | ||||
|     m.def("u8_eacute", []() -> char { return '\xe9'; }); | ||||
|     m.def("u16_ibang", [=]() -> char16_t { return ib16; }); | ||||
|  | @ -117,8 +139,13 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
| 
 | ||||
| #ifdef PYBIND11_HAS_U8STRING | ||||
|     m.attr("has_u8string") = true; | ||||
|     m.def("good_utf8_u8string", []() { return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀
 | ||||
|     m.def("bad_utf8_u8string", []()  { return std::u8string((const char8_t*)"abc\xd0" "def"); }); | ||||
|     m.def("good_utf8_u8string", []() { | ||||
|         return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); | ||||
|     }); // Say utf8‽ 🎂 𝐀
 | ||||
|     m.def("bad_utf8_u8string", []() { | ||||
|         return std::u8string((const char8_t *) "abc\xd0" | ||||
|                                                "def"); | ||||
|     }); | ||||
| 
 | ||||
|     m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; }); | ||||
| 
 | ||||
|  | @ -133,28 +160,55 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
|     m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); }); | ||||
|     m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); }); | ||||
|     m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); }); | ||||
|     m.def("string_view_chars",   [](std::string_view s)    { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); | ||||
|     m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); | ||||
|     m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); | ||||
|     m.def("string_view_return",   []() { return std::string_view((const char*)u8"utf8 secret \U0001f382"); }); | ||||
|     m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); | ||||
|     m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); | ||||
|     m.def("string_view_chars", [](std::string_view s) { | ||||
|         py::list l; | ||||
|         for (auto c : s) { | ||||
|             l.append((std::uint8_t) c); | ||||
|         } | ||||
|         return l; | ||||
|     }); | ||||
|     m.def("string_view16_chars", [](std::u16string_view s) { | ||||
|         py::list l; | ||||
|         for (auto c : s) { | ||||
|             l.append((int) c); | ||||
|         } | ||||
|         return l; | ||||
|     }); | ||||
|     m.def("string_view32_chars", [](std::u32string_view s) { | ||||
|         py::list l; | ||||
|         for (auto c : s) { | ||||
|             l.append((int) c); | ||||
|         } | ||||
|         return l; | ||||
|     }); | ||||
|     m.def("string_view_return", | ||||
|           []() { return std::string_view((const char *) u8"utf8 secret \U0001f382"); }); | ||||
|     m.def("string_view16_return", | ||||
|           []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); | ||||
|     m.def("string_view32_return", | ||||
|           []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); | ||||
| 
 | ||||
|     // The inner lambdas here are to also test implicit conversion
 | ||||
|     using namespace std::literals; | ||||
|     m.def("string_view_bytes", []() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); }); | ||||
|     m.def("string_view_str",   []() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); }); | ||||
|     m.def("string_view_from_bytes", [](const py::bytes &b) { return [](std::string_view s) { return s; }(b); }); | ||||
| #if PY_MAJOR_VERSION >= 3 | ||||
|     m.def("string_view_bytes", | ||||
|           []() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); }); | ||||
|     m.def("string_view_str", | ||||
|           []() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); }); | ||||
|     m.def("string_view_from_bytes", | ||||
|           [](const py::bytes &b) { return [](std::string_view s) { return s; }(b); }); | ||||
|     m.def("string_view_memoryview", []() { | ||||
|         static constexpr auto val = "Have some \360\237\216\202"sv; | ||||
|         return py::memoryview::from_memory(val); | ||||
|     }); | ||||
| #endif | ||||
| 
 | ||||
| #    ifdef PYBIND11_HAS_U8STRING | ||||
|     m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); }); | ||||
|     m.def("string_view8_chars",  [](std::u8string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); | ||||
|     m.def("string_view8_chars", [](std::u8string_view s) { | ||||
|         py::list l; | ||||
|         for (auto c : s) | ||||
|             l.append((std::uint8_t) c); | ||||
|         return l; | ||||
|     }); | ||||
|     m.def("string_view8_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); }); | ||||
|     m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; }); | ||||
| #    endif | ||||
|  | @ -179,7 +233,8 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
| 
 | ||||
|     // test_int_convert
 | ||||
|     m.def("int_passthrough", [](int arg) { return arg; }); | ||||
|     m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert()); | ||||
|     m.def( | ||||
|         "int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert()); | ||||
| 
 | ||||
|     // test_tuple
 | ||||
|     m.def( | ||||
|  | @ -188,23 +243,32 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
|             return std::make_pair(input.second, input.first); | ||||
|         }, | ||||
|         "Return a pair in reversed order"); | ||||
|     m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) { | ||||
|     m.def( | ||||
|         "tuple_passthrough", | ||||
|         [](std::tuple<bool, std::string, int> input) { | ||||
|             return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); | ||||
|     }, "Return a triple in reversed order"); | ||||
|         }, | ||||
|         "Return a triple in reversed order"); | ||||
|     m.def("empty_tuple", []() { return std::tuple<>(); }); | ||||
|     static std::pair<RValueCaster, RValueCaster> lvpair; | ||||
|     static std::tuple<RValueCaster, RValueCaster, RValueCaster> lvtuple; | ||||
|     static std::pair<RValueCaster, std::tuple<RValueCaster, std::pair<RValueCaster, RValueCaster>>> lvnested; | ||||
|     static std::pair<RValueCaster, std::tuple<RValueCaster, std::pair<RValueCaster, RValueCaster>>> | ||||
|         lvnested; | ||||
|     m.def("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); }); | ||||
|     m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; }); | ||||
|     m.def("rvalue_tuple", []() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); }); | ||||
|     m.def("rvalue_tuple", | ||||
|           []() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); }); | ||||
|     m.def("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; }); | ||||
|     m.def("rvalue_nested", []() { | ||||
|         return std::make_pair(RValueCaster{}, std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); }); | ||||
|         return std::make_pair( | ||||
|             RValueCaster{}, | ||||
|             std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); | ||||
|     }); | ||||
|     m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; }); | ||||
| 
 | ||||
|     static std::pair<int, std::string> int_string_pair{2, "items"}; | ||||
|     m.def("int_string_pair", []() { return &int_string_pair; }); | ||||
|     m.def( | ||||
|         "int_string_pair", []() { return &int_string_pair; }, py::return_value_policy::reference); | ||||
| 
 | ||||
|     // test_builtins_cast_return_none
 | ||||
|     m.def("return_none_string", []() -> std::string * { return nullptr; }); | ||||
|  | @ -230,7 +294,8 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
| 
 | ||||
|     // test_bool_caster
 | ||||
|     m.def("bool_passthrough", [](bool arg) { return arg; }); | ||||
|     m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert()); | ||||
|     m.def( | ||||
|         "bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert()); | ||||
| 
 | ||||
|     // TODO: This should be disabled and fixed in future Intel compilers
 | ||||
| #if !defined(__INTEL_COMPILER) | ||||
|  | @ -238,13 +303,15 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
|     // When compiled with the Intel compiler, this results in segmentation faults when importing
 | ||||
|     // the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when
 | ||||
|     // a newer version of icc is available.
 | ||||
|     m.def("bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert()); | ||||
|     m.def( | ||||
|         "bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert()); | ||||
| #endif | ||||
| 
 | ||||
|     // test_reference_wrapper
 | ||||
|     m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); }); | ||||
|     m.def("refwrap_usertype", [](std::reference_wrapper<UserType> p) { return p.get().value(); }); | ||||
|     m.def("refwrap_usertype_const", [](std::reference_wrapper<const UserType> p) { return p.get().value(); }); | ||||
|     m.def("refwrap_usertype_const", | ||||
|           [](std::reference_wrapper<const UserType> p) { return p.get().value(); }); | ||||
| 
 | ||||
|     m.def("refwrap_lvalue", []() -> std::reference_wrapper<UserType> { | ||||
|         static UserType x(1); | ||||
|  | @ -259,15 +326,18 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
|     // triggers static_assert failure.
 | ||||
|     // m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
 | ||||
| 
 | ||||
|     m.def("refwrap_list", [](bool copy) { | ||||
|     m.def( | ||||
|         "refwrap_list", | ||||
|         [](bool copy) { | ||||
|             static IncType x1(1), x2(2); | ||||
|             py::list l; | ||||
|         for (auto &f : {std::ref(x1), std::ref(x2)}) { | ||||
|             l.append(py::cast(f, copy ? py::return_value_policy::copy | ||||
|                                       : py::return_value_policy::reference)); | ||||
|             for (const auto &f : {std::ref(x1), std::ref(x2)}) { | ||||
|                 l.append(py::cast( | ||||
|                     f, copy ? py::return_value_policy::copy : py::return_value_policy::reference)); | ||||
|             } | ||||
|             return l; | ||||
|     }, "copy"_a); | ||||
|         }, | ||||
|         "copy"_a); | ||||
| 
 | ||||
|     m.def("refwrap_iiw", [](const IncType &w) { return w.value(); }); | ||||
|     m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) { | ||||
|  | @ -284,7 +354,8 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
| 
 | ||||
|     // test_complex
 | ||||
|     m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); | ||||
|     m.def("complex_cast", [](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); }); | ||||
|     m.def("complex_cast", | ||||
|           [](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); }); | ||||
| 
 | ||||
|     // test int vs. long (Python 2)
 | ||||
|     m.def("int_cast", []() { return (int) 42; }); | ||||
|  | @ -306,5 +377,6 @@ TEST_SUBMODULE(builtin_casters, m) { | |||
|     m.def("takes_ref_wrap", [](std::reference_wrapper<ConstRefCasted> x) { return x.get().tag; }); | ||||
|     m.def("takes_const_ptr", [](const ConstRefCasted *x) { return x->tag; }); | ||||
|     m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; }); | ||||
|     m.def("takes_const_ref_wrap", [](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; }); | ||||
|     m.def("takes_const_ref_wrap", | ||||
|           [](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; }); | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import sys | ||||
| 
 | ||||
| import pytest | ||||
| 
 | ||||
| import env | ||||
|  | @ -12,12 +13,12 @@ def test_simple_string(): | |||
| 
 | ||||
| def test_unicode_conversion(): | ||||
|     """Tests unicode conversion and error reporting.""" | ||||
|     assert m.good_utf8_string() == u"Say utf8‽ 🎂 𝐀" | ||||
|     assert m.good_utf16_string() == u"b‽🎂𝐀z" | ||||
|     assert m.good_utf32_string() == u"a𝐀🎂‽z" | ||||
|     assert m.good_wchar_string() == u"a⸘𝐀z" | ||||
|     assert m.good_utf8_string() == "Say utf8‽ 🎂 𝐀" | ||||
|     assert m.good_utf16_string() == "b‽🎂𝐀z" | ||||
|     assert m.good_utf32_string() == "a𝐀🎂‽z" | ||||
|     assert m.good_wchar_string() == "a⸘𝐀z" | ||||
|     if hasattr(m, "has_u8string"): | ||||
|         assert m.good_utf8_u8string() == u"Say utf8‽ 🎂 𝐀" | ||||
|         assert m.good_utf8_u8string() == "Say utf8‽ 🎂 𝐀" | ||||
| 
 | ||||
|     with pytest.raises(UnicodeDecodeError): | ||||
|         m.bad_utf8_string() | ||||
|  | @ -25,7 +26,7 @@ def test_unicode_conversion(): | |||
|     with pytest.raises(UnicodeDecodeError): | ||||
|         m.bad_utf16_string() | ||||
| 
 | ||||
|     # These are provided only if they actually fail (they don't when 32-bit and under Python 2.7) | ||||
|     # These are provided only if they actually fail (they don't when 32-bit) | ||||
|     if hasattr(m, "bad_utf32_string"): | ||||
|         with pytest.raises(UnicodeDecodeError): | ||||
|             m.bad_utf32_string() | ||||
|  | @ -37,10 +38,10 @@ def test_unicode_conversion(): | |||
|             m.bad_utf8_u8string() | ||||
| 
 | ||||
|     assert m.u8_Z() == "Z" | ||||
|     assert m.u8_eacute() == u"é" | ||||
|     assert m.u16_ibang() == u"‽" | ||||
|     assert m.u32_mathbfA() == u"𝐀" | ||||
|     assert m.wchar_heart() == u"♥" | ||||
|     assert m.u8_eacute() == "é" | ||||
|     assert m.u16_ibang() == "‽" | ||||
|     assert m.u32_mathbfA() == "𝐀" | ||||
|     assert m.wchar_heart() == "♥" | ||||
|     if hasattr(m, "has_u8string"): | ||||
|         assert m.u8_char8_Z() == "Z" | ||||
| 
 | ||||
|  | @ -49,72 +50,72 @@ def test_single_char_arguments(): | |||
|     """Tests failures for passing invalid inputs to char-accepting functions""" | ||||
| 
 | ||||
|     def toobig_message(r): | ||||
|         return "Character code point not in range({:#x})".format(r) | ||||
|         return f"Character code point not in range({r:#x})" | ||||
| 
 | ||||
|     toolong_message = "Expected a character, but multi-character string found" | ||||
| 
 | ||||
|     assert m.ord_char(u"a") == 0x61  # simple ASCII | ||||
|     assert m.ord_char_lv(u"b") == 0x62 | ||||
|     assert m.ord_char("a") == 0x61  # simple ASCII | ||||
|     assert m.ord_char_lv("b") == 0x62 | ||||
|     assert ( | ||||
|         m.ord_char(u"é") == 0xE9 | ||||
|         m.ord_char("é") == 0xE9 | ||||
|     )  # requires 2 bytes in utf-8, but can be stuffed in a char | ||||
|     with pytest.raises(ValueError) as excinfo: | ||||
|         assert m.ord_char(u"Ā") == 0x100  # requires 2 bytes, doesn't fit in a char | ||||
|         assert m.ord_char("Ā") == 0x100  # requires 2 bytes, doesn't fit in a char | ||||
|     assert str(excinfo.value) == toobig_message(0x100) | ||||
|     with pytest.raises(ValueError) as excinfo: | ||||
|         assert m.ord_char(u"ab") | ||||
|         assert m.ord_char("ab") | ||||
|     assert str(excinfo.value) == toolong_message | ||||
| 
 | ||||
|     assert m.ord_char16(u"a") == 0x61 | ||||
|     assert m.ord_char16(u"é") == 0xE9 | ||||
|     assert m.ord_char16_lv(u"ê") == 0xEA | ||||
|     assert m.ord_char16(u"Ā") == 0x100 | ||||
|     assert m.ord_char16(u"‽") == 0x203D | ||||
|     assert m.ord_char16(u"♥") == 0x2665 | ||||
|     assert m.ord_char16_lv(u"♡") == 0x2661 | ||||
|     assert m.ord_char16("a") == 0x61 | ||||
|     assert m.ord_char16("é") == 0xE9 | ||||
|     assert m.ord_char16_lv("ê") == 0xEA | ||||
|     assert m.ord_char16("Ā") == 0x100 | ||||
|     assert m.ord_char16("‽") == 0x203D | ||||
|     assert m.ord_char16("♥") == 0x2665 | ||||
|     assert m.ord_char16_lv("♡") == 0x2661 | ||||
|     with pytest.raises(ValueError) as excinfo: | ||||
|         assert m.ord_char16(u"🎂") == 0x1F382  # requires surrogate pair | ||||
|         assert m.ord_char16("🎂") == 0x1F382  # requires surrogate pair | ||||
|     assert str(excinfo.value) == toobig_message(0x10000) | ||||
|     with pytest.raises(ValueError) as excinfo: | ||||
|         assert m.ord_char16(u"aa") | ||||
|         assert m.ord_char16("aa") | ||||
|     assert str(excinfo.value) == toolong_message | ||||
| 
 | ||||
|     assert m.ord_char32(u"a") == 0x61 | ||||
|     assert m.ord_char32(u"é") == 0xE9 | ||||
|     assert m.ord_char32(u"Ā") == 0x100 | ||||
|     assert m.ord_char32(u"‽") == 0x203D | ||||
|     assert m.ord_char32(u"♥") == 0x2665 | ||||
|     assert m.ord_char32(u"🎂") == 0x1F382 | ||||
|     assert m.ord_char32("a") == 0x61 | ||||
|     assert m.ord_char32("é") == 0xE9 | ||||
|     assert m.ord_char32("Ā") == 0x100 | ||||
|     assert m.ord_char32("‽") == 0x203D | ||||
|     assert m.ord_char32("♥") == 0x2665 | ||||
|     assert m.ord_char32("🎂") == 0x1F382 | ||||
|     with pytest.raises(ValueError) as excinfo: | ||||
|         assert m.ord_char32(u"aa") | ||||
|         assert m.ord_char32("aa") | ||||
|     assert str(excinfo.value) == toolong_message | ||||
| 
 | ||||
|     assert m.ord_wchar(u"a") == 0x61 | ||||
|     assert m.ord_wchar(u"é") == 0xE9 | ||||
|     assert m.ord_wchar(u"Ā") == 0x100 | ||||
|     assert m.ord_wchar(u"‽") == 0x203D | ||||
|     assert m.ord_wchar(u"♥") == 0x2665 | ||||
|     assert m.ord_wchar("a") == 0x61 | ||||
|     assert m.ord_wchar("é") == 0xE9 | ||||
|     assert m.ord_wchar("Ā") == 0x100 | ||||
|     assert m.ord_wchar("‽") == 0x203D | ||||
|     assert m.ord_wchar("♥") == 0x2665 | ||||
|     if m.wchar_size == 2: | ||||
|         with pytest.raises(ValueError) as excinfo: | ||||
|             assert m.ord_wchar(u"🎂") == 0x1F382  # requires surrogate pair | ||||
|             assert m.ord_wchar("🎂") == 0x1F382  # requires surrogate pair | ||||
|         assert str(excinfo.value) == toobig_message(0x10000) | ||||
|     else: | ||||
|         assert m.ord_wchar(u"🎂") == 0x1F382 | ||||
|         assert m.ord_wchar("🎂") == 0x1F382 | ||||
|     with pytest.raises(ValueError) as excinfo: | ||||
|         assert m.ord_wchar(u"aa") | ||||
|         assert m.ord_wchar("aa") | ||||
|     assert str(excinfo.value) == toolong_message | ||||
| 
 | ||||
|     if hasattr(m, "has_u8string"): | ||||
|         assert m.ord_char8(u"a") == 0x61  # simple ASCII | ||||
|         assert m.ord_char8_lv(u"b") == 0x62 | ||||
|         assert m.ord_char8("a") == 0x61  # simple ASCII | ||||
|         assert m.ord_char8_lv("b") == 0x62 | ||||
|         assert ( | ||||
|             m.ord_char8(u"é") == 0xE9 | ||||
|             m.ord_char8("é") == 0xE9 | ||||
|         )  # requires 2 bytes in utf-8, but can be stuffed in a char | ||||
|         with pytest.raises(ValueError) as excinfo: | ||||
|             assert m.ord_char8(u"Ā") == 0x100  # requires 2 bytes, doesn't fit in a char | ||||
|             assert m.ord_char8("Ā") == 0x100  # requires 2 bytes, doesn't fit in a char | ||||
|         assert str(excinfo.value) == toobig_message(0x100) | ||||
|         with pytest.raises(ValueError) as excinfo: | ||||
|             assert m.ord_char8(u"ab") | ||||
|             assert m.ord_char8("ab") | ||||
|         assert str(excinfo.value) == toolong_message | ||||
| 
 | ||||
| 
 | ||||
|  | @ -123,18 +124,22 @@ def test_bytes_to_string(): | |||
|     one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" | ||||
|     # Issue #816 | ||||
| 
 | ||||
|     def to_bytes(s): | ||||
|         b = s if env.PY2 else s.encode("utf8") | ||||
|         assert isinstance(b, bytes) | ||||
|         return b | ||||
| 
 | ||||
|     assert m.strlen(to_bytes("hi")) == 2 | ||||
|     assert m.string_length(to_bytes("world")) == 5 | ||||
|     assert m.string_length(to_bytes("a\x00b")) == 3 | ||||
|     assert m.strlen(to_bytes("a\x00b")) == 1  # C-string limitation | ||||
|     assert m.strlen(b"hi") == 2 | ||||
|     assert m.string_length(b"world") == 5 | ||||
|     assert m.string_length("a\x00b".encode()) == 3 | ||||
|     assert m.strlen("a\x00b".encode()) == 1  # C-string limitation | ||||
| 
 | ||||
|     # passing in a utf8 encoded string should work | ||||
|     assert m.string_length(u"💩".encode("utf8")) == 4 | ||||
|     assert m.string_length("💩".encode()) == 4 | ||||
| 
 | ||||
| 
 | ||||
| def test_bytearray_to_string(): | ||||
|     """Tests the ability to pass bytearray to C++ string-accepting functions""" | ||||
|     assert m.string_length(bytearray(b"Hi")) == 2 | ||||
|     assert m.strlen(bytearray(b"bytearray")) == 9 | ||||
|     assert m.string_length(bytearray()) == 0 | ||||
|     assert m.string_length(bytearray("🦜", "utf-8", "strict")) == 4 | ||||
|     assert m.string_length(bytearray(b"\x80")) == 1 | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no <string_view>") | ||||
|  | @ -142,26 +147,26 @@ def test_string_view(capture): | |||
|     """Tests support for C++17 string_view arguments and return values""" | ||||
|     assert m.string_view_chars("Hi") == [72, 105] | ||||
|     assert m.string_view_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] | ||||
|     assert m.string_view16_chars(u"Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82] | ||||
|     assert m.string_view32_chars(u"Hi 🎂") == [72, 105, 32, 127874] | ||||
|     assert m.string_view16_chars("Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82] | ||||
|     assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874] | ||||
|     if hasattr(m, "has_u8string"): | ||||
|         assert m.string_view8_chars("Hi") == [72, 105] | ||||
|         assert m.string_view8_chars(u"Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] | ||||
|         assert m.string_view8_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] | ||||
| 
 | ||||
|     assert m.string_view_return() == u"utf8 secret 🎂" | ||||
|     assert m.string_view16_return() == u"utf16 secret 🎂" | ||||
|     assert m.string_view32_return() == u"utf32 secret 🎂" | ||||
|     assert m.string_view_return() == "utf8 secret 🎂" | ||||
|     assert m.string_view16_return() == "utf16 secret 🎂" | ||||
|     assert m.string_view32_return() == "utf32 secret 🎂" | ||||
|     if hasattr(m, "has_u8string"): | ||||
|         assert m.string_view8_return() == u"utf8 secret 🎂" | ||||
|         assert m.string_view8_return() == "utf8 secret 🎂" | ||||
| 
 | ||||
|     with capture: | ||||
|         m.string_view_print("Hi") | ||||
|         m.string_view_print("utf8 🎂") | ||||
|         m.string_view16_print(u"utf16 🎂") | ||||
|         m.string_view32_print(u"utf32 🎂") | ||||
|         m.string_view16_print("utf16 🎂") | ||||
|         m.string_view32_print("utf32 🎂") | ||||
|     assert ( | ||||
|         capture | ||||
|         == u""" | ||||
|         == """ | ||||
|         Hi 2 | ||||
|         utf8 🎂 9 | ||||
|         utf16 🎂 8 | ||||
|  | @ -171,10 +176,10 @@ def test_string_view(capture): | |||
|     if hasattr(m, "has_u8string"): | ||||
|         with capture: | ||||
|             m.string_view8_print("Hi") | ||||
|             m.string_view8_print(u"utf8 🎂") | ||||
|             m.string_view8_print("utf8 🎂") | ||||
|         assert ( | ||||
|             capture | ||||
|             == u""" | ||||
|             == """ | ||||
|             Hi 2 | ||||
|             utf8 🎂 9 | ||||
|         """ | ||||
|  | @ -183,11 +188,11 @@ def test_string_view(capture): | |||
|     with capture: | ||||
|         m.string_view_print("Hi, ascii") | ||||
|         m.string_view_print("Hi, utf8 🎂") | ||||
|         m.string_view16_print(u"Hi, utf16 🎂") | ||||
|         m.string_view32_print(u"Hi, utf32 🎂") | ||||
|         m.string_view16_print("Hi, utf16 🎂") | ||||
|         m.string_view32_print("Hi, utf32 🎂") | ||||
|     assert ( | ||||
|         capture | ||||
|         == u""" | ||||
|         == """ | ||||
|         Hi, ascii 9 | ||||
|         Hi, utf8 🎂 13 | ||||
|         Hi, utf16 🎂 12 | ||||
|  | @ -197,21 +202,20 @@ def test_string_view(capture): | |||
|     if hasattr(m, "has_u8string"): | ||||
|         with capture: | ||||
|             m.string_view8_print("Hi, ascii") | ||||
|             m.string_view8_print(u"Hi, utf8 🎂") | ||||
|             m.string_view8_print("Hi, utf8 🎂") | ||||
|         assert ( | ||||
|             capture | ||||
|             == u""" | ||||
|             == """ | ||||
|             Hi, ascii 9 | ||||
|             Hi, utf8 🎂 13 | ||||
|         """ | ||||
|         ) | ||||
| 
 | ||||
|     assert m.string_view_bytes() == b"abc \x80\x80 def" | ||||
|     assert m.string_view_str() == u"abc ‽ def" | ||||
|     assert m.string_view_from_bytes(u"abc ‽ def".encode("utf-8")) == u"abc ‽ def" | ||||
|     assert m.string_view_str() == "abc ‽ def" | ||||
|     assert m.string_view_from_bytes("abc ‽ def".encode()) == "abc ‽ def" | ||||
|     if hasattr(m, "has_u8string"): | ||||
|         assert m.string_view8_str() == u"abc ‽ def" | ||||
|     if not env.PY2: | ||||
|         assert m.string_view8_str() == "abc ‽ def" | ||||
|     assert m.string_view_memoryview() == "Have some 🎂".encode() | ||||
| 
 | ||||
|     assert m.bytes_from_type_with_both_operator_string_and_string_view() == b"success" | ||||
|  | @ -224,18 +228,6 @@ def test_integer_casting(): | |||
|     assert m.i64_str(-1) == "-1" | ||||
|     assert m.i32_str(2000000000) == "2000000000" | ||||
|     assert m.u32_str(2000000000) == "2000000000" | ||||
|     if env.PY2: | ||||
|         assert m.i32_str(long(-1)) == "-1"  # noqa: F821 undefined name 'long' | ||||
|         assert m.i64_str(long(-1)) == "-1"  # noqa: F821 undefined name 'long' | ||||
|         assert ( | ||||
|             m.i64_str(long(-999999999999))  # noqa: F821 undefined name 'long' | ||||
|             == "-999999999999" | ||||
|         ) | ||||
|         assert ( | ||||
|             m.u64_str(long(999999999999))  # noqa: F821 undefined name 'long' | ||||
|             == "999999999999" | ||||
|         ) | ||||
|     else: | ||||
|     assert m.i64_str(-999999999999) == "-999999999999" | ||||
|     assert m.u64_str(999999999999) == "999999999999" | ||||
| 
 | ||||
|  | @ -252,46 +244,38 @@ def test_integer_casting(): | |||
|         m.i32_str(3000000000) | ||||
|     assert "incompatible function arguments" in str(excinfo.value) | ||||
| 
 | ||||
|     if env.PY2: | ||||
|         with pytest.raises(TypeError) as excinfo: | ||||
|             m.u32_str(long(-1))  # noqa: F821 undefined name 'long' | ||||
|         assert "incompatible function arguments" in str(excinfo.value) | ||||
|         with pytest.raises(TypeError) as excinfo: | ||||
|             m.u64_str(long(-1))  # noqa: F821 undefined name 'long' | ||||
|         assert "incompatible function arguments" in str(excinfo.value) | ||||
| 
 | ||||
| 
 | ||||
| def test_int_convert(): | ||||
|     class Int(object): | ||||
|     class Int: | ||||
|         def __int__(self): | ||||
|             return 42 | ||||
| 
 | ||||
|     class NotInt(object): | ||||
|     class NotInt: | ||||
|         pass | ||||
| 
 | ||||
|     class Float(object): | ||||
|     class Float: | ||||
|         def __float__(self): | ||||
|             return 41.99999 | ||||
| 
 | ||||
|     class Index(object): | ||||
|     class Index: | ||||
|         def __index__(self): | ||||
|             return 42 | ||||
| 
 | ||||
|     class IntAndIndex(object): | ||||
|     class IntAndIndex: | ||||
|         def __int__(self): | ||||
|             return 42 | ||||
| 
 | ||||
|         def __index__(self): | ||||
|             return 0 | ||||
| 
 | ||||
|     class RaisingTypeErrorOnIndex(object): | ||||
|     class RaisingTypeErrorOnIndex: | ||||
|         def __index__(self): | ||||
|             raise TypeError | ||||
| 
 | ||||
|         def __int__(self): | ||||
|             return 42 | ||||
| 
 | ||||
|     class RaisingValueErrorOnIndex(object): | ||||
|     class RaisingValueErrorOnIndex: | ||||
|         def __index__(self): | ||||
|             raise ValueError | ||||
| 
 | ||||
|  | @ -311,7 +295,7 @@ def test_int_convert(): | |||
|     cant_convert(3.14159) | ||||
|     # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) | ||||
|     # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) | ||||
|     if (3, 8) <= env.PY < (3, 10) and env.CPYTHON: | ||||
|     if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: | ||||
|         with env.deprecated_call(): | ||||
|             assert convert(Int()) == 42 | ||||
|     else: | ||||
|  | @ -348,7 +332,7 @@ def test_numpy_int_convert(): | |||
|     # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) | ||||
|     # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) | ||||
|     # https://github.com/pybind/pybind11/issues/3408 | ||||
|     if (3, 8) <= env.PY < (3, 10) and env.CPYTHON: | ||||
|     if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: | ||||
|         with env.deprecated_call(): | ||||
|             assert convert(np.float32(3.14159)) == 3 | ||||
|     else: | ||||
|  | @ -475,7 +459,7 @@ def test_bool_caster(): | |||
|     require_implicit(None) | ||||
|     assert convert(None) is False | ||||
| 
 | ||||
|     class A(object): | ||||
|     class A: | ||||
|         def __init__(self, x): | ||||
|             self.x = x | ||||
| 
 | ||||
|  | @ -485,7 +469,7 @@ def test_bool_caster(): | |||
|         def __bool__(self): | ||||
|             return self.x | ||||
| 
 | ||||
|     class B(object): | ||||
|     class B: | ||||
|         pass | ||||
| 
 | ||||
|     # Arbitrary objects are not accepted | ||||
|  | @ -515,17 +499,9 @@ def test_numpy_bool(): | |||
| 
 | ||||
| 
 | ||||
| def test_int_long(): | ||||
|     """In Python 2, a C++ int should return a Python int rather than long | ||||
|     if possible: longs are not always accepted where ints are used (such | ||||
|     as the argument to sys.exit()). A C++ long long is always a Python | ||||
|     long.""" | ||||
| 
 | ||||
|     import sys | ||||
| 
 | ||||
|     must_be_long = type(getattr(sys, "maxint", 1) + 1) | ||||
|     assert isinstance(m.int_cast(), int) | ||||
|     assert isinstance(m.long_cast(), int) | ||||
|     assert isinstance(m.longlong_cast(), must_be_long) | ||||
|     assert isinstance(m.longlong_cast(), int) | ||||
| 
 | ||||
| 
 | ||||
| def test_void_caster_2(): | ||||
|  |  | |||
|  | @ -40,8 +40,7 @@ TEST_SUBMODULE(call_policies, m) { | |||
|         Child(Child &&) = default; | ||||
|         ~Child() { py::print("Releasing child."); } | ||||
|     }; | ||||
|     py::class_<Child>(m, "Child") | ||||
|         .def(py::init<>()); | ||||
|     py::class_<Child>(m, "Child").def(py::init<>()); | ||||
| 
 | ||||
|     class Parent { | ||||
|     public: | ||||
|  | @ -62,11 +61,12 @@ TEST_SUBMODULE(call_policies, m) { | |||
|         .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()) | ||||
|         .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>()) | ||||
|         .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()) | ||||
|         .def_static( | ||||
|             "staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>()); | ||||
|         .def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>()); | ||||
| 
 | ||||
|     m.def("free_function", [](Parent*, Child*) {}, py::keep_alive<1, 2>()); | ||||
|     m.def("invalid_arg_index", []{}, py::keep_alive<0, 1>()); | ||||
|     m.def( | ||||
|         "free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>()); | ||||
|     m.def( | ||||
|         "invalid_arg_index", [] {}, py::keep_alive<0, 1>()); | ||||
| 
 | ||||
| #if !defined(PYPY_VERSION) | ||||
|     // test_alive_gc
 | ||||
|  | @ -74,29 +74,37 @@ TEST_SUBMODULE(call_policies, m) { | |||
|     public: | ||||
|         using Parent::Parent; | ||||
|     }; | ||||
|     py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()) | ||||
|         .def(py::init<>()); | ||||
|     py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()).def(py::init<>()); | ||||
| #endif | ||||
| 
 | ||||
|     // test_call_guard
 | ||||
|     m.def("unguarded_call", &CustomGuard::report_status); | ||||
|     m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>()); | ||||
| 
 | ||||
|     m.def("multiple_guards_correct_order", []() { | ||||
|         return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status(); | ||||
|     }, py::call_guard<CustomGuard, DependentGuard>()); | ||||
|     m.def( | ||||
|         "multiple_guards_correct_order", | ||||
|         []() { | ||||
|             return CustomGuard::report_status() + std::string(" & ") | ||||
|                    + DependentGuard::report_status(); | ||||
|         }, | ||||
|         py::call_guard<CustomGuard, DependentGuard>()); | ||||
| 
 | ||||
|     m.def("multiple_guards_wrong_order", []() { | ||||
|         return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status(); | ||||
|     }, py::call_guard<DependentGuard, CustomGuard>()); | ||||
|     m.def( | ||||
|         "multiple_guards_wrong_order", | ||||
|         []() { | ||||
|             return DependentGuard::report_status() + std::string(" & ") | ||||
|                    + CustomGuard::report_status(); | ||||
|         }, | ||||
|         py::call_guard<DependentGuard, CustomGuard>()); | ||||
| 
 | ||||
| #if defined(WITH_THREAD) && !defined(PYPY_VERSION) | ||||
|     // `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
 | ||||
|     // but it's unclear how to test it without `PyGILState_GetThisThreadState`.
 | ||||
|     auto report_gil_status = []() { | ||||
|         auto is_gil_held = false; | ||||
|         if (auto tstate = py::detail::get_thread_state_unchecked()) | ||||
|         if (auto *tstate = py::detail::get_thread_state_unchecked()) { | ||||
|             is_gil_held = (tstate == PyGILState_GetThisThreadState()); | ||||
|         } | ||||
| 
 | ||||
|         return is_gil_held ? "GIL held" : "GIL released"; | ||||
|     }; | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import pytest | ||||
| 
 | ||||
| import env  # noqa: F401 | ||||
|  |  | |||
|  | @ -7,11 +7,12 @@ | |||
|     BSD-style license that can be found in the LICENSE file. | ||||
| */ | ||||
| 
 | ||||
| #include "pybind11_tests.h" | ||||
| #include "constructor_stats.h" | ||||
| #include <pybind11/functional.h> | ||||
| #include <thread> | ||||
| 
 | ||||
| #include "constructor_stats.h" | ||||
| #include "pybind11_tests.h" | ||||
| 
 | ||||
| #include <thread> | ||||
| 
 | ||||
| int dummy_function(int i) { return i + 1; } | ||||
| 
 | ||||
|  | @ -20,11 +21,12 @@ TEST_SUBMODULE(callbacks, m) { | |||
|     m.def("test_callback1", [](const py::object &func) { return func(); }); | ||||
|     m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); }); | ||||
|     m.def("test_callback3", [](const std::function<int(int)> &func) { | ||||
|         return "func(43) = " + std::to_string(func(43)); }); | ||||
|     m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; }); | ||||
|     m.def("test_callback5", []() { | ||||
|         return py::cpp_function([](int i) { return i+1; }, py::arg("number")); | ||||
|         return "func(43) = " + std::to_string(func(43)); | ||||
|     }); | ||||
|     m.def("test_callback4", | ||||
|           []() -> std::function<int(int)> { return [](int i) { return i + 1; }; }); | ||||
|     m.def("test_callback5", | ||||
|           []() { return py::cpp_function([](int i) { return i + 1; }, py::arg("number")); }); | ||||
| 
 | ||||
|     // test_keyword_args_and_generalized_unpacking
 | ||||
|     m.def("test_tuple_unpacking", [](const py::function &f) { | ||||
|  | @ -51,8 +53,16 @@ TEST_SUBMODULE(callbacks, m) { | |||
|     m.def("test_unpacking_and_keywords2", [](const py::function &f) { | ||||
|         auto kwargs1 = py::dict("a"_a = 1); | ||||
|         auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4); | ||||
|         return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5, | ||||
|                  "key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5); | ||||
|         return f("positional", | ||||
|                  *py::make_tuple(1), | ||||
|                  2, | ||||
|                  *py::make_tuple(3, 4), | ||||
|                  5, | ||||
|                  "key"_a = "value", | ||||
|                  **kwargs1, | ||||
|                  "b"_a = 2, | ||||
|                  **kwargs2, | ||||
|                  "e"_a = 5); | ||||
|     }); | ||||
| 
 | ||||
|     m.def("test_unpacking_error1", [](const py::function &f) { | ||||
|  | @ -136,14 +146,19 @@ TEST_SUBMODULE(callbacks, m) { | |||
|     m.def("dummy_function_overloaded", [](int i, int j) { return i + j; }); | ||||
|     m.def("dummy_function_overloaded", &dummy_function); | ||||
|     m.def("dummy_function2", [](int i, int j) { return i + j; }); | ||||
|     m.def("roundtrip", [](std::function<int(int)> f, bool expect_none = false) { | ||||
|         if (expect_none && f) | ||||
|     m.def( | ||||
|         "roundtrip", | ||||
|         [](std::function<int(int)> f, bool expect_none = false) { | ||||
|             if (expect_none && f) { | ||||
|                 throw std::runtime_error("Expected None to be converted to empty std::function"); | ||||
|             } | ||||
|             return f; | ||||
|     }, py::arg("f"), py::arg("expect_none")=false); | ||||
|         }, | ||||
|         py::arg("f"), | ||||
|         py::arg("expect_none") = false); | ||||
|     m.def("test_dummy_function", [](const std::function<int(int)> &f) -> std::string { | ||||
|         using fn_type = int (*)(int); | ||||
|         auto result = f.target<fn_type>(); | ||||
|         const auto *result = f.target<fn_type>(); | ||||
|         if (!result) { | ||||
|             auto r = f(1); | ||||
|             return "can't convert to function pointer: eval(1) = " + std::to_string(r); | ||||
|  | @ -153,7 +168,6 @@ TEST_SUBMODULE(callbacks, m) { | |||
|             return "matches dummy_function: eval(1) = " + std::to_string(r); | ||||
|         } | ||||
|         return "argument does NOT match dummy_function. This should never happen!"; | ||||
| 
 | ||||
|     }); | ||||
| 
 | ||||
|     class AbstractBase { | ||||
|  | @ -197,7 +211,8 @@ TEST_SUBMODULE(callbacks, m) { | |||
| 
 | ||||
|     // This checks that builtin functions can be passed as callbacks
 | ||||
|     // rather than throwing RuntimeError due to trying to extract as capsule
 | ||||
|     m.def("test_sum_builtin", [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) { | ||||
|     m.def("test_sum_builtin", | ||||
|           [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) { | ||||
|               return sum_builtin(i); | ||||
|           }); | ||||
| 
 | ||||
|  | @ -215,8 +230,9 @@ TEST_SUBMODULE(callbacks, m) { | |||
|         }; | ||||
| 
 | ||||
|         // spawn worker threads
 | ||||
|         for (auto i : work) | ||||
|         for (auto i : work) { | ||||
|             start_f(py::cast<int>(i)); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     m.def("callback_num_times", [](const py::function &f, std::size_t num) { | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue