Fixes to support Python >= 3.3 in ns3 python bindings

This commit is contained in:
Gustavo Carneiro
2014-03-02 23:48:27 +00:00
parent 60e2a6c117
commit ff3054b592
8 changed files with 37 additions and 370 deletions

View File

@@ -1,3 +1,4 @@
from __future__ import print_function
import warnings
import sys
import os
@@ -76,11 +77,11 @@ def main(argv):
try:
from callbacks_list import callback_classes
except ImportError, ex:
print >> sys.stderr, "***************", repr(ex)
except ImportError as ex:
print("***************", repr(ex), file=sys.stderr)
callback_classes = []
else:
print >> sys.stderr, ">>>>>>>>>>>>>>>>", repr(callback_classes)
print(">>>>>>>>>>>>>>>>", repr(callback_classes), file=sys.stderr)
finally:
sys.path.pop(0)

View File

@@ -1,3 +1,5 @@
from __future__ import print_function
import sys
import re
from pybindgen.typehandlers import base as typehandlers
@@ -26,11 +28,13 @@ class SmartPointerTransformation(typehandlers.TypeTransformation):
def __init__(self):
super(SmartPointerTransformation, self).__init__()
self.rx = re.compile(r'(ns3::|::ns3::|)Ptr<([^>]+)>\s*$')
print("{!r}".format(self), file=sys.stderr)
def _get_untransformed_type_traits(self, name):
m = self.rx.match(name)
is_const = False
if m is None:
print("{!r} did not match".format(name), file=sys.stderr)
return None, False
else:
name1 = m.group(2).strip()
@@ -61,9 +65,9 @@ class SmartPointerTransformation(typehandlers.TypeTransformation):
## fix the ctype, add ns3:: namespace
orig_ctype, is_const = self._get_untransformed_type_traits(args[0])
if is_const:
correct_ctype = 'ns3::Ptr< %s const >' % orig_ctype[:-2]
correct_ctype = 'ns3::Ptr< {} const >'.format(orig_ctype[:-2])
else:
correct_ctype = 'ns3::Ptr< %s >' % orig_ctype[:-2]
correct_ctype = 'ns3::Ptr< {} >'.format(orig_ctype[:-2])
args = tuple([correct_ctype] + list(args[1:]))
handler = type_handler(*args, **kwargs)
@@ -84,58 +88,6 @@ typehandlers.param_type_matcher.register_transformation(transf)
del transf
class ArgvParam(Parameter):
"""
Converts a python list-of-strings argument to a pair of 'int argc,
char *argv[]' arguments to pass into C.
One Python argument becomes two C function arguments -> it's a miracle!
Note: this parameter type handler is not registered by any name;
must be used explicitly.
"""
DIRECTIONS = [Parameter.DIRECTION_IN]
CTYPES = []
def convert_c_to_python(self, wrapper):
raise NotImplementedError
def convert_python_to_c(self, wrapper):
py_name = wrapper.declarations.declare_variable('PyObject*', 'py_' + self.name)
argc_var = wrapper.declarations.declare_variable('int', 'argc')
name = wrapper.declarations.declare_variable('char**', self.name)
idx = wrapper.declarations.declare_variable('Py_ssize_t', 'idx')
wrapper.parse_params.add_parameter('O!', ['&PyList_Type', '&'+py_name], self.name)
#wrapper.before_call.write_error_check('!PyList_Check(%s)' % py_name) # XXX
wrapper.before_call.write_code("%s = (char **) malloc(sizeof(char*)*PyList_Size(%s));"
% (name, py_name))
wrapper.before_call.add_cleanup_code('free(%s);' % name)
wrapper.before_call.write_code('''
for (%(idx)s = 0; %(idx)s < PyList_Size(%(py_name)s); %(idx)s++)
{
''' % vars())
wrapper.before_call.sink.indent()
wrapper.before_call.write_code('''
PyObject *item = PyList_GET_ITEM(%(py_name)s, %(idx)s);
''' % vars())
#wrapper.before_call.write_error_check('item == NULL')
wrapper.before_call.write_error_check(
'!PyString_Check(item)',
failure_cleanup=('PyErr_SetString(PyExc_TypeError, '
'"argument %s must be a list of strings");') % self.name)
wrapper.before_call.write_code(
'%s[%s] = PyString_AsString(item);' % (name, idx))
wrapper.before_call.sink.unindent()
wrapper.before_call.write_code('}')
wrapper.before_call.write_code('%s = PyList_Size(%s);' % (argc_var, py_name))
wrapper.call_params.append(argc_var)
wrapper.call_params.append(name)
class CallbackImplProxyMethod(typehandlers.ReverseWrapperBase):
"""
Class that generates a proxy virtual method that calls a similarly named python method.
@@ -204,7 +156,7 @@ public:
kwargs = {}
try:
return_type = ReturnValue.new(str(return_ctype), **kwargs)
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError) as ex:
warnings.warn("***** Unable to register callback; Return value '%s' error (used in %s): %r"
% (callback_return, cls_name, ex),
Warning)
@@ -223,7 +175,7 @@ public:
kwargs = {}
try:
arguments.append(Parameter.new(str(param_ctype), arg_name, **kwargs))
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError) as ex:
warnings.warn("***** Unable to register callback; parameter '%s %s' error (used in %s): %r"
% (arg_type, arg_name, cls_name, ex),
Warning)
@@ -241,7 +193,7 @@ public:
class PythonCallbackParameter(Parameter):
"Class handlers"
CTYPES = [cls_name]
print >> sys.stderr, "***** registering callback handler: %r" % ctypeparser.normalize_type_string(cls_name)
print("***** registering callback handler: %r" % ctypeparser.normalize_type_string(cls_name), file=sys.stderr)
DIRECTIONS = [Parameter.DIRECTION_IN]
PYTHON_CALLBACK_IMPL_NAME = class_name
TEMPLATE_ARGS = template_parameters

View File

@@ -13,7 +13,7 @@ from waflib.Errors import WafError
# after = TaskGen.after
## https://launchpad.net/pybindgen/
REQUIRED_PYBINDGEN_VERSION = (0, 17, 0, 866)
REQUIRED_PYBINDGEN_VERSION = (0, 17, 0, 868)
REQUIRED_PYGCCXML_VERSION = (0, 9, 5)
RUN_ME=-3
@@ -165,7 +165,7 @@ def configure(conf):
else:
out = subprocess.Popen([conf.env['PYTHON'][0], "-c",
"import pybindgen.version; "
"print '.'.join([str(x) for x in pybindgen.version.__version__])"],
"print('.'.join([str(x) for x in pybindgen.version.__version__]))"],
stdout=subprocess.PIPE).communicate()[0]
pybindgen_version_str = out.strip()
pybindgen_version = tuple([int(x) for x in pybindgen_version_str.split('.')])

View File

@@ -1,10 +1,10 @@
from __future__ import absolute_import
# "from _core import *" doesn't work here because symbols starting
# with underscore would not be imported. But they are needed because
# other modules depend on them...
import _core
from . import _core
g = globals()
for k,v in _core.__dict__.iteritems():
for k,v in _core.__dict__.items():
g[k] = v
del g, k, v, _core

View File

@@ -2,6 +2,10 @@
#include "ns3/ref-count-base.h"
#include <unistd.h>
#if PY_VERSION_HEX >= 0x03000000
# define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask
# define PyString_FromStringAndSize PyUnicode_FromStringAndSize
#endif
namespace {

View File

@@ -2474,10 +2474,10 @@ def register_Ns3SystemThread_methods(root_module, cls):
return
def register_Ns3Time_methods(root_module, cls):
#cls.add_binary_numeric_operator('*', root_module['ns3::Time'], root_module['ns3::Time'], param('int64_t const &', u'right'))
cls.add_binary_numeric_operator('*', root_module['ns3::Time'], root_module['ns3::Time'], param('int64_t const &', u'right'))
cls.add_binary_numeric_operator('+', root_module['ns3::Time'], root_module['ns3::Time'], param('ns3::Time const &', u'right'))
cls.add_binary_numeric_operator('-', root_module['ns3::Time'], root_module['ns3::Time'], param('ns3::Time const &', u'right'))
#cls.add_binary_numeric_operator('/', root_module['ns3::Time'], root_module['ns3::Time'], param('int64_t const &', u'right'))
cls.add_binary_numeric_operator('/', root_module['ns3::Time'], root_module['ns3::Time'], param('int64_t const &', u'right'))
cls.add_binary_comparison_operator('<')
cls.add_binary_comparison_operator('>')
cls.add_binary_comparison_operator('!=')

View File

@@ -13,77 +13,6 @@ import warnings
from pybindgen.typehandlers.base import CodeGenerationError
# class SmartPointerTransformation(typehandlers.TypeTransformation):
# """
# This class provides a "type transformation" that tends to support
# NS-3 smart pointers. Parameters such as "Ptr<Foo> foo" are
# transformed into something like Parameter.new("Foo*", "foo",
# transfer_ownership=False). Return values such as Ptr<Foo> are
# transformed into ReturnValue.new("Foo*",
# caller_owns_return=False). Since the underlying objects have
# reference counting, PyBindGen does the right thing.
# """
# def __init__(self):
# super(SmartPointerTransformation, self).__init__()
# self.rx = re.compile(r'(ns3::|::ns3::|)Ptr<([^>]+)>\s*$')
# def _get_untransformed_type_traits(self, name):
# m = self.rx.match(name)
# is_const = False
# if m is None:
# return None, False
# else:
# name1 = m.group(2).strip()
# if name1.startswith('const '):
# name1 = name1[len('const '):]
# is_const = True
# if name1.endswith(' const'):
# name1 = name1[:-len(' const')]
# is_const = True
# new_name = name1+' *'
# if new_name.startswith('::'):
# new_name = new_name[2:]
# return new_name, is_const
# def get_untransformed_name(self, name):
# new_name, dummy_is_const = self._get_untransformed_type_traits(name)
# return new_name
# def create_type_handler(self, type_handler, *args, **kwargs):
# if issubclass(type_handler, Parameter):
# kwargs['transfer_ownership'] = False
# elif issubclass(type_handler, ReturnValue):
# kwargs['caller_owns_return'] = False
# else:
# raise AssertionError
# ## fix the ctype, add ns3:: namespace
# orig_ctype, is_const = self._get_untransformed_type_traits(args[0])
# if is_const:
# correct_ctype = 'ns3::Ptr< %s const >' % orig_ctype[:-2]
# else:
# correct_ctype = 'ns3::Ptr< %s >' % orig_ctype[:-2]
# args = tuple([correct_ctype] + list(args[1:]))
# handler = type_handler(*args, **kwargs)
# handler.set_tranformation(self, orig_ctype)
# return handler
# def untransform(self, type_handler, declarations, code_block, expression):
# return 'const_cast<%s> (ns3::PeekPointer (%s))' % (type_handler.untransformed_ctype, expression)
# def transform(self, type_handler, declarations, code_block, expression):
# assert type_handler.untransformed_ctype[-1] == '*'
# return 'ns3::Ptr< %s > (%s)' % (type_handler.untransformed_ctype[:-1], expression)
# ## register the type transformation
# transf = SmartPointerTransformation()
# typehandlers.return_type_matcher.register_transformation(transf)
# typehandlers.param_type_matcher.register_transformation(transf)
# del transf
class ArgvParam(Parameter):
"""
Converts a python list-of-strings argument to a pair of 'int argc,
@@ -123,11 +52,21 @@ PyObject *item = PyList_GET_ITEM(%(py_name)s, %(idx)s);
''' % vars())
#wrapper.before_call.write_error_check('item == NULL')
wrapper.before_call.write_error_check(
'!PyString_Check(item)',
'\n'
'#if PY_VERSION_HEX >= 0x03000000\n'
'!PyUnicode_Check(item)\n'
'#else\n'
'!PyString_Check(item)\n'
'#endif\n',
failure_cleanup=('PyErr_SetString(PyExc_TypeError, '
'"argument %s must be a list of strings");') % self.name)
wrapper.before_call.write_code(
'%s[%s] = PyString_AsString(item);' % (name, idx))
'#if PY_VERSION_HEX >= 0x03000000\n'
'{var}[{idx}] = PyUnicode_AsUTF8(item);\n'
'#else\n'
'{var}[{idx}] = PyString_AsString(item);\n'
'#endif\n'
.format(var=name, idx=idx))
wrapper.before_call.sink.unindent()
wrapper.before_call.write_code('}')
wrapper.before_call.write_code('%s = PyList_Size(%s);' % (argc_var, py_name))
@@ -136,165 +75,6 @@ PyObject *item = PyList_GET_ITEM(%(py_name)s, %(idx)s);
wrapper.call_params.append(name)
# class CallbackImplProxyMethod(typehandlers.ReverseWrapperBase):
# """
# Class that generates a proxy virtual method that calls a similarly named python method.
# """
# def __init__(self, return_value, parameters):
# super(CallbackImplProxyMethod, self).__init__(return_value, parameters)
# def generate_python_call(self):
# """code to call the python method"""
# build_params = self.build_params.get_parameters(force_tuple_creation=True)
# if build_params[0][0] == '"':
# build_params[0] = '(char *) ' + build_params[0]
# args = self.before_call.declare_variable('PyObject*', 'args')
# self.before_call.write_code('%s = Py_BuildValue(%s);'
# % (args, ', '.join(build_params)))
# self.before_call.add_cleanup_code('Py_DECREF(%s);' % args)
# self.before_call.write_code('py_retval = PyObject_CallObject(m_callback, %s);' % args)
# self.before_call.write_error_check('py_retval == NULL')
# self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
# def generate_callback_classes(out, callbacks):
# for callback_impl_num, template_parameters in enumerate(callbacks):
# sink = MemoryCodeSink()
# cls_name = "ns3::Callback< %s >" % ', '.join(template_parameters)
# #print >> sys.stderr, "***** trying to register callback: %r" % cls_name
# class_name = "PythonCallbackImpl%i" % callback_impl_num
# sink.writeln('''
# class %s : public ns3::CallbackImpl<%s>
# {
# public:
# PyObject *m_callback;
# %s(PyObject *callback)
# {
# Py_INCREF(callback);
# m_callback = callback;
# }
# virtual ~%s()
# {
# Py_DECREF(m_callback);
# m_callback = NULL;
# }
# virtual bool IsEqual(ns3::Ptr<const ns3::CallbackImplBase> other_base) const
# {
# const %s *other = dynamic_cast<const %s*> (ns3::PeekPointer (other_base));
# if (other != NULL)
# return (other->m_callback == m_callback);
# else
# return false;
# }
# ''' % (class_name, ', '.join(template_parameters), class_name, class_name, class_name, class_name))
# sink.indent()
# callback_return = template_parameters[0]
# return_ctype = ctypeparser.parse_type(callback_return)
# if ('const' in return_ctype.remove_modifiers()):
# kwargs = {'is_const': True}
# else:
# kwargs = {}
# try:
# return_type = ReturnValue.new(str(return_ctype), **kwargs)
# except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
# warnings.warn("***** Unable to register callback; Return value '%s' error (used in %s): %r"
# % (callback_return, cls_name, ex),
# Warning)
# continue
# arguments = []
# ok = True
# callback_parameters = [arg for arg in template_parameters[1:] if arg != 'ns3::empty']
# for arg_num, arg_type in enumerate(callback_parameters):
# arg_name = 'arg%i' % (arg_num+1)
# param_ctype = ctypeparser.parse_type(arg_type)
# if ('const' in param_ctype.remove_modifiers()):
# kwargs = {'is_const': True}
# else:
# kwargs = {}
# try:
# arguments.append(Parameter.new(str(param_ctype), arg_name, **kwargs))
# except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
# warnings.warn("***** Unable to register callback; parameter '%s %s' error (used in %s): %r"
# % (arg_type, arg_name, cls_name, ex),
# Warning)
# ok = False
# if not ok:
# continue
# wrapper = CallbackImplProxyMethod(return_type, arguments)
# wrapper.generate(sink, 'operator()', decl_modifiers=[])
# sink.unindent()
# sink.writeln('};\n')
# sink.flush_to(out)
# class PythonCallbackParameter(Parameter):
# "Class handlers"
# CTYPES = [cls_name]
# #print >> sys.stderr, "***** registering callback handler: %r" % ctypeparser.normalize_type_string(cls_name)
# DIRECTIONS = [Parameter.DIRECTION_IN]
# PYTHON_CALLBACK_IMPL_NAME = class_name
# TEMPLATE_ARGS = template_parameters
# def convert_python_to_c(self, wrapper):
# "parses python args to get C++ value"
# assert isinstance(wrapper, typehandlers.ForwardWrapperBase)
# if self.default_value is None:
# py_callback = wrapper.declarations.declare_variable('PyObject*', self.name)
# wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name)
# wrapper.before_call.write_error_check(
# '!PyCallable_Check(%s)' % py_callback,
# 'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
# callback_impl = wrapper.declarations.declare_variable(
# 'ns3::Ptr<%s>' % self.PYTHON_CALLBACK_IMPL_NAME,
# '%s_cb_impl' % self.name)
# wrapper.before_call.write_code("%s = ns3::Create<%s> (%s);"
# % (callback_impl, self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
# wrapper.call_params.append(
# 'ns3::Callback<%s> (%s)' % (', '.join(self.TEMPLATE_ARGS), callback_impl))
# else:
# py_callback = wrapper.declarations.declare_variable('PyObject*', self.name, 'NULL')
# wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name, optional=True)
# value = wrapper.declarations.declare_variable(
# 'ns3::Callback<%s>' % ', '.join(self.TEMPLATE_ARGS),
# self.name+'_value',
# self.default_value)
# wrapper.before_call.write_code("if (%s) {" % (py_callback,))
# wrapper.before_call.indent()
# wrapper.before_call.write_error_check(
# '!PyCallable_Check(%s)' % py_callback,
# 'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
# wrapper.before_call.write_code("%s = ns3::Callback<%s> (ns3::Create<%s> (%s));"
# % (value, ', '.join(self.TEMPLATE_ARGS),
# self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
# wrapper.before_call.unindent()
# wrapper.before_call.write_code("}") # closes: if (py_callback) {
# wrapper.call_params.append(value)
# def convert_c_to_python(self, wrapper):
# raise typehandlers.NotSupportedError("Reverse wrappers for ns3::Callback<...> types "
# "(python using callbacks defined in C++) not implemented.")
# def write_preamble(out):
# pybindgen.write_preamble(out)
# out.writeln("#include \"ns3/everything.h\"")
def Simulator_customizations(module):
Simulator = module['ns3::Simulator']
@@ -327,76 +107,6 @@ def CommandLine_customizations(module):
flags=["METH_VARARGS", "METH_KEYWORDS"])
# def Object_customizations(module):
# ## ---------------------------------------------------------------------
# ## Here we generate custom constructor code for all classes that
# ## derive from ns3::Object. The custom constructors are needed in
# ## order to support kwargs only and to translate kwargs into ns3
# ## attributes, etc.
# ## ---------------------------------------------------------------------
# Object = module['ns3::Object']
# ## add a GetTypeId method to all generatd helper classes
# def helper_class_hook(helper_class):
# decl = """
# static ns3::TypeId GetTypeId (void)
# {
# static ns3::TypeId tid = ns3::TypeId ("%s")
# .SetParent< %s > ()
# ;
# return tid;
# }""" % (helper_class.name, helper_class.class_.full_name)
# helper_class.add_custom_method(decl)
# helper_class.add_post_generation_code(
# "NS_OBJECT_ENSURE_REGISTERED (%s);" % helper_class.name)
# Object.add_helper_class_hook(helper_class_hook)
# def ns3_object_instance_creation_function(cpp_class, code_block, lvalue,
# parameters, construct_type_name):
# assert lvalue
# assert not lvalue.startswith('None')
# if cpp_class.cannot_be_constructed:
# raise CodeGenerationError("%s cannot be constructed (%s)"
# % cpp_class.full_name)
# if cpp_class.incomplete_type:
# raise CodeGenerationError("%s cannot be constructed (incomplete type)"
# % cpp_class.full_name)
# code_block.write_code("%s = new %s(%s);" % (lvalue, construct_type_name, parameters))
# code_block.write_code("%s->Ref ();" % (lvalue))
# def ns3_object_post_instance_creation_function(cpp_class, code_block, lvalue,
# parameters, construct_type_name):
# code_block.write_code("ns3::CompleteConstruct(%s);" % (lvalue, ))
# Object.set_instance_creation_function(ns3_object_instance_creation_function)
# Object.set_post_instance_creation_function(ns3_object_post_instance_creation_function)
# def Attribute_customizations(module):
# # Fix up for the "const AttributeValue &v = EmptyAttribute()"
# # case, as used extensively by helper classes.
# # Here's why we need to do this: pybindgen.gccxmlscanner, when
# # scanning parameter default values, is only provided with the
# # value as a simple C expression string. (py)gccxml does not
# # report the type of the default value.
# # As a workaround, here we iterate over all parameters of all
# # methods of all classes and tell pybindgen what is the type of
# # the default value for attributes.
# for cls in module.classes:
# for meth in cls.get_all_methods():
# for param in meth.parameters:
# if isinstance(param, cppclass.CppClassRefParameter):
# if param.cpp_class.name == 'AttributeValue' \
# and param.default_value is not None \
# and param.default_value_type is None:
# param.default_value_type = 'ns3::EmptyAttributeValue'
def TypeId_customizations(module):
TypeId = module['ns3::TypeId']
TypeId.add_custom_method_wrapper("LookupByNameFailSafe", "_wrap_TypeId_LookupByNameFailSafe",

View File

@@ -5,7 +5,7 @@ def post_register_types(root_module):
if 'EmuFdNetDevice' not in enabled_features:
if 'ns3::EmuFdNetDeviceHelper'in root_module:
root_module.classes.remove(root_module['ns3::EmuFdNetDeviceHelper'])
root_module.classes.remove(root_module['ns3::EmuFdNetDeviceHelper'])
if 'TapFdNetDevice' not in enabled_features:
if 'ns3::TapFdNetDeviceHelper'in root_module: