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,11 +117,12 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
is_instantiated_class = isinstance(collector_function[1],
|
||||
instantiator.InstantiatedClass)
|
||||
|
||||
if is_instantiated_class:
|
||||
function_name = collector_function[0] + \
|
||||
collector_function[1].name + '_' + collector_function[2]
|
||||
else:
|
||||
function_name = collector_function[1].name
|
||||
if function_name is None:
|
||||
if is_instantiated_class:
|
||||
function_name = collector_function[0] + \
|
||||
collector_function[1].name + '_' + collector_function[2]
|
||||
else:
|
||||
function_name = collector_function[1].name
|
||||
|
||||
self.wrapper_map[self.wrapper_id] = (
|
||||
collector_function[0], collector_function[1],
|
||||
|
|
@ -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,6 +339,38 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
|
||||
return check_statement
|
||||
|
||||
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:
|
||||
arg_type = "{ctype}&".format(ctype=ctype_sep)
|
||||
unwrap = '*unwrap_shared_ptr< {ctype} >(in[{id}], "ptr_{ctype_camel}");'.format(
|
||||
ctype=ctype_sep, ctype_camel=ctype_camel, id=arg_id)
|
||||
|
||||
elif self.is_ptr(arg.ctype) and \
|
||||
arg.ctype.typename.name not in self.ignore_namespace:
|
||||
|
||||
arg_type = "{ctype_sep}*".format(ctype_sep=ctype_sep)
|
||||
unwrap = 'unwrap_ptr< {ctype_sep} >(in[{id}], "ptr_{ctype}");'.format(
|
||||
ctype_sep=ctype_sep, ctype=ctype_camel, id=arg_id)
|
||||
|
||||
elif (self.is_shared_ptr(arg.ctype) or self.can_be_pointer(arg.ctype)) and \
|
||||
arg.ctype.typename.name not in self.ignore_namespace:
|
||||
|
||||
arg_type = "{std_boost}::shared_ptr<{ctype_sep}>".format(
|
||||
std_boost='boost' if constructor else 'boost',
|
||||
ctype_sep=ctype_sep)
|
||||
unwrap = 'unwrap_shared_ptr< {ctype_sep} >(in[{id}], "ptr_{ctype}");'.format(
|
||||
ctype_sep=ctype_sep, ctype=ctype_camel, id=arg_id)
|
||||
|
||||
else:
|
||||
arg_type = "{ctype}".format(ctype=arg.ctype.typename.name)
|
||||
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.
|
||||
|
||||
|
|
@ -343,36 +382,7 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
body_args = ''
|
||||
|
||||
for arg in args.list():
|
||||
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:
|
||||
arg_type = "{ctype}&".format(ctype=ctype_sep)
|
||||
unwrap = '*unwrap_shared_ptr< {ctype} >(in[{id}], "ptr_{ctype_camel}");'.format(
|
||||
ctype=ctype_sep, ctype_camel=ctype_camel, id=arg_id)
|
||||
|
||||
elif self.is_ptr(arg.ctype) and \
|
||||
arg.ctype.typename.name not in self.ignore_namespace:
|
||||
|
||||
arg_type = "{ctype_sep}*".format(ctype_sep=ctype_sep)
|
||||
unwrap = 'unwrap_ptr< {ctype_sep} >(in[{id}], "ptr_{ctype}");'.format(
|
||||
ctype_sep=ctype_sep, ctype=ctype_camel, id=arg_id)
|
||||
|
||||
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',
|
||||
ctype_sep=ctype_sep)
|
||||
unwrap = 'unwrap_shared_ptr< {ctype_sep} >(in[{id}], "ptr_{ctype}");'.format(
|
||||
ctype_sep=ctype_sep, ctype=ctype_camel, id=arg_id)
|
||||
|
||||
else:
|
||||
arg_type = "{ctype}".format(ctype=arg.ctype.typename.name)
|
||||
unwrap = 'unwrap< {ctype} >(in[{id}]);'.format(
|
||||
ctype=arg.ctype.typename.name, id=arg_id)
|
||||
arg_type, unwrap = self._unwrap_argument(arg, arg_id, constructor)
|
||||
|
||||
body_args += textwrap.indent(textwrap.dedent('''\
|
||||
{arg_type} {name} = {unwrap}
|
||||
|
|
@ -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_gtsamISAM2Params = 0
|
||||
relinearizeSkip
|
||||
end
|
||||
```
|
||||
|
||||
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
|
||||
ptr_{} = 0
|
||||
{properties}
|
||||
end
|
||||
''').format(class_name)
|
||||
''').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,14 +1085,25 @@ 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
|
||||
''')
|
||||
|
||||
|
|
@ -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,66 +1,75 @@
|
|||
FormatStyle: file
|
||||
|
||||
Checks: '
|
||||
*bugprone*,
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-slicing,
|
||||
clang-analyzer-optin.cplusplus.VirtualCall,
|
||||
google-explicit-constructor,
|
||||
llvm-namespace-comment,
|
||||
misc-misplaced-const,
|
||||
misc-non-copyable-objects,
|
||||
misc-static-assert,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-uniqueptr-reset-release,
|
||||
misc-unused-parameters,
|
||||
modernize-avoid-bind,
|
||||
modernize-make-shared,
|
||||
modernize-redundant-void-arg,
|
||||
modernize-replace-auto-ptr,
|
||||
modernize-replace-disallow-copy-and-assign-macro,
|
||||
modernize-replace-random-shuffle,
|
||||
modernize-shrink-to-fit,
|
||||
modernize-use-auto,
|
||||
modernize-use-bool-literals,
|
||||
modernize-use-equals-default,
|
||||
modernize-use-equals-delete,
|
||||
modernize-use-default-member-init,
|
||||
modernize-use-noexcept,
|
||||
modernize-use-emplace,
|
||||
modernize-use-override,
|
||||
modernize-use-using,
|
||||
*performance*,
|
||||
readability-avoid-const-params-in-decls,
|
||||
readability-const-return-type,
|
||||
readability-container-size-empty,
|
||||
readability-delete-null-pointer,
|
||||
readability-else-after-return,
|
||||
readability-implicit-bool-conversion,
|
||||
readability-make-member-function-const,
|
||||
readability-misplaced-array-index,
|
||||
readability-non-const-parameter,
|
||||
readability-redundant-function-ptr-dereference,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-simplify-subscript-expr,
|
||||
readability-static-accessed-through-instance,
|
||||
readability-static-definition-in-anonymous-namespace,
|
||||
readability-string-compare,
|
||||
readability-suspicious-call-argument,
|
||||
readability-uniqueptr-delete-release,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-reserved-identifier,
|
||||
-bugprone-unused-raii,
|
||||
'
|
||||
Checks: |
|
||||
*bugprone*,
|
||||
*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,
|
||||
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,
|
||||
modernize-replace-disallow-copy-and-assign-macro,
|
||||
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-noexcept,
|
||||
modernize-use-nullptr,
|
||||
modernize-use-override,
|
||||
modernize-use-using,
|
||||
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,
|
||||
readability-simplify-subscript-expr,
|
||||
readability-static-accessed-through-instance,
|
||||
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
|
||||
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"
|
||||
|
||||
|
|
@ -75,7 +71,7 @@ def generate_dummy_code_boost(nclasses=10):
|
|||
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]:
|
||||
print("{")
|
||||
for i in range(0, 10):
|
||||
nclasses = 2 ** i
|
||||
nclasses = 2**i
|
||||
with open("test.cpp", "w") as f:
|
||||
f.write(codegen(nclasses))
|
||||
n1 = dt.datetime.now()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
pybind11_strip(example)
|
||||
if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo)
|
||||
# Strip unnecessary sections of the binary on Linux/macOS
|
||||
pybind11_strip(example)
|
||||
endif()
|
||||
|
||||
set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden"
|
||||
CUDA_VISIBILITY_PRESET "hidden")
|
||||
|
|
@ -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,65 +21,72 @@ 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) {}
|
||||
};
|
||||
|
||||
/// Annotation for operators
|
||||
struct is_operator { };
|
||||
struct is_operator {};
|
||||
|
||||
/// Annotation for classes that cannot be subclassed
|
||||
struct is_final { };
|
||||
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 { };
|
||||
struct multiple_inheritance {};
|
||||
|
||||
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
|
||||
struct dynamic_attr { };
|
||||
struct dynamic_attr {};
|
||||
|
||||
/// Annotation which enables the buffer protocol for a type
|
||||
struct buffer_protocol { };
|
||||
struct buffer_protocol {};
|
||||
|
||||
/// Annotation which requests that a special metaclass is created for a type
|
||||
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) { }
|
||||
explicit metaclass(handle value) : value(value) {}
|
||||
};
|
||||
|
||||
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
|
||||
|
|
@ -99,15 +107,16 @@ 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) {}
|
||||
};
|
||||
|
||||
/// Annotation to mark enums as an arithmetic type
|
||||
struct arithmetic { };
|
||||
struct arithmetic {};
|
||||
|
||||
/// Mark a function for addition at the beginning of the existing overload chain instead of the end
|
||||
struct prepend { };
|
||||
struct prepend {};
|
||||
|
||||
/** \rst
|
||||
A call policy which places one or more guard variables (``Ts...``) around the function call.
|
||||
|
|
@ -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
|
||||
|
|
@ -166,15 +180,16 @@ struct argument_record {
|
|||
bool none : 1; ///< True if None is allowed when loading
|
||||
|
||||
argument_record(const char *name, const char *descr, handle value, bool convert, bool none)
|
||||
: name(name), descr(descr), value(value), convert(convert), none(none) { }
|
||||
: 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.. */
|
||||
|
|
@ -189,13 +204,13 @@ struct function_record {
|
|||
std::vector<argument_record> args;
|
||||
|
||||
/// Pointer to lambda function which converts arguments and performs the actual call
|
||||
handle (*impl) (function_call &) = nullptr;
|
||||
handle (*impl)(function_call &) = nullptr;
|
||||
|
||||
/// Storage for the wrapped function pointer and captured data, if any
|
||||
void *data[3] = { };
|
||||
void *data[3] = {};
|
||||
|
||||
/// Pointer to custom destructor for 'data' (if needed)
|
||||
void (*free_data) (function_record *ptr) = nullptr;
|
||||
void (*free_data)(function_record *ptr) = nullptr;
|
||||
|
||||
/// Return value policy associated with this function
|
||||
return_value_policy policy = return_value_policy::automatic;
|
||||
|
|
@ -251,7 +266,7 @@ struct function_record {
|
|||
struct type_record {
|
||||
PYBIND11_NOINLINE type_record()
|
||||
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
|
||||
default_holder(true), module_local(false), is_final(false) { }
|
||||
default_holder(true), module_local(false), is_final(false) {}
|
||||
|
||||
/// Handle to the parent scope
|
||||
handle scope;
|
||||
|
|
@ -310,42 +325,45 @@ struct type_record {
|
|||
/// Is the class inheritable from python classes?
|
||||
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);
|
||||
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
|
||||
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);
|
||||
}
|
||||
|
||||
/// Tag for a new-style `__init__` defined in `detail/init.h`
|
||||
struct is_new_style_constructor { };
|
||||
struct is_new_style_constructor {};
|
||||
|
||||
/**
|
||||
* Partial template specializations to process custom attributes provided to
|
||||
|
|
@ -353,74 +371,97 @@ 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 *) { }
|
||||
static void precall(function_call &) { }
|
||||
static void postcall(function_call &, handle) { }
|
||||
static void init(const T &, function_record *) {}
|
||||
static void init(const T &, type_record *) {}
|
||||
static void precall(function_call &) {}
|
||||
static void postcall(function_call &, handle) {}
|
||||
};
|
||||
|
||||
/// 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())
|
||||
r->args.emplace_back("self", nullptr, handle(), /*convert=*/ true, /*none=*/ false);
|
||||
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
|
||||
}
|
||||
// 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 <>
|
||||
|
|
@ -544,34 +602,41 @@ template <>
|
|||
struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> { };
|
||||
struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> {};
|
||||
|
||||
/**
|
||||
* Process a keep_alive call policy -- invokes keep_alive_impl during the
|
||||
* 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) { }
|
||||
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 &) { }
|
||||
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 {
|
||||
static void init(const Args&... args, function_record *r) {
|
||||
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);
|
||||
using expander = int[];
|
||||
(void) expander{
|
||||
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||
}
|
||||
static void init(const Args&... args, type_record *r) {
|
||||
static void init(const Args &...args, type_record *r) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||
using expander = int[];
|
||||
|
|
@ -603,7 +668,7 @@ using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extr
|
|||
/// Check the number of named arguments at compile time
|
||||
template <typename... Extra,
|
||||
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
|
||||
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
||||
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
||||
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
|
||||
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
|
||||
|
|
|
|||
|
|
@ -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,55 +44,85 @@ 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)
|
||||
: 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())
|
||||
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()) {
|
||||
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(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { }
|
||||
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>
|
||||
buffer_info(T *ptr, ssize_t size, bool readonly=false)
|
||||
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { }
|
||||
buffer_info(T *ptr, ssize_t size, bool readonly = false)
|
||||
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {}
|
||||
|
||||
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 T *ptr, ssize_t size, bool readonly = true)
|
||||
: 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.
|
||||
* When strides are NULL, build them manually. */
|
||||
view->strides
|
||||
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
||||
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
||||
? 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;
|
||||
}
|
||||
|
||||
buffer_info(const buffer_info &) = delete;
|
||||
buffer_info& operator=(const buffer_info &) = delete;
|
||||
buffer_info &operator=(const buffer_info &) = delete;
|
||||
|
||||
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
|
||||
|
||||
|
|
@ -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) { }
|
||||
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) {}
|
||||
|
||||
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 {
|
||||
static bool compare(const buffer_info& b) {
|
||||
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>> {
|
||||
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")));
|
||||
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")));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,61 +15,59 @@
|
|||
#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>>(
|
||||
days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
|
||||
days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
|
||||
+ seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
|
||||
+ microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
|
||||
return true;
|
||||
}
|
||||
// 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,76 +116,89 @@ 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;
|
||||
|
||||
if (PyDateTime_Check(src.ptr())) {
|
||||
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
|
||||
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
|
||||
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
|
||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
|
||||
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
|
||||
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
|
||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||
cal.tm_isdst = -1;
|
||||
msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
||||
msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
||||
} else if (PyDate_Check(src.ptr())) {
|
||||
cal.tm_sec = 0;
|
||||
cal.tm_min = 0;
|
||||
cal.tm_hour = 0;
|
||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||
cal.tm_sec = 0;
|
||||
cal.tm_min = 0;
|
||||
cal.tm_hour = 0;
|
||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||
cal.tm_isdst = -1;
|
||||
msecs = microseconds(0);
|
||||
msecs = microseconds(0);
|
||||
} else if (PyTime_Check(src.ptr())) {
|
||||
cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
|
||||
cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
|
||||
cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
|
||||
cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
|
||||
cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
|
||||
cal.tm_year = 70; // earliest available date for Python's datetime
|
||||
cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
|
||||
cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
|
||||
cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
|
||||
cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
|
||||
cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
|
||||
cal.tm_year = 70; // earliest available date for Python's datetime
|
||||
cal.tm_isdst = -1;
|
||||
msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
|
||||
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,42 +10,50 @@
|
|||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <complex>
|
||||
|
||||
/// glibc defines I as a macro which breaks things, e.g., boost template names
|
||||
#ifdef I
|
||||
# undef I
|
||||
# undef I
|
||||
#endif
|
||||
|
||||
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 constexpr const char value[3] = {'Z', c, '\0'};
|
||||
static std::string format() { return std::string(value); }
|
||||
};
|
||||
|
||||
#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)
|
||||
# define PYBIND11_BUILTIN_QUALNAME
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
|
||||
#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
|
||||
# ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
# 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);
|
||||
|
|
@ -98,15 +101,17 @@ inline PyTypeObject *make_static_property_type() {
|
|||
inline PyTypeObject *make_static_property_type() {
|
||||
auto d = dict();
|
||||
PyObject *result = PyRun_String(R"(\
|
||||
class pybind11_static_property(property):
|
||||
def __get__(self, obj, cls):
|
||||
return property.__get__(self, cls, cls)
|
||||
class pybind11_static_property(property):
|
||||
def __get__(self, obj, cls):
|
||||
return property.__get__(self, cls, cls)
|
||||
|
||||
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()
|
||||
);
|
||||
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());
|
||||
if (result == nullptr)
|
||||
throw error_already_set();
|
||||
Py_DECREF(result);
|
||||
|
|
@ -119,7 +124,7 @@ inline PyTypeObject *make_static_property_type() {
|
|||
By default, Python replaces the `static_property` itself, but for wrapped C++ types
|
||||
we need to call `static_property.__set__()` in order to propagate the new value to
|
||||
the underlying C++ data structure. */
|
||||
extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) {
|
||||
extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value) {
|
||||
// Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
|
||||
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
|
||||
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
|
||||
|
|
@ -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,27 +205,28 @@ 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)
|
||||
for (auto it = cache.begin(), last = cache.end(); it != last;) {
|
||||
if (it->first == (PyObject *) tinfo->type) {
|
||||
it = cache.erase(it);
|
||||
else
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
delete tinfo;
|
||||
|
|
@ -233,7 +238,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
|||
/** This metaclass is assigned by default to all pybind11 types and is required in order
|
||||
for static properties to function correctly. Users may override this using `py::metaclass`.
|
||||
Return value: New reference. */
|
||||
inline PyTypeObject* make_default_metaclass() {
|
||||
inline PyTypeObject *make_default_metaclass() {
|
||||
constexpr auto *name = "pybind11_type";
|
||||
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||
|
||||
|
|
@ -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,
|
||||
bool (*f)(void * /*parentptr*/, 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,25 +404,32 @@ 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`
|
||||
|
|
@ -418,7 +437,7 @@ inline void clear_instance(PyObject *self) {
|
|||
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)
|
||||
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
|
||||
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;
|
||||
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
|
||||
type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it
|
||||
#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}
|
||||
};
|
||||
{const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, 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;
|
||||
|
|
@ -593,32 +626,29 @@ inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
|
|||
|
||||
/** Create a brand new Python type according to the `type_record` specification.
|
||||
Return value: New reference. */
|
||||
inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
inline PyObject *make_new_python_type(const type_record &rec) {
|
||||
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
|
||||
|
||||
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
|
||||
rec.name);
|
||||
rec.name);
|
||||
|
||||
char *tp_doc = nullptr;
|
||||
if (rec.doc && options::show_user_defined_docstrings()) {
|
||||
|
|
@ -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_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);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,9 +15,9 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
# define PYBIND11_DESCR_CONSTEXPR static constexpr
|
||||
# define PYBIND11_DESCR_CONSTEXPR static constexpr
|
||||
#else
|
||||
# define PYBIND11_DESCR_CONSTEXPR const
|
||||
# define PYBIND11_DESCR_CONSTEXPR const
|
||||
#endif
|
||||
|
||||
/* Concatenate type signatures at compile time */
|
||||
|
|
@ -27,14 +27,14 @@ struct descr {
|
|||
|
||||
constexpr descr() = default;
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
|
||||
constexpr descr(char const (&s)[N + 1]) : descr(s, make_index_sequence<N>()) {}
|
||||
|
||||
template <size_t... Is>
|
||||
constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { }
|
||||
constexpr descr(char const (&s)[N + 1], index_sequence<Is...>) : text{s[Is]..., '\0'} {}
|
||||
|
||||
template <typename... Chars>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { }
|
||||
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} {}
|
||||
|
||||
static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
|
||||
return {{&typeid(Ts)..., nullptr}};
|
||||
|
|
@ -42,81 +42,106 @@ 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<0> const_name(char const(&)[1]) { return {}; }
|
||||
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)...);
|
||||
};
|
||||
|
||||
// Ternary description (like std::conditional)
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> const_name(char const(&text1)[N1], char const(&)[N2]) {
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> const_name(char const (&text1)[N1], char const (&)[N2]) {
|
||||
return const_name(text1);
|
||||
}
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const(&)[N1], char const(&text2)[N2]) {
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const (&)[N1], char const (&text2)[N2]) {
|
||||
return const_name(text2);
|
||||
}
|
||||
|
||||
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.
|
||||
// This block is for backward compatibility only.
|
||||
// (The const_name code is repeated to avoid introducing a "_" #define ourselves.)
|
||||
#ifndef _
|
||||
#define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
# define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
template <size_t N>
|
||||
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);
|
||||
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<N2 - 1>> _(char const(&text1)[N1], char const(&text2)[N2]) {
|
||||
return const_name<B,N1,N2>(text1, text2);
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
|
||||
return const_name<B, N1, N2>(text1, text2);
|
||||
}
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
|
||||
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>(); }
|
||||
#endif // #ifndef _
|
||||
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");
|
||||
}
|
||||
|
|
@ -84,8 +103,8 @@ template <typename Class>
|
|||
template <typename Class>
|
||||
void construct(...) {
|
||||
static_assert(!std::is_same<Class, Class>::value /* always false */,
|
||||
"pybind11::init(): init function must return a compatible pointer, "
|
||||
"holder, or value");
|
||||
"pybind11::init(): init function must return a compatible pointer, "
|
||||
"holder, or value");
|
||||
}
|
||||
|
||||
// Pointer return v1: the factory function returns a class pointer for a registered class.
|
||||
|
|
@ -106,7 +125,7 @@ void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
|
|||
// the holder and destruction happens when we leave the C++ scope, and the holder
|
||||
// class gets to handle the destruction however it likes.
|
||||
v_h.value_ptr() = ptr;
|
||||
v_h.set_instance_registered(true); // To prevent init_instance from registering it
|
||||
v_h.set_instance_registered(true); // To prevent init_instance from registering it
|
||||
v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder
|
||||
Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder
|
||||
v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
|
||||
|
|
@ -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);
|
||||
|
|
@ -152,11 +173,12 @@ template <typename Class>
|
|||
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)
|
||||
"pybind11::init() return-by-value factory function requires a movable class");
|
||||
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
|
||||
|
|
@ -164,7 +186,8 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
|||
// 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));
|
||||
}
|
||||
|
|
@ -173,48 +196,76 @@ void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
|||
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) {
|
||||
v_h.value_ptr() = construct_or_initialize<Cpp<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>
|
||||
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)
|
||||
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<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...);
|
||||
},
|
||||
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>
|
||||
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...);
|
||||
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...);
|
||||
}
|
||||
|
||||
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...);
|
||||
}
|
||||
};
|
||||
|
||||
// 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>
|
||||
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...);
|
||||
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...);
|
||||
}
|
||||
};
|
||||
|
||||
// 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)
|
||||
|
|
@ -232,22 +283,32 @@ struct factory<Func, void_type (*)(), Return(Args...)> {
|
|||
// instance, or the alias needs to be constructible from a `Class &&` argument.
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) && {
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def("__init__", [func = std::move(class_factory)]
|
||||
#else
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def(
|
||||
"__init__",
|
||||
[func = std::move(class_factory)]
|
||||
#else
|
||||
auto &func = class_factory;
|
||||
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...);
|
||||
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...);
|
||||
}
|
||||
};
|
||||
|
||||
// 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 "
|
||||
|
|
@ -260,29 +321,37 @@ struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
|
|||
remove_reference_t<AFunc> alias_factory;
|
||||
|
||||
factory(CFunc &&c, AFunc &&a)
|
||||
: class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) { }
|
||||
: class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {}
|
||||
|
||||
// The class factory is called when the `self` type passed to `__init__` is the direct
|
||||
// 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 "
|
||||
"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)]
|
||||
#else
|
||||
void execute(Class &cl, const Extra &...extra) && {
|
||||
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)]
|
||||
#else
|
||||
auto &class_func = class_factory;
|
||||
auto &alias_func = alias_factory;
|
||||
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:
|
||||
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
|
||||
else
|
||||
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
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:
|
||||
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
|
||||
} else {
|
||||
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
|
||||
}
|
||||
},
|
||||
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...);
|
||||
(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...);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
|
@ -135,9 +137,9 @@ template <typename value_type>
|
|||
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
|
||||
|
||||
struct override_hash {
|
||||
inline size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
|
||||
inline size_t operator()(const std::pair<const PyObject *, const char *> &v) const {
|
||||
size_t value = std::hash<const void *>()(v.first);
|
||||
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2);
|
||||
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value << 6) + (value >> 2);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
|
@ -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::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance*
|
||||
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> inactive_override_cache;
|
||||
// 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;
|
||||
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;
|
||||
|
|
@ -193,8 +200,8 @@ struct type_info {
|
|||
void *(*operator_new)(size_t);
|
||||
void (*init_instance)(instance *, const void *);
|
||||
void (*dealloc)(value_and_holder &v_h);
|
||||
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
|
||||
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
|
||||
std::vector<PyObject *(*) (PyObject *, PyTypeObject *)> implicit_conversions;
|
||||
std::vector<std::pair<const std::type_info *, void *(*) (void *)>> implicit_casts;
|
||||
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
|
||||
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
|
||||
void *get_buffer_data = nullptr;
|
||||
|
|
@ -214,67 +221,71 @@ struct type_info {
|
|||
|
||||
/// On MSVC, debug and release builds are not ABI-compatible!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
#else
|
||||
# define PYBIND11_BUILD_TYPE ""
|
||||
# define PYBIND11_BUILD_TYPE ""
|
||||
#endif
|
||||
|
||||
/// Let's assume that different compilers are ABI-incompatible.
|
||||
/// A user can manually set this string if they know their
|
||||
/// compiler is compatible.
|
||||
#ifndef PYBIND11_COMPILER_TYPE
|
||||
# if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_TYPE "_msvc"
|
||||
# elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_TYPE "_icc"
|
||||
# elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_TYPE "_clang"
|
||||
# elif defined(__PGI)
|
||||
# define PYBIND11_COMPILER_TYPE "_pgi"
|
||||
# elif defined(__MINGW32__)
|
||||
# define PYBIND11_COMPILER_TYPE "_mingw"
|
||||
# elif defined(__CYGWIN__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
|
||||
# elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc"
|
||||
# else
|
||||
# define PYBIND11_COMPILER_TYPE "_unknown"
|
||||
# endif
|
||||
# if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_TYPE "_msvc"
|
||||
# elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_TYPE "_icc"
|
||||
# elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_TYPE "_clang"
|
||||
# elif defined(__PGI)
|
||||
# define PYBIND11_COMPILER_TYPE "_pgi"
|
||||
# elif defined(__MINGW32__)
|
||||
# define PYBIND11_COMPILER_TYPE "_mingw"
|
||||
# elif defined(__CYGWIN__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
|
||||
# elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc"
|
||||
# else
|
||||
# define PYBIND11_COMPILER_TYPE "_unknown"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/// Also standard libs
|
||||
#ifndef PYBIND11_STDLIB
|
||||
# if defined(_LIBCPP_VERSION)
|
||||
# define PYBIND11_STDLIB "_libcpp"
|
||||
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
# define PYBIND11_STDLIB "_libstdcpp"
|
||||
# else
|
||||
# define PYBIND11_STDLIB ""
|
||||
# endif
|
||||
# if defined(_LIBCPP_VERSION)
|
||||
# define PYBIND11_STDLIB "_libcpp"
|
||||
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
# define PYBIND11_STDLIB "_libstdcpp"
|
||||
# else
|
||||
# define PYBIND11_STDLIB ""
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
|
||||
#ifndef PYBIND11_BUILD_ABI
|
||||
# if defined(__GXX_ABI_VERSION)
|
||||
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
|
||||
# else
|
||||
# define PYBIND11_BUILD_ABI ""
|
||||
# endif
|
||||
# if defined(__GXX_ABI_VERSION)
|
||||
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
|
||||
# else
|
||||
# define PYBIND11_BUILD_ABI ""
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PYBIND11_INTERNALS_KIND
|
||||
# if defined(WITH_THREAD)
|
||||
# define PYBIND11_INTERNALS_KIND ""
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_KIND "_without_thread"
|
||||
# endif
|
||||
# if defined(WITH_THREAD)
|
||||
# define PYBIND11_INTERNALS_KIND ""
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_KIND "_without_thread"
|
||||
# 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,16 +404,18 @@ 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.
|
||||
struct gil_scoped_acquire_local {
|
||||
gil_scoped_acquire_local() : state (PyGILState_Ensure()) {}
|
||||
gil_scoped_acquire_local() : state(PyGILState_Ensure()) {}
|
||||
~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)
|
||||
|
|
@ -502,11 +512,10 @@ struct local_internals {
|
|||
|
||||
/// Works like `get_internals`, but for things which are locally registered.
|
||||
inline local_internals &get_local_internals() {
|
||||
static local_internals locals;
|
||||
return locals;
|
||||
static local_internals locals;
|
||||
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
|
||||
|
|
@ -538,7 +547,7 @@ PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
|
|||
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
|
||||
/// such entry exists. Otherwise, a new object of default-constructible type `T` is
|
||||
/// added to the shared data under the given name and a reference to it is returned.
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
T &get_or_create_shared_data(const std::string &name) {
|
||||
auto &internals = detail::get_internals();
|
||||
auto it = internals.shared_data.find(name);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,18 +13,21 @@
|
|||
#include <cstdlib>
|
||||
|
||||
#if defined(__GNUG__)
|
||||
#include <cxxabi.h>
|
||||
# include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
@ -32,10 +35,11 @@ inline void erase_all(std::string &string, const std::string &search) {
|
|||
PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
||||
#if defined(__GNUG__)
|
||||
int status = 0;
|
||||
std::unique_ptr<char, void (*)(void *)> res {
|
||||
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free };
|
||||
if (status == 0)
|
||||
std::unique_ptr<char, void (*)(void *)> res{
|
||||
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free};
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -21,155 +21,187 @@
|
|||
// make it version specific, or even remove it later, but considering that
|
||||
// 1. C4127 is generally far more distracting than useful for modern template code, and
|
||||
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
|
||||
// it is probably best to keep this around indefinitely.
|
||||
// it is probably best to keep this around indefinitely.
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||
# 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>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// 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)
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3,3,0)
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
|
||||
using EigenIndex = Eigen::Index;
|
||||
template<typename Scalar, int Flags, typename StorageIndex>
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
|
||||
#else
|
||||
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
|
||||
template<typename Scalar, int Flags, typename StorageIndex>
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
|
||||
#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!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
|
||||
// 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) {}
|
||||
: 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,
|
||||
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,
|
||||
fixed = size != Eigen::Dynamic, // Fully-fixed size
|
||||
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
|
||||
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,
|
||||
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,
|
||||
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;
|
||||
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;
|
||||
|
||||
// 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),
|
||||
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))
|
||||
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)) {
|
||||
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));
|
||||
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;
|
||||
return {n, 1, stride};
|
||||
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)
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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); });
|
||||
|
|
@ -248,35 +291,42 @@ handle eigen_encapsulate(Type *src) {
|
|||
|
||||
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
|
||||
// types.
|
||||
template<typename Type>
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using props = EigenProps<Type>;
|
||||
|
||||
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
|
||||
|
|
@ -345,30 +397,31 @@ public:
|
|||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return &value; }
|
||||
operator Type *() { return &value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&() { return value; }
|
||||
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>;
|
||||
operator Type &&() && { return std::move(value); }
|
||||
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,66 +516,93 @@ 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;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return ref.get(); }
|
||||
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>;
|
||||
operator Type &() { return *ref; }
|
||||
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,10 +632,11 @@ 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>
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
||||
|
|
@ -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,36 +666,42 @@ 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());
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
std::move(nnz),
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
const_cast<Type&>(src).makeCompressed();
|
||||
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[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
|
||||
"scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
|
|
|||
|
|
@ -16,22 +16,12 @@
|
|||
#include <vector>
|
||||
|
||||
#if defined(PYPY_VERSION)
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
# 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
|
||||
#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(); }
|
||||
|
||||
/** \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
|
||||
using init_t = PyObject *(*) ();
|
||||
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,8 +265,9 @@ public:
|
|||
scoped_interpreter &operator=(scoped_interpreter &&) = delete;
|
||||
|
||||
~scoped_interpreter() {
|
||||
if (is_valid)
|
||||
if (is_valid) {
|
||||
finalize_interpreter();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -11,24 +11,24 @@
|
|||
|
||||
#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.
|
||||
if (!global.contains("__builtins__"))
|
||||
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
|
||||
#else
|
||||
(void) global;
|
||||
#endif
|
||||
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
|
||||
// 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
|
||||
(void) global;
|
||||
#endif
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
|
@ -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)
|
||||
|
|
@ -19,18 +20,21 @@ template <typename Return, typename... Args>
|
|||
struct type_caster<std::function<Return(Args...)>> {
|
||||
using type = std::function<Return(Args...)>;
|
||||
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
||||
using function_type = Return (*) (Args...);
|
||||
using function_type = Return (*)(Args...);
|
||||
|
||||
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,10 +62,11 @@ public:
|
|||
|
||||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
#if !defined(NDEBUG)
|
||||
if (!tstate)
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
#endif
|
||||
# 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);
|
||||
} else {
|
||||
|
|
@ -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)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||
if (tstate->gilstate_counter < 0)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
#endif
|
||||
# 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) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
}
|
||||
# endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
#if !defined(NDEBUG)
|
||||
if (!release)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
#endif
|
||||
# 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)
|
||||
PyEval_SaveThread();
|
||||
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 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;
|
||||
}
|
||||
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
|
||||
|
|
@ -100,27 +95,26 @@ private:
|
|||
if (pbase() != pptr()) { // If buffer is not empty
|
||||
gil_scoped_acquire tmp;
|
||||
// This subtraction cannot be negative, so dropping the sign.
|
||||
auto size = static_cast<size_t>(pptr() - pbase());
|
||||
auto size = static_cast<size_t>(pptr() - pbase());
|
||||
size_t remainder = utf8_remainder();
|
||||
|
||||
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)
|
||||
|
|
@ -129,17 +123,14 @@ public:
|
|||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
}
|
||||
|
||||
pythonbuf(pythonbuf&&) = default;
|
||||
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,10 +211,12 @@ 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() {
|
||||
|
|
|
|||
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 {
|
||||
|
|
@ -30,126 +68,126 @@ enum op_type : int {
|
|||
op_u /* unary operator */
|
||||
};
|
||||
|
||||
struct self_t { };
|
||||
struct self_t {};
|
||||
static const self_t self = self_t();
|
||||
|
||||
/// Type for an unused type slot
|
||||
struct undefined_t { };
|
||||
struct undefined_t {};
|
||||
|
||||
/// Don't warn about an unused variable
|
||||
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> { \
|
||||
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> { \
|
||||
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); } \
|
||||
}; \
|
||||
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 &) { \
|
||||
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 &) { \
|
||||
return op_<op_##id, op_r, T, self_t>(); \
|
||||
}
|
||||
#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> { \
|
||||
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> { \
|
||||
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); } \
|
||||
}; \
|
||||
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 &) { \
|
||||
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 &) { \
|
||||
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> { \
|
||||
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 &) { \
|
||||
return op_<op_##id, op_l, self_t, 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> { \
|
||||
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 &) { \
|
||||
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> { \
|
||||
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); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
|
||||
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||
}
|
||||
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \
|
||||
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); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
|
||||
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r)
|
||||
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r)
|
||||
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||
//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l *r)
|
||||
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l &r)
|
||||
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||
// PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||
// WARNING: This usage of `abs` should only be done for existing STL overloads.
|
||||
// Adding overloads directly in to the `std::` namespace is advised against:
|
||||
// https://en.cppreference.com/w/cpp/language/extending_std
|
||||
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||
PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
|
||||
PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
|
||||
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||
PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
|
||||
PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
|
||||
|
||||
#undef PYBIND11_BINARY_OPERATOR
|
||||
#undef PYBIND11_INPLACE_OPERATOR
|
||||
|
|
|
|||
|
|
@ -15,43 +15,54 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||
|
||||
class options {
|
||||
public:
|
||||
|
||||
// Default RAII constructor, which leaves settings as they currently are.
|
||||
options() : previous_state(global_state()) {}
|
||||
|
||||
// Class is non-copyable.
|
||||
options(const options&) = delete;
|
||||
options& operator=(const options&) = delete;
|
||||
options(const options &) = delete;
|
||||
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; }
|
||||
|
||||
// This type is not meant to be allocated on the heap.
|
||||
void* operator new(size_t) = delete;
|
||||
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_user_defined_docstrings = true; //< Include user-supplied texts 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,26 +9,27 @@
|
|||
|
||||
#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.
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
# include <optional>
|
||||
# include <optional>
|
||||
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||
# include <experimental/optional>
|
||||
# include <experimental/optional>
|
||||
#endif
|
||||
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
# include <variant>
|
||||
# include <variant>
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
|
@ -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,13 +72,16 @@ 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();
|
||||
}
|
||||
|
|
@ -81,21 +89,23 @@ template <typename Type, typename Key> struct set_caster {
|
|||
PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
using key_conv = make_caster<Key>;
|
||||
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;
|
||||
|
|
@ -143,7 +161,7 @@ template <typename Type, typename Value> struct list_caster {
|
|||
|
||||
private:
|
||||
template <
|
||||
typename T = Type,
|
||||
typename T = Type,
|
||||
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
|
||||
void reserve_maybe(const sequence &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
|
|
@ -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>>
|
||||
: array_caster<std::array<Type, Size>, Type, false, 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>>
|
||||
: set_caster<std::set<Key, Compare, Alloc>, Key> { };
|
||||
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>>
|
||||
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> { };
|
||||
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>>
|
||||
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> { };
|
||||
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>>
|
||||
: map_caster<std::unordered_map<Key, Value, Hash, Equal, 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>>
|
||||
: 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);
|
||||
}
|
||||
|
|
@ -263,11 +303,12 @@ template<typename Type, typename Value = typename Type::value_type> struct optio
|
|||
return false;
|
||||
}
|
||||
if (src.is_none()) {
|
||||
return true; // default-constructed value is already empty
|
||||
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
|
||||
|
||||
|
|
@ -309,7 +352,7 @@ struct variant_caster_visitor {
|
|||
/// `namespace::variant` types which provide a `namespace::visit()` function are handled here
|
||||
/// automatically using argument-dependent lookup. Users can provide specializations for other
|
||||
/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
|
||||
template <template<typename...> class Variant>
|
||||
template <template <typename...> class Variant>
|
||||
struct visit_helper {
|
||||
template <typename... Args>
|
||||
static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) {
|
||||
|
|
@ -318,9 +361,10 @@ 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>
|
||||
template <template <typename...> class V, typename... Ts>
|
||||
struct variant_caster<V<Ts...>> {
|
||||
static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative.");
|
||||
|
||||
|
|
@ -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...>> { };
|
||||
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,54 +4,60 @@
|
|||
|
||||
#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>) && \
|
||||
PY_VERSION_HEX >= 0x03060000
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# endif
|
||||
# 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) {
|
||||
#if !defined(PYPY_VERSION)
|
||||
static PyObject *unicode_from_fs_native(const std::string &w) {
|
||||
# if !defined(PYPY_VERSION)
|
||||
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
|
||||
#else
|
||||
# else
|
||||
// PyPy mistakenly declares the first parameter as non-const.
|
||||
return PyUnicode_DecodeFSDefaultAndSize(
|
||||
const_cast<char*>(w.c_str()), ssize_t(w.size()));
|
||||
#endif
|
||||
return PyUnicode_DecodeFSDefaultAndSize(const_cast<char *>(w.c_str()), ssize_t(w.size()));
|
||||
# endif
|
||||
}
|
||||
|
||||
static PyObject* unicode_from_fs_native(const std::wstring& w) {
|
||||
static PyObject *unicode_from_fs_native(const std::wstring &w) {
|
||||
return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
|
||||
}
|
||||
|
||||
public:
|
||||
static handle cast(const T& path, return_value_policy, handle) {
|
||||
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))
|
||||
.release();
|
||||
return module_::import("pathlib")
|
||||
.attr("Path")(reinterpret_steal<object>(py_str))
|
||||
.release();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -60,15 +66,15 @@ public:
|
|||
// PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
|
||||
// calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
|
||||
// issue #3168) so we do it ourselves instead.
|
||||
PyObject* buf = PyOS_FSPath(handle.ptr());
|
||||
PyObject *buf = PyOS_FSPath(handle.ptr());
|
||||
if (!buf) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
PyObject* native = nullptr;
|
||||
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,9 +82,9 @@ 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.
|
||||
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,137 +19,148 @@ 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;
|
||||
};
|
||||
|
||||
/* Default: is_comparable -> std::false_type */
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct is_comparable : std::false_type { };
|
||||
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>>
|
||||
: std::true_type { };
|
||||
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_>
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) {
|
||||
cl.def(init<const Vector &>(), "Copy constructor");
|
||||
}
|
||||
|
||||
template<typename Vector, typename Class_>
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
|
||||
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",
|
||||
[](Vector &v, const T &value) { v.push_back(value); },
|
||||
arg("x"),
|
||||
"Add an item to the end of the list");
|
||||
cl.def(
|
||||
"append",
|
||||
[](Vector &v, const T &value) { v.push_back(value); },
|
||||
arg("x"),
|
||||
"Add an item to the end of the list");
|
||||
|
||||
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());
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
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");
|
||||
|
||||
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,29 +222,27 @@ 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) {
|
||||
i = wrap_i(i, v.size());
|
||||
v[(SizeType)i] = 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);
|
||||
|
||||
for (size_t i=0; i<slicelength; ++i) {
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
seq->push_back(v[start]);
|
||||
start += step;
|
||||
}
|
||||
|
|
@ -239,36 +253,40 @@ 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) {
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
v[start] = value[i];
|
||||
start += step;
|
||||
}
|
||||
},
|
||||
"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_>
|
||||
|
|
@ -293,31 +313,34 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl)
|
|||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
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];
|
||||
return v[(SizeType) i];
|
||||
},
|
||||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Vector &v) {
|
||||
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 */
|
||||
cl.def(
|
||||
"__iter__",
|
||||
[](Vector &v) {
|
||||
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 */
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -327,53 +350,60 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
|
|||
using T = typename Vector::value_type;
|
||||
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)
|
||||
throw index_error();
|
||||
if ((SizeType)i >= v.size())
|
||||
throw index_error();
|
||||
return v[(SizeType)i];
|
||||
using ItType = typename Vector::iterator;
|
||||
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];
|
||||
});
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Vector &v) {
|
||||
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 */
|
||||
cl.def(
|
||||
"__iter__",
|
||||
[](Vector &v) {
|
||||
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__",
|
||||
[name](Vector &v) {
|
||||
cl.def(
|
||||
"__repr__",
|
||||
[name](Vector &v) {
|
||||
std::ostringstream s;
|
||||
s << name << '[';
|
||||
for (size_type i=0; i < v.size(); ++i) {
|
||||
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
|
||||
|
|
@ -388,26 +418,37 @@ constexpr bool args_any_are_buffer() {
|
|||
|
||||
// Add the buffer interface to a vector
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
void vector_buffer_impl(Class_& cl, std::true_type) {
|
||||
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)});
|
||||
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)});
|
||||
});
|
||||
|
||||
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);
|
||||
T *p = static_cast<T *>(info.ptr);
|
||||
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
|
||||
T *end = p + info.shape[0] * step;
|
||||
if (step == 1) {
|
||||
|
|
@ -415,21 +456,22 @@ 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;
|
||||
}
|
||||
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
void vector_buffer_impl(Class_&, std::false_type) {}
|
||||
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>...>{});
|
||||
void vector_buffer(Class_ &cl) {
|
||||
vector_buffer_impl<Vector, Class_, Args...>(
|
||||
cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{});
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
|
@ -438,13 +480,13 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
// std::vector
|
||||
//
|
||||
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
|
||||
class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args&&... args) {
|
||||
class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args &&...args) {
|
||||
using Class_ = class_<Vector, holder_type>;
|
||||
|
||||
// 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,90 +568,93 @@ 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) {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) it->second = v;
|
||||
else m.emplace(k, 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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,
|
||||
Class_> &cl) {
|
||||
// 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,
|
||||
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) {
|
||||
// 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...
|
||||
m.erase(r.first);
|
||||
m.emplace(k, 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...
|
||||
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__",
|
||||
[name](Map &m) {
|
||||
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
|
||||
{
|
||||
template <typename Map>
|
||||
struct keys_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct values_view
|
||||
{
|
||||
template <typename Map>
|
||||
struct values_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct items_view
|
||||
{
|
||||
template <typename Map>
|
||||
struct items_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
|
||||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) {
|
||||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
using KeysView = detail::keys_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__",
|
||||
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
|
||||
);
|
||||
|
||||
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",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("keys",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
cl.def(
|
||||
"values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
cl.def(
|
||||
"items",
|
||||
[](Map &m) { return ItemsView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
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())
|
||||
throw key_error();
|
||||
return it->second;
|
||||
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 {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
return false;
|
||||
return true;
|
||||
cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end()) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
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) {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
throw key_error();
|
||||
m.erase(it);
|
||||
}
|
||||
);
|
||||
cl.def("__delitem__", [](Map &m, const KeyType &k) {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end()) {
|
||||
throw key_error();
|
||||
}
|
||||
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 {
|
||||
auto it = view.map.find(k);
|
||||
if (it == view.map.end())
|
||||
return false;
|
||||
return true;
|
||||
keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool {
|
||||
auto it = view.map.find(k);
|
||||
if (it == view.map.end()) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
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)
|
||||
|
||||
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:
|
||||
# Don't trigger -Wunused-parameter.
|
||||
f.write("int main (int, char **) { return 0; }")
|
||||
fname = Path("flagcheck.cpp")
|
||||
# Don't trigger -Wunused-parameter.
|
||||
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:
|
||||
|
||||
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]))
|
||||
return exts
|
||||
|
||||
for path in paths:
|
||||
for prefix, parent in package_dir.items():
|
||||
if path.startswith(parent):
|
||||
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]))
|
||||
break
|
||||
else:
|
||||
found = False
|
||||
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)
|
||||
)
|
||||
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,53 +1,50 @@
|
|||
#!/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
|
||||
elif len(flds) == 2:
|
||||
level_serial = flds[1]
|
||||
for level in ("a", "b", "c", "dev"):
|
||||
if level_serial.startswith(level):
|
||||
serial = int(level_serial[len(level) :])
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
major = int(matches["MAJOR"])
|
||||
minor = int(matches["MINOR"])
|
||||
flds = patch_level_serial.split(".")
|
||||
if flds:
|
||||
patch = int(flds[0])
|
||||
if len(flds) == 1:
|
||||
level = "0"
|
||||
serial = 0
|
||||
elif len(flds) == 2:
|
||||
level_serial = flds[1]
|
||||
for level in ("a", "b", "c", "dev"):
|
||||
if level_serial.startswith(level):
|
||||
serial = int(level_serial[len(level) :])
|
||||
break
|
||||
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"
|
||||
"for i in range(2):"
|
||||
" gc.collect()\n",
|
||||
Py_file_input, globals, globals);
|
||||
PyObject *result = PyRun_String("import gc\n"
|
||||
"for i in range(2):"
|
||||
" gc.collect()\n",
|
||||
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,19 +152,22 @@ 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;
|
||||
}
|
||||
|
||||
// Gets constructor stats from a C++ type index
|
||||
static ConstructorStats& get(std::type_index type) {
|
||||
static ConstructorStats &get(std::type_index type) {
|
||||
static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
|
||||
return all_cstats[type];
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -165,11 +175,12 @@ public:
|
|||
}
|
||||
|
||||
// Gets constructor stats from a Python class
|
||||
static ConstructorStats& get(py::object class_) {
|
||||
static ConstructorStats &get(py::object class_) {
|
||||
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
|
||||
} // 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*),
|
||||
"Function pointer must have the same size as void*");
|
||||
PyModule_AddObject(m, "gil_acquire_funcaddr",
|
||||
PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire)));
|
||||
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)));
|
||||
}
|
||||
|
||||
#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,12 +1,13 @@
|
|||
#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) { }
|
||||
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();}
|
||||
explicit LocalException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
|
@ -48,8 +49,9 @@ private:
|
|||
// Exception that will be registered with register_local_exception_translator
|
||||
class LocalSimpleException : public std::exception {
|
||||
public:
|
||||
explicit LocalSimpleException(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
explicit LocalSimpleException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
|
@ -58,17 +60,16 @@ PYBIND11_MAKE_OPAQUE(LocalVec);
|
|||
PYBIND11_MAKE_OPAQUE(LocalVec2);
|
||||
PYBIND11_MAKE_OPAQUE(LocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalVec);
|
||||
//PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
|
||||
// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
|
||||
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; });
|
||||
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;
|
||||
});
|
||||
};
|
||||
|
||||
// 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 <atomic>
|
||||
#include "constructor_stats.h"
|
||||
# include "constructor_stats.h"
|
||||
|
||||
# include <atomic>
|
||||
|
||||
/// Reference counted object base class
|
||||
class Object {
|
||||
|
|
@ -26,20 +28,23 @@ 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 };
|
||||
mutable std::atomic<int> m_refCount{0};
|
||||
};
|
||||
|
||||
// Tag class used to track constructions of ref objects. When we track constructors, below, we
|
||||
|
|
@ -58,84 +63,105 @@ 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;
|
||||
}
|
||||
|
||||
/// Overwrite this reference with another reference
|
||||
ref& operator=(const ref& r) {
|
||||
ref &operator=(const ref &r) {
|
||||
if (this == &r) {
|
||||
return *this;
|
||||
}
|
||||
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");
|
||||
ref &operator=(T *ptr) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -146,31 +172,32 @@ public:
|
|||
bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }
|
||||
|
||||
/// Compare this reference with a pointer
|
||||
bool operator==(const T* ptr) const { return m_ptr == ptr; }
|
||||
bool operator==(const T *ptr) const { return m_ptr == ptr; }
|
||||
|
||||
/// Compare this reference with a pointer
|
||||
bool operator!=(const T* ptr) const { return m_ptr != ptr; }
|
||||
bool operator!=(const T *ptr) const { return m_ptr != ptr; }
|
||||
|
||||
/// Access the object referenced by this reference
|
||||
T* operator->() { return m_ptr; }
|
||||
T *operator->() { return m_ptr; }
|
||||
|
||||
/// Access the object referenced by this reference
|
||||
const T* operator->() const { return m_ptr; }
|
||||
const T *operator->() const { return m_ptr; }
|
||||
|
||||
/// Return a C++ reference to the referenced object
|
||||
T& operator*() { return *m_ptr; }
|
||||
T &operator*() { return *m_ptr; }
|
||||
|
||||
/// Return a const C++ reference to the referenced object
|
||||
const T& operator*() const { return *m_ptr; }
|
||||
const T &operator*() const { return *m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
explicit operator T* () { return m_ptr; }
|
||||
explicit operator T *() { return m_ptr; }
|
||||
|
||||
/// Return a const pointer to the referenced object
|
||||
T* get_ptr() { return m_ptr; }
|
||||
T *get_ptr() { return m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
const T* get_ptr() const { return m_ptr; }
|
||||
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,37 +30,45 @@ 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(); });
|
||||
m.def("throw_local_error", []() { throw LocalException("just local"); });
|
||||
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);
|
||||
} catch (const shared_exception &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const shared_exception &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// translate the local exception into a key error but only in this module
|
||||
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const LocalException &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const LocalException &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// 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")
|
||||
|
|
@ -79,13 +104,14 @@ PYBIND11_MODULE(pybind11_tests, m) {
|
|||
.def("get_value", &UserType::value, "Get value using a method")
|
||||
.def("set_value", &UserType::set, "Set value using a method")
|
||||
.def_property("value", &UserType::value, &UserType::set, "Get/set value using a property")
|
||||
.def("__repr__", [](const UserType& u) { return "UserType({})"_s.format(u.value()); });
|
||||
.def("__repr__", [](const UserType &u) { return "UserType({})"_s.format(u.value()); });
|
||||
|
||||
py::class_<IncType, UserType>(m, "IncType")
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def("__repr__", [](const IncType& u) { return "IncType({})"_s.format(u.value()); });
|
||||
.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;
|
||||
|
|
@ -26,13 +20,13 @@ public:
|
|||
void test_submodule_##name(py::module_ &(variable))
|
||||
|
||||
/// Dummy type which is not exported anywhere -- something to trigger a conversion error
|
||||
struct UnregisteredType { };
|
||||
struct UnregisteredType {};
|
||||
|
||||
/// A user-defined type which is exported and can be used by any test
|
||||
class UserType {
|
||||
public:
|
||||
UserType() = default;
|
||||
explicit UserType(int i) : i(i) { }
|
||||
explicit UserType(int i) : i(i) {}
|
||||
|
||||
int value() const { return i; }
|
||||
void set(int set) { i = set; }
|
||||
|
|
@ -46,7 +40,7 @@ class IncType : public UserType {
|
|||
public:
|
||||
using UserType::UserType;
|
||||
IncType() = default;
|
||||
IncType(const IncType &other) : IncType(other.value() + 1) { }
|
||||
IncType(const IncType &other) : IncType(other.value() + 1) {}
|
||||
IncType(IncType &&) = delete;
|
||||
IncType &operator=(const IncType &) = delete;
|
||||
IncType &operator=(IncType &&) = delete;
|
||||
|
|
@ -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,12 +11,11 @@
|
|||
|
||||
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<>())
|
||||
.def("__await__", [](const SupportsAsync& self) -> py::object {
|
||||
.def("__await__", [](const SupportsAsync &self) -> py::object {
|
||||
static_cast<void>(self);
|
||||
py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")();
|
||||
py::object f = loop.attr("create_future")();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
m_data = new float[(size_t) (rows*cols)];
|
||||
// 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,27 +59,33 @@ 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;
|
||||
}
|
||||
|
||||
float operator()(py::ssize_t i, py::ssize_t j) const {
|
||||
return m_data[(size_t) (i*m_cols + j)];
|
||||
return m_data[(size_t) (i * m_cols + j)];
|
||||
}
|
||||
|
||||
float &operator()(py::ssize_t i, py::ssize_t j) {
|
||||
return m_data[(size_t) (i*m_cols + j)];
|
||||
return m_data[(size_t) (i * m_cols + j)];
|
||||
}
|
||||
|
||||
float *data() { return m_data; }
|
||||
|
||||
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,24 +111,25 @@ 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
|
||||
.def_buffer([](Matrix &m) -> py::buffer_info {
|
||||
return py::buffer_info(
|
||||
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) }
|
||||
);
|
||||
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)});
|
||||
});
|
||||
|
||||
// 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}) {}
|
||||
|
|
@ -165,7 +175,7 @@ TEST_SUBMODULE(buffers, m) {
|
|||
.def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value)
|
||||
.def_buffer(&ConstBuffer::get_buffer_info);
|
||||
|
||||
struct DerivedBuffer : public Buffer { };
|
||||
struct DerivedBuffer : public Buffer {};
|
||||
py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
|
||||
|
|
@ -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,64 +7,67 @@
|
|||
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;
|
||||
int tag;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <>
|
||||
class type_caster<ConstRefCasted> {
|
||||
public:
|
||||
static constexpr auto name = const_name<ConstRefCasted>();
|
||||
public:
|
||||
static constexpr auto name = const_name<ConstRefCasted>();
|
||||
|
||||
// Input is unimportant, a new value will always be constructed based on the
|
||||
// cast operator.
|
||||
bool load(handle, bool) { return true; }
|
||||
// Input is unimportant, a new value will always be constructed based on the
|
||||
// cast operator.
|
||||
bool load(handle, bool) { return true; }
|
||||
|
||||
explicit operator ConstRefCasted &&() {
|
||||
value = {1};
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return std::move(value);
|
||||
}
|
||||
explicit operator ConstRefCasted &() {
|
||||
value = {2};
|
||||
return value;
|
||||
}
|
||||
explicit operator ConstRefCasted *() {
|
||||
value = {3};
|
||||
return &value;
|
||||
}
|
||||
explicit operator ConstRefCasted &&() {
|
||||
value = {1};
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return std::move(value);
|
||||
}
|
||||
explicit operator ConstRefCasted &() {
|
||||
value = {2};
|
||||
return value;
|
||||
}
|
||||
explicit operator ConstRefCasted *() {
|
||||
value = {3};
|
||||
return &value;
|
||||
}
|
||||
|
||||
explicit operator const ConstRefCasted &() {
|
||||
value = {4};
|
||||
return value;
|
||||
}
|
||||
explicit operator const ConstRefCasted *() {
|
||||
value = {5};
|
||||
return &value;
|
||||
}
|
||||
explicit operator const ConstRefCasted &() {
|
||||
value = {4};
|
||||
return value;
|
||||
}
|
||||
explicit operator const ConstRefCasted *() {
|
||||
value = {5};
|
||||
return &value;
|
||||
}
|
||||
|
||||
// custom cast_op to explicitly propagate types to the conversion operators.
|
||||
template <typename T_>
|
||||
using cast_op_type =
|
||||
/// const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*,
|
||||
conditional_t<
|
||||
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&,
|
||||
/* else */ConstRefCasted&&>>>>;
|
||||
// custom cast_op to explicitly propagate types to the conversion operators.
|
||||
template <typename T_>
|
||||
using cast_op_type =
|
||||
/// const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, const ConstRefCasted *>::value,
|
||||
const ConstRefCasted *,
|
||||
conditional_t<
|
||||
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 &,
|
||||
/* else */ ConstRefCasted &&>>>>;
|
||||
|
||||
private:
|
||||
ConstRefCasted value = {0};
|
||||
private:
|
||||
ConstRefCasted value = {0};
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
|
@ -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(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_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
|
||||
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
|
||||
m.def("bad_utf8_string", []() {
|
||||
return std::string("abc\xd0"
|
||||
"def");
|
||||
});
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
|
||||
// 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)});
|
||||
});
|
||||
}
|
||||
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'; });
|
||||
|
||||
|
|
@ -130,34 +157,61 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
// test_string_view
|
||||
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||
m.attr("has_string_view") = true;
|
||||
m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); });
|
||||
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; });
|
||||
# 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_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); });
|
||||
m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; });
|
||||
# endif
|
||||
m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; });
|
||||
# endif
|
||||
|
||||
struct TypeWithBothOperatorStringAndStringView {
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
|
|
@ -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,31 +243,40 @@ 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) {
|
||||
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
|
||||
}, "Return a triple in reversed order");
|
||||
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");
|
||||
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; });
|
||||
m.def("return_none_char", []() -> const char * { return nullptr; });
|
||||
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
||||
m.def("return_none_int", []() -> int * { return nullptr; });
|
||||
m.def("return_none_float", []() -> float * { return nullptr; });
|
||||
m.def("return_none_pair", []() -> std::pair<int,int> * { return nullptr; });
|
||||
m.def("return_none_char", []() -> const char * { return nullptr; });
|
||||
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
||||
m.def("return_none_int", []() -> int * { return nullptr; });
|
||||
m.def("return_none_float", []() -> float * { return nullptr; });
|
||||
m.def("return_none_pair", []() -> std::pair<int, int> * { return nullptr; });
|
||||
|
||||
// test_none_deferred
|
||||
m.def("defer_none_cstring", [](char *) { return false; });
|
||||
|
|
@ -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);
|
||||
|
|
@ -257,17 +324,20 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
|
||||
// Not currently supported (std::pair caster has return-by-value cast operator);
|
||||
// triggers static_assert failure.
|
||||
//m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
||||
// m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
||||
|
||||
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));
|
||||
}
|
||||
return l;
|
||||
}, "copy"_a);
|
||||
m.def(
|
||||
"refwrap_list",
|
||||
[](bool copy) {
|
||||
static IncType x1(1), x2(2);
|
||||
py::list l;
|
||||
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);
|
||||
|
||||
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
|
||||
m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) {
|
||||
|
|
@ -284,12 +354,13 @@ 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;});
|
||||
m.def("long_cast", []() {return (long) 42;});
|
||||
m.def("longlong_cast", []() {return ULLONG_MAX;});
|
||||
m.def("int_cast", []() { return (int) 42; });
|
||||
m.def("long_cast", []() { return (long) 42; });
|
||||
m.def("longlong_cast", []() { return ULLONG_MAX; });
|
||||
|
||||
/// test void* cast operator
|
||||
m.def("test_void_caster", []() -> bool {
|
||||
|
|
@ -300,11 +371,12 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
|
||||
// Tests const/non-const propagation in cast_op.
|
||||
m.def("takes", [](ConstRefCasted x) { return x.tag; });
|
||||
m.def("takes_move", [](ConstRefCasted&& x) { return x.tag; });
|
||||
m.def("takes_ptr", [](ConstRefCasted* x) { return x->tag; });
|
||||
m.def("takes_ref", [](ConstRefCasted& x) { return x.tag; });
|
||||
m.def("takes_move", [](ConstRefCasted &&x) { return x.tag; });
|
||||
m.def("takes_ptr", [](ConstRefCasted *x) { return x->tag; });
|
||||
m.def("takes_ref", [](ConstRefCasted &x) { return x.tag; });
|
||||
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_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; });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,22 +202,21 @@ 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_view_memoryview() == "Have some 🎂".encode()
|
||||
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"
|
||||
assert m.str_from_type_with_both_operator_string_and_string_view() == "success"
|
||||
|
|
@ -224,20 +228,8 @@ 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"
|
||||
assert m.i64_str(-999999999999) == "-999999999999"
|
||||
assert m.u64_str(999999999999) == "999999999999"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u32_str(-1)
|
||||
|
|
@ -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,18 +40,17 @@ 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:
|
||||
Parent() { py::print("Allocating parent."); }
|
||||
Parent(const Parent& parent) = default;
|
||||
Parent(const Parent &parent) = default;
|
||||
~Parent() { py::print("Releasing parent."); }
|
||||
void addChild(Child *) { }
|
||||
void addChild(Child *) {}
|
||||
Child *returnChild() { return new Child(); }
|
||||
Child *returnNullChild() { return nullptr; }
|
||||
static Child *staticFunction(Parent*) { return new Child(); }
|
||||
static Child *staticFunction(Parent *) { return new Child(); }
|
||||
};
|
||||
py::class_<Parent>(m, "Parent")
|
||||
.def(py::init<>())
|
||||
|
|
@ -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) {
|
||||
|
|
@ -34,9 +36,9 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
});
|
||||
|
||||
m.def("test_dict_unpacking", [](const py::function &f) {
|
||||
auto d1 = py::dict("key"_a="value", "a"_a=1);
|
||||
auto d1 = py::dict("key"_a = "value", "a"_a = 1);
|
||||
auto d2 = py::dict();
|
||||
auto d3 = py::dict("b"_a=2);
|
||||
auto d3 = py::dict("b"_a = 2);
|
||||
return f("positional", 1, **d1, **d2, **d3);
|
||||
});
|
||||
|
||||
|
|
@ -44,32 +46,40 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
|
||||
m.def("test_unpacking_and_keywords1", [](const py::function &f) {
|
||||
auto args = py::make_tuple(2);
|
||||
auto kwargs = py::dict("d"_a=4);
|
||||
return f(1, *args, "c"_a=3, **kwargs);
|
||||
auto kwargs = py::dict("d"_a = 4);
|
||||
return f(1, *args, "c"_a = 3, **kwargs);
|
||||
});
|
||||
|
||||
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);
|
||||
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);
|
||||
});
|
||||
|
||||
m.def("test_unpacking_error1", [](const py::function &f) {
|
||||
auto kwargs = py::dict("x"_a=3);
|
||||
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
|
||||
auto kwargs = py::dict("x"_a = 3);
|
||||
return f("x"_a = 1, "y"_a = 2, **kwargs); // duplicate ** after keyword
|
||||
});
|
||||
|
||||
m.def("test_unpacking_error2", [](const py::function &f) {
|
||||
auto kwargs = py::dict("x"_a=3);
|
||||
return f(**kwargs, "x"_a=1); // duplicate keyword after **
|
||||
auto kwargs = py::dict("x"_a = 3);
|
||||
return f(**kwargs, "x"_a = 1); // duplicate keyword after **
|
||||
});
|
||||
|
||||
m.def("test_arg_conversion_error1",
|
||||
[](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
|
||||
|
||||
m.def("test_arg_conversion_error2", [](const py::function &f) {
|
||||
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567);
|
||||
f(234, "expected_name"_a = UnregisteredType(), "kw"_a = 567);
|
||||
});
|
||||
|
||||
// test_lambda_closure_cleanup
|
||||
|
|
@ -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)
|
||||
throw std::runtime_error("Expected None to be converted to empty std::function");
|
||||
return f;
|
||||
}, py::arg("f"), py::arg("expect_none")=false);
|
||||
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);
|
||||
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 {
|
||||
|
|
@ -185,7 +199,7 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
// test_movable_object
|
||||
m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
|
||||
auto x = MovableObject();
|
||||
f(x); // lvalue reference shouldn't move out object
|
||||
f(x); // lvalue reference shouldn't move out object
|
||||
return x.valid; // must still return `true`
|
||||
});
|
||||
|
||||
|
|
@ -197,9 +211,10 @@ 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) {
|
||||
return sum_builtin(i);
|
||||
});
|
||||
m.def("test_sum_builtin",
|
||||
[](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
|
||||
return sum_builtin(i);
|
||||
});
|
||||
|
||||
// test async Python callbacks
|
||||
using callback_f = std::function<void(int)>;
|
||||
|
|
@ -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