[pypy-commit] pypy default: merge heads
arigo
pypy.commits at gmail.com
Sat Jul 14 03:47:52 EDT 2018
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r94867:46410c58b280
Date: 2018-07-14 09:47 +0200
http://bitbucket.org/pypy/pypy/changeset/46410c58b280/
Log: merge heads
diff --git a/pypy/doc/config/objspace.disable_entrypoints.txt b/pypy/doc/config/objspace.disable_entrypoints.txt
new file mode 100644
diff --git a/pypy/doc/config/objspace.fstrings.txt b/pypy/doc/config/objspace.fstrings.txt
new file mode 100644
diff --git a/pypy/doc/config/objspace.hash.txt b/pypy/doc/config/objspace.hash.txt
new file mode 100644
diff --git a/pypy/doc/config/objspace.usemodules._frozen_importlib.txt b/pypy/doc/config/objspace.usemodules._frozen_importlib.txt
new file mode 100644
diff --git a/pypy/doc/config/objspace.usemodules._jitlog.txt b/pypy/doc/config/objspace.usemodules._jitlog.txt
new file mode 100644
diff --git a/pypy/doc/config/objspace.usemodules.faulthandler.txt b/pypy/doc/config/objspace.usemodules.faulthandler.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.backendopt.replace_we_are_jitted.txt b/pypy/doc/config/translation.backendopt.replace_we_are_jitted.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.jit_opencoder_model.txt b/pypy/doc/config/translation.jit_opencoder_model.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.keepgoing.txt b/pypy/doc/config/translation.keepgoing.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.libname.txt b/pypy/doc/config/translation.libname.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.lto.txt b/pypy/doc/config/translation.lto.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.profoptargs.txt b/pypy/doc/config/translation.profoptargs.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.reverse_debugger.txt b/pypy/doc/config/translation.reverse_debugger.txt
new file mode 100644
diff --git a/pypy/doc/config/translation.split_gc_address_space.txt b/pypy/doc/config/translation.split_gc_address_space.txt
new file mode 100644
diff --git a/pypy/doc/tool/makecontributor.py b/pypy/doc/tool/makecontributor.py
--- a/pypy/doc/tool/makecontributor.py
+++ b/pypy/doc/tool/makecontributor.py
@@ -18,12 +18,13 @@
'Antonio Cuni': ['antocuni', 'anto'],
'Armin Rigo': ['arigo', 'arfigo', 'armin', 'arigato'],
'Maciej Fijalkowski': ['fijal'],
- 'Carl Friedrich Bolz-Tereick': ['Carl Friedrich Bolz', 'cfbolz', 'cf'],
+ 'Carl Friedrich Bolz-Tereick': ['Carl Friedrich Bolz', 'cfbolz', 'cf', 'cbolz'],
'Samuele Pedroni': ['pedronis', 'samuele', 'samule'],
- 'Richard Plangger':['planrich'],
- 'Michael Hudson': ['mwh'],
+ 'Richard Plangger': ['planrich', 'plan_rich'],
+ 'Remi Meier': ['remi'],
+ 'Michael Hudson-Doyle': ['mwh', 'Michael Hudson'],
'Holger Krekel': ['hpk', 'holger krekel', 'holger', 'hufpk'],
- "Amaury Forgeot d'Arc": ['afa'],
+ "Amaury Forgeot d'Arc": ['afa', 'amauryfa at gmail.com'],
'Alex Gaynor': ['alex', 'agaynor'],
'David Schneider': ['bivab', 'david'],
'Christian Tismer': ['chris', 'christian', 'tismer',
@@ -41,7 +42,7 @@
'Mark Pearse': ['mwp'],
'Toon Verwaest': ['tverwaes'],
'Eric van Riet Paap': ['ericvrp'],
- 'Jacob Hallen': ['jacob', 'jakob'],
+ 'Jacob Hallen': ['jacob', 'jakob', 'jacob hallen'],
'Anders Lehmann': ['ale', 'anders'],
'Bert Freudenberg': ['bert'],
'Boris Feigin': ['boris', 'boria'],
@@ -69,19 +70,25 @@
'Manuel Jacob': ['mjacob'],
'Rami Chowdhury': ['necaris'],
'Stanislaw Halik': ['Stanislaw Halik', 'w31rd0'],
- 'Wenzhu Man':['wenzhu man', 'wenzhuman'],
- 'Anton Gulenko':['anton gulenko', 'anton_gulenko'],
- 'Richard Lancaster':['richardlancaster'],
- 'William Leslie':['William ML Leslie'],
- 'Spenser Bauman':['Spenser Andrew Bauman'],
- 'Raffael Tfirst':['raffael.tfirst at gmail.com'],
- 'timo':['timo at eistee.fritz.box'],
- 'Jasper Schulz':['Jasper.Schulz', 'jbs'],
- 'Aaron Gallagher':['"Aaron Gallagher'],
- 'Yasir Suhail':['yasirs'],
+ 'Wenzhu Man': ['wenzhu man', 'wenzhuman'],
+ 'Anton Gulenko': ['anton gulenko', 'anton_gulenko'],
+ 'Richard Lancaster': ['richardlancaster'],
+ 'William Leslie': ['William ML Leslie'],
+ 'Spenser Bauman': ['Spenser Andrew Bauman'],
+ 'Raffael Tfirst': ['raffael.tfirst at gmail.com'],
+ 'timo': ['timo at eistee.fritz.box'],
+ 'Jasper Schulz': ['Jasper.Schulz', 'jbs'],
+ 'Aaron Gallagher': ['"Aaron Gallagher'],
+ 'Yasir Suhail': ['yasirs'],
'Squeaky': ['squeaky'],
- "Amaury Forgeot d'Arc": ['amauryfa at gmail.com'],
"Dodan Mihai": ['mihai.dodan at gmail.com'],
+ 'Wim Lavrijsen': ['wlav'],
+ 'Toon Verwaest': ['toon', 'tverwaes'],
+ 'Seo Sanghyeon': ['sanxiyn'],
+ 'Leonardo Santagada': ['santagada'],
+ 'Laurence Tratt': ['ltratt'],
+ 'Pieter Zieschang': ['pzieschang', 'p_zieschang at yahoo.de'],
+ 'John Witulski': ['witulski'],
}
alias_map = {}
@@ -103,7 +110,8 @@
return set()
ignore_words = ['around', 'consulting', 'yesterday', 'for a bit', 'thanks',
'in-progress', 'bits of', 'even a little', 'floating',
- 'a bit', 'reviewing']
+ 'a bit', 'reviewing', 'looking', 'advising', 'partly', 'ish',
+ 'watching', 'mostly', 'jumping']
sep_words = ['and', ';', '+', '/', 'with special by']
nicknames = match.group(1)
for word in ignore_words:
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -7,9 +7,12 @@
.. branch: cppyy-packaging
-Upgrade to backend 1.1.0, improved handling of templated methods and
+Upgrade to backend 1.2.0, improved handling of templated methods and
functions (in particular automatic deduction of types), improved pythonization
-interface, and a range of compatibility fixes for Python3
+interface, range of compatibility fixes for Python3, free functions now take
+fast libffi path when possible, moves for strings (incl. from Python str),
+easier/faster handling of std::vector by numpy, improved and faster object
+identity preservation
.. branch: socket_default_timeout_blockingness
diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py
--- a/pypy/module/_cppyy/converter.py
+++ b/pypy/module/_cppyy/converter.py
@@ -1,15 +1,12 @@
import sys
from pypy.interpreter.error import OperationError, oefmt
-
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat
from rpython.rlib import rfloat, rawrefcount
-
from pypy.module._rawffi.interp_rawffi import letter2tp
from pypy.module._rawffi.array import W_ArrayInstance
-
-from pypy.module._cppyy import helper, capi, ffitypes
+from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews
# Converter objects are used to translate between RPython and C++. They are
# defined by the type name for which they provide conversion. Uses are for
@@ -149,7 +146,8 @@
# read access, so no copy needed
address_value = self._get_raw_address(space, w_obj, offset)
address = rffi.cast(rffi.ULONG, address_value)
- return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address)
+ return lowlevelviews.W_LowLevelView(
+ space, letter2tp(space, self.typecode), self.size, address)
def to_memory(self, space, w_obj, w_value, offset):
# copy the full array (uses byte copy for now)
@@ -190,7 +188,8 @@
# read access, so no copy needed
address_value = self._get_raw_address(space, w_obj, offset)
address = rffi.cast(rffi.ULONGP, address_value)
- return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address[0])
+ return lowlevelviews.W_LowLevelView(
+ space, letter2tp(space, self.typecode), self.size, address[0])
def to_memory(self, space, w_obj, w_value, offset):
# copy only the pointer value
@@ -438,7 +437,7 @@
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.get_nullptr(space)
shape = letter2tp(space, 'P')
- return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
+ return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval)
def to_memory(self, space, w_obj, w_value, offset):
address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset))
@@ -491,7 +490,7 @@
from pypy.module._cppyy.interp_cppyy import W_CPPInstance
if isinstance(w_obj, W_CPPInstance):
from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_RVALUE
- if w_obj.flags & INSTANCE_FLAGS_IS_RVALUE:
+ if w_obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE:
# reject moves as all are explicit
raise ValueError("lvalue expected")
if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl):
@@ -523,14 +522,14 @@
from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE
obj = space.interp_w(W_CPPInstance, w_obj)
if obj:
- if obj.flags & INSTANCE_FLAGS_IS_RVALUE:
- obj.flags &= ~INSTANCE_FLAGS_IS_RVALUE
+ if obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE:
+ obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE
try:
return InstanceRefConverter._unwrap_object(self, space, w_obj)
except Exception:
# TODO: if the method fails on some other converter, then the next
# overload can not be an rvalue anymore
- obj.flags |= INSTANCE_FLAGS_IS_RVALUE
+ obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE
raise
raise oefmt(space.w_ValueError, "object is not an rvalue")
@@ -566,10 +565,6 @@
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False)
- def to_memory(self, space, w_obj, w_value, offset):
- address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset))
- address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value))
-
class InstancePtrPtrConverter(InstancePtrConverter):
typecode = 'o'
@@ -597,6 +592,25 @@
return interp_cppyy.wrap_cppinstance(
space, address, self.clsdecl, do_cast=False, is_ref=True)
+ def to_memory(self, space, w_obj, w_value, offset):
+ # the actual data member is of object* type, but we receive a pointer to that
+ # data member in order to modify its value, so by convention, the internal type
+ # used is object**
+ address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset))
+ from pypy.module._cppyy.interp_cppyy import W_CPPInstance
+ cppinstance = space.interp_w(W_CPPInstance, w_value, can_be_None=True)
+ if cppinstance:
+ rawobject = cppinstance.get_rawobject()
+ offset = capi.c_base_offset(space, cppinstance.clsdecl, self.clsdecl, rawobject, 1)
+ obj_address = capi.direct_ptradd(rawobject, offset)
+ address[0] = rffi.cast(rffi.VOIDP, obj_address);
+ # register the value for potential recycling
+ from pypy.module._cppyy.interp_cppyy import memory_regulator
+ memory_regulator.register(cppinstance)
+ else:
+ raise oefmt(space.w_TypeError,
+ "cannot pass %T instance as %s", w_value, self.clsdecl.name)
+
def finalize_call(self, space, w_obj):
if self.ref_buffer:
set_rawobject(space, w_obj, self.ref_buffer[0])
@@ -607,7 +621,6 @@
self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO)
class StdStringConverter(InstanceConverter):
-
def __init__(self, space, extra):
from pypy.module._cppyy import interp_cppyy
cppclass = interp_cppyy.scope_byname(space, capi.std_string_name)
@@ -633,6 +646,34 @@
def free_argument(self, space, arg):
capi.c_destruct(space, self.clsdecl, rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, arg)[0]))
+class StdStringMoveConverter(StdStringConverter):
+ def _unwrap_object(self, space, w_obj):
+ # moving is same as by-ref, but have to check that move is allowed
+ moveit_reason = 3
+ from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE
+ try:
+ obj = space.interp_w(W_CPPInstance, w_obj)
+ if obj and obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE:
+ obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE
+ moveit_reason = 1
+ else:
+ moveit_reason = 0
+ except:
+ pass
+
+ if moveit_reason:
+ try:
+ return StdStringConverter._unwrap_object(self, space, w_obj)
+ except Exception:
+ if moveit_reason == 1:
+ # TODO: if the method fails on some other converter, then the next
+ # overload can not be an rvalue anymore
+ obj = space.interp_w(W_CPPInstance, w_obj)
+ obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE
+ raise
+
+ raise oefmt(space.w_ValueError, "object is not an rvalue")
+
class StdStringRefConverter(InstancePtrConverter):
_immutable_fields_ = ['cppclass', 'typecode']
typecode = 'V'
@@ -885,6 +926,7 @@
_converters["std::basic_string<char>"] = StdStringConverter
_converters["const std::basic_string<char>&"] = StdStringConverter # TODO: shouldn't copy
_converters["std::basic_string<char>&"] = StdStringRefConverter
+_converters["std::basic_string<char>&&"] = StdStringMoveConverter
_converters["PyObject*"] = PyObjectConverter
@@ -1002,6 +1044,7 @@
("std::basic_string<char>", "string"),
("const std::basic_string<char>&", "const string&"),
("std::basic_string<char>&", "string&"),
+ ("std::basic_string<char>&&", "string&&"),
("PyObject*", "_object*"),
)
diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py
--- a/pypy/module/_cppyy/executor.py
+++ b/pypy/module/_cppyy/executor.py
@@ -1,14 +1,10 @@
import sys
from pypy.interpreter.error import oefmt
-
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib import jit_libffi
-
from pypy.module._rawffi.interp_rawffi import letter2tp
-from pypy.module._rawffi.array import W_Array, W_ArrayInstance
-
-from pypy.module._cppyy import helper, capi, ffitypes
+from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews
# Executor objects are used to dispatch C++ methods. They are defined by their
# return type only: arguments are converted by Converter objects, and Executors
@@ -60,7 +56,7 @@
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.get_nullptr(space)
shape = letter2tp(space, self.typecode)
- return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
+ return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval)
class VoidExecutor(Executor):
@@ -98,10 +94,10 @@
def __init__(self, space, extra):
Executor.__init__(self, space, extra)
self.do_assign = False
- self.item = rffi.cast(self.c_type, 0)
+ self.w_item = space.w_None
def set_item(self, space, w_item):
- self.item = self._unwrap_object(space, w_item)
+ self.w_item = w_item
self.do_assign = True
#def _wrap_object(self, space, obj):
@@ -109,7 +105,7 @@
def _wrap_reference(self, space, rffiptr):
if self.do_assign:
- rffiptr[0] = self.item
+ rffiptr[0] = rffi.cast(self.c_type, self._unwrap_object(space, self.w_item))
self.do_assign = False
return self._wrap_object(space, rffiptr[0]) # all paths, for rtyper
diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py
--- a/pypy/module/_cppyy/interp_cppyy.py
+++ b/pypy/module/_cppyy/interp_cppyy.py
@@ -41,6 +41,7 @@
'void**' : 100,
'float' : 30,
'double' : 10,
+ 'bool' : 1,
'const string&' : 1, } # solves a specific string ctor overload
from rpython.rlib.listsort import make_timsort_class
@@ -167,6 +168,7 @@
#
# W_CPPOverload: instance methods (base class)
# W_CPPConstructorOverload: constructors
+# W_CPPAbstractCtorOverload: to provent instantiation of abstract classes
# W_CPPStaticOverload: free and static functions
# W_CPPTemplateOverload: templated methods
# W_CPPTemplateStaticOverload: templated free and static functions
@@ -258,7 +260,8 @@
jit.promote(self)
cif_descr = self.cif_descr
# add extra space for const-ref support (see converter.py)
- buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw')
+ buffer = lltype.malloc(rffi.CCHARP.TO,
+ cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw')
thisoff = 0
try:
if cppthis:
@@ -412,8 +415,10 @@
def priority(self):
total_arg_priority = 0
- for p in [priority.get(arg_type, 0) for arg_type, arg_dflt in self.arg_defs]:
- total_arg_priority += p
+ for arg_type, arg_dflt in self.arg_defs:
+ total_arg_priority += priority.get(arg_type, 0)
+ if '&&' in arg_type:
+ total_arg_priority += 100
return total_arg_priority
@rgc.must_be_light_finalizer
@@ -433,7 +438,7 @@
class CPPSetItem(CPPMethod):
"""Method dispatcher specific to Python's __setitem__ mapped onto C++'s
- operator[](int). The former function takes an extra argument to assign to
+ operator[](T). The former function takes an extra argument to assign to
the return type of the latter."""
_attrs_ = []
@@ -505,14 +510,17 @@
def descr_get(self, w_obj, w_cls=None):
"""functionobject.__get__(obj[, type]) -> method"""
# TODO: check validity of w_cls if given
+ # TODO: this does not work for Python 3, which does not have
+ # unbound methods (probably no common code possible, see also
+ # pypy/interpreter/function.py)
space = self.space
asking_for_bound = (space.is_none(w_cls) or
not space.is_w(w_obj, space.w_None) or
space.is_w(w_cls, space.type(space.w_None)))
if asking_for_bound:
- return MethodWithProps(space, self, w_obj)
+ return MethodWithProps(space, self, w_obj, w_cls)
else:
- return self # unbound methods don't exist in Python 3, return self
+ return MethodWithProps(space, self, None, w_cls)
@unwrap_spec(args_w='args_w')
def call_args(self, args_w):
@@ -584,6 +592,16 @@
sig += '\n'+self.functions[i].prototype()
return self.space.newtext(sig)
+ @unwrap_spec(signature='text')
+ def mp_overload(self, signature):
+ sig = '(%s)' % signature
+ for f in self.functions:
+ if f.signature(False) == sig:
+ if isinstance(self, W_CPPStaticOverload):
+ return W_CPPStaticOverload(self.space, self.scope, [f])
+ return W_CPPOverload(self.space, self.scope, [f])
+ raise oefmt(self.space.w_LookupError, "signature '%s' not found", signature)
+
# allow user to determine ffi use rules per overload
def fget_useffi(self, space):
return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI))
@@ -607,10 +625,11 @@
W_CPPOverload.typedef = TypeDef(
'CPPOverload',
- __get__ = interp2app(W_CPPOverload.descr_get),
- __call__ = interp2app(W_CPPOverload.call_args),
- __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi),
- __doc__ = GetSetProperty(W_CPPOverload.fget_doc)
+ __get__ = interp2app(W_CPPOverload.descr_get),
+ __call__ = interp2app(W_CPPOverload.call_args),
+ __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi),
+ __overload__ = interp2app(W_CPPOverload.mp_overload),
+ __doc__ = GetSetProperty(W_CPPOverload.fget_doc)
)
@@ -626,27 +645,24 @@
# onto a class and w_this should be set
cppinstance = self.space.interp_w(W_CPPInstance, w_obj)
if cppinstance.clsdecl.handle != self.scope.handle:
- return MethodWithProps(self.space, self, w_obj) # bound
+ return MethodWithProps(self.space, self, w_obj, w_cls) # bound
return self # unbound
@unwrap_spec(args_w='args_w')
def call_args(self, args_w):
jit.promote(self)
- #if isinstance(args_w[0], W_CPPInstance):
- # free function used as bound method, leave in place
return self.call_impl(capi.C_NULL_OBJECT, args_w)
- # free functions are implemented as methods of 'namespace' classes, remove 'instance'
- #return self.call_impl(capi.C_NULL_OBJECT, args_w[1:])
def __repr__(self):
return "W_CPPStaticOverload(%s)" % [f.prototype() for f in self.functions]
W_CPPStaticOverload.typedef = TypeDef(
'CPPStaticOverload',
- __get__ = interp2app(W_CPPStaticOverload.descr_get),
- __call__ = interp2app(W_CPPStaticOverload.call_args),
- __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi),
- __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc)
+ __get__ = interp2app(W_CPPStaticOverload.descr_get),
+ __call__ = interp2app(W_CPPStaticOverload.call_args),
+ __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi),
+ __overload__ = interp2app(W_CPPStaticOverload.mp_overload),
+ __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc)
)
@@ -660,11 +676,6 @@
@unwrap_spec(args_w='args_w')
def call_args(self, args_w):
jit.promote(self)
- # TODO: factor out the following:
- if capi.c_is_abstract(self.space, self.scope.handle):
- raise oefmt(self.space.w_TypeError,
- "cannot instantiate abstract class '%s'",
- self.scope.name)
cppinstance = self.space.interp_w(W_CPPInstance, args_w[0])
w_result = self.call_impl(rffi.cast(capi.C_OBJECT, self.scope.handle), args_w[1:])
newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result))
@@ -677,9 +688,27 @@
W_CPPConstructorOverload.typedef = TypeDef(
'CPPConstructorOverload',
- __get__ = interp2app(W_CPPConstructorOverload.descr_get),
- __call__ = interp2app(W_CPPConstructorOverload.call_args),
- __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc)
+ __get__ = interp2app(W_CPPConstructorOverload.descr_get),
+ __call__ = interp2app(W_CPPConstructorOverload.call_args),
+ __overload__ = interp2app(W_CPPConstructorOverload.mp_overload),
+ __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc)
+)
+
+class W_CPPAbstractCtorOverload(W_CPPOverload):
+ _attrs_ = []
+
+ @unwrap_spec(args_w='args_w')
+ def call_args(self, args_w):
+ raise oefmt(self.space.w_TypeError,
+ "cannot instantiate abstract class '%s'", self.scope.name)
+
+ def __repr__(self):
+ return "W_CPPAbstractCtorOverload"
+
+W_CPPAbstractCtorOverload.typedef = TypeDef(
+ 'CPPAbstractCtorOverload',
+ __get__ = interp2app(W_CPPAbstractCtorOverload.descr_get),
+ __call__ = interp2app(W_CPPAbstractCtorOverload.call_args),
)
@@ -702,7 +731,7 @@
if args_w:
# try to specialize the type match for the given object
cppinstance = self.space.interp_w(W_CPPInstance, args_w[i])
- if cppinstance.flags & INSTANCE_FLAGS_IS_RVALUE:
+ if cppinstance.rt_flags & INSTANCE_FLAGS_IS_RVALUE:
sugar = "&&"
elif cppinstance.flags & INSTANCE_FLAGS_IS_REF:
sugar = "*"
@@ -842,11 +871,11 @@
W_CPPTemplateOverload.typedef = TypeDef(
'CPPTemplateOverload',
- __get__ = interp2app(W_CPPTemplateOverload.descr_get),
- __getitem__ = interp2app(W_CPPTemplateOverload.getitem),
- __call__ = interp2app(W_CPPTemplateOverload.call_args),
- __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi),
- __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc)
+ __get__ = interp2app(W_CPPTemplateOverload.descr_get),
+ __getitem__ = interp2app(W_CPPTemplateOverload.getitem),
+ __call__ = interp2app(W_CPPTemplateOverload.call_args),
+ __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi),
+ __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc)
)
class W_CPPTemplateStaticOverload(W_CPPStaticOverload, TemplateOverloadMixin):
@@ -900,11 +929,11 @@
W_CPPTemplateStaticOverload.typedef = TypeDef(
'CPPTemplateStaticOverload',
- __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get),
- __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem),
- __call__ = interp2app(W_CPPTemplateStaticOverload.call_args),
- __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, W_CPPTemplateStaticOverload.fset_useffi),
- __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc)
+ __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get),
+ __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem),
+ __call__ = interp2app(W_CPPTemplateStaticOverload.call_args),
+ __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, W_CPPTemplateStaticOverload.fset_useffi),
+ __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc)
)
@@ -932,6 +961,7 @@
def _get_offset(self, cppinstance):
if cppinstance:
+ assert isinstance(cppinstance.clsdecl, W_CPPClassDecl)
assert lltype.typeOf(cppinstance.clsdecl.handle) == lltype.typeOf(self.scope.handle)
offset = self.offset + cppinstance.clsdecl.get_base_offset(cppinstance, self.scope)
else:
@@ -1077,6 +1107,8 @@
sig = '(%s)' % signature
for f in overload.functions:
if f.signature(False) == sig:
+ if isinstance(overload, W_CPPStaticOverload):
+ return W_CPPStaticOverload(self.space, self, [f])
return W_CPPOverload(self.space, self, [f])
raise oefmt(self.space.w_LookupError, "no overload matches signature")
@@ -1172,9 +1204,13 @@
class W_CPPClassDecl(W_CPPScopeDecl):
- _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers']
+ _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers', 'cppobjects']
_immutable_fields_ = ['handle', 'name', 'overloads[*]', 'datamembers[*]']
+ def __init__(self, space, opaque_handle, final_scoped_name):
+ W_CPPScopeDecl.__init__(self, space, opaque_handle, final_scoped_name)
+ self.cppobjects = rweakref.RWeakValueDictionary(int, W_CPPInstance)
+
def _build_overloads(self):
assert len(self.overloads) == 0
methods_tmp = {}; ftype_tmp = {}
@@ -1213,7 +1249,10 @@
ftype = ftype_tmp[pyname]
CPPMethodSort(methods).sort()
if ftype & FUNCTION_IS_CONSTRUCTOR:
- overload = W_CPPConstructorOverload(self.space, self, methods[:])
+ if capi.c_is_abstract(self.space, self.handle):
+ overload = W_CPPAbstractCtorOverload(self.space, self, methods[:])
+ else:
+ overload = W_CPPConstructorOverload(self.space, self, methods[:])
elif ftype & FUNCTION_IS_STATIC:
if ftype & FUNCTION_IS_TEMPLATE:
cppname = capi.c_method_name(self.space, methods[0].cppmethod)
@@ -1279,10 +1318,12 @@
raise self.missing_attribute_error(name)
def get_base_offset(self, cppinstance, calling_scope):
+ assert isinstance(cppinstance.clsdecl, W_CPPClassDecl)
assert self == cppinstance.clsdecl
return 0
def get_cppthis(self, cppinstance, calling_scope):
+ assert isinstance(cppinstance.clsdecl, W_CPPClassDecl)
assert self == cppinstance.clsdecl
return cppinstance.get_rawobject()
@@ -1318,12 +1359,14 @@
class W_CPPComplexClassDecl(W_CPPClassDecl):
def get_base_offset(self, cppinstance, calling_scope):
+ assert isinstance(cppinstance.clsdecl, W_CPPComplexClassDecl)
assert self == cppinstance.clsdecl
offset = capi.c_base_offset(self.space,
- self, calling_scope, cppinstance.get_rawobject(), 1)
+ self, calling_scope, cppinstance.get_rawobject(), 1)
return offset
def get_cppthis(self, cppinstance, calling_scope):
+ assert isinstance(cppinstance.clsdecl, W_CPPComplexClassDecl)
assert self == cppinstance.clsdecl
offset = self.get_base_offset(cppinstance, calling_scope)
return capi.direct_ptradd(cppinstance.get_rawobject(), offset)
@@ -1343,9 +1386,9 @@
class W_CPPInstance(W_Root):
- _attrs_ = ['space', 'clsdecl', '_rawobject', 'smartdecl', 'deref', 'flags',
+ _attrs_ = ['space', 'clsdecl', '_rawobject', 'smartdecl', 'deref', 'flags', 'rt_flags',
'finalizer_registered']
- _immutable_fields_ = ['clsdecl', 'smartdecl', 'deref']
+ _immutable_fields_ = ['clsdecl', 'smartdecl', 'deref', 'flags']
finalizer_registered = False
@@ -1353,6 +1396,7 @@
smartdecl=None, deref=rffi.cast(capi.C_METHOD, 0)):
self.space = space
self.clsdecl = decl
+ assert isinstance(self.clsdecl, W_CPPClassDecl)
assert lltype.typeOf(rawobject) == capi.C_OBJECT
assert not isref or rawobject
self._rawobject = rawobject
@@ -1360,15 +1404,16 @@
self.flags = 0
if isref or (smartdecl and deref):
self.flags |= INSTANCE_FLAGS_IS_REF
+ self.rt_flags = 0
if python_owns:
- self.flags |= INSTANCE_FLAGS_PYTHON_OWNS
+ self.rt_flags |= INSTANCE_FLAGS_PYTHON_OWNS
self._opt_register_finalizer()
self.smartdecl = smartdecl
self.deref = deref
def _opt_register_finalizer(self):
if not self.finalizer_registered and not hasattr(self.space, "fake"):
- assert self.flags & INSTANCE_FLAGS_PYTHON_OWNS
+ assert self.rt_flags & INSTANCE_FLAGS_PYTHON_OWNS
self.register_finalizer(self.space)
self.finalizer_registered = True
@@ -1380,17 +1425,18 @@
# allow user to determine ownership rules on a per object level
def fget_python_owns(self, space):
- return space.newbool(bool(self.flags & INSTANCE_FLAGS_PYTHON_OWNS))
+ return space.newbool(bool(self.rt_flags & INSTANCE_FLAGS_PYTHON_OWNS))
@unwrap_spec(value=bool)
def fset_python_owns(self, space, value):
if space.is_true(value):
- self.flags |= INSTANCE_FLAGS_PYTHON_OWNS
+ self.rt_flags |= INSTANCE_FLAGS_PYTHON_OWNS
self._opt_register_finalizer()
else:
- self.flags &= ~INSTANCE_FLAGS_PYTHON_OWNS
+ self.rt_flags &= ~INSTANCE_FLAGS_PYTHON_OWNS
def get_cppthis(self, calling_scope):
+ assert isinstance(self.clsdecl, W_CPPClassDecl)
return self.clsdecl.get_cppthis(self, calling_scope)
def get_rawobject(self):
@@ -1497,6 +1543,7 @@
def destruct(self):
if self._rawobject:
+ assert isinstance(self.clsdecl, W_CPPClassDecl)
if self.smartdecl and self.deref:
klass = self.smartdecl
elif not (self.flags & INSTANCE_FLAGS_IS_REF):
@@ -1508,7 +1555,7 @@
self._rawobject = capi.C_NULL_OBJECT
def _finalize_(self):
- if self.flags & INSTANCE_FLAGS_PYTHON_OWNS:
+ if self.rt_flags & INSTANCE_FLAGS_PYTHON_OWNS:
self.destruct()
W_CPPInstance.typedef = TypeDef(
@@ -1530,31 +1577,33 @@
class MemoryRegulator:
- # TODO: (?) An object address is not unique if e.g. the class has a
- # public data member of class type at the start of its definition and
- # has no virtual functions. A _key class that hashes on address and
- # type would be better, but my attempt failed in the rtyper, claiming
- # a call on None ("None()") and needed a default ctor. (??)
- # Note that for now, the associated test carries an m_padding to make
- # a difference in the addresses.
- def __init__(self):
- self.objects = rweakref.RWeakValueDictionary(int, W_CPPInstance)
+ _immutable_ = True
- def register(self, obj):
+ @staticmethod
+ def register(obj):
if not obj._rawobject:
return
- int_address = int(rffi.cast(rffi.LONG, obj._rawobject))
- self.objects.set(int_address, obj)
+ addr_as_int = int(rffi.cast(rffi.LONG, obj.get_rawobject()))
+ clsdecl = obj.clsdecl
+ assert isinstance(clsdecl, W_CPPClassDecl)
+ clsdecl.cppobjects.set(addr_as_int, obj)
- def unregister(self, obj):
+ @staticmethod
+ def unregister(obj):
if not obj._rawobject:
return
- int_address = int(rffi.cast(rffi.LONG, obj._rawobject))
- self.objects.set(int_address, None)
+ addr_as_int = int(rffi.cast(rffi.LONG, obj.get_rawobject()))
+ clsdecl = obj.clsdecl
+ assert isinstance(clsdecl, W_CPPClassDecl)
+ clsdecl.cppobjects.set(addr_as_int, None) # actually deletes (pops)
- def retrieve(self, address):
- int_address = int(rffi.cast(rffi.LONG, address))
- return self.objects.get(int_address)
+ @staticmethod
+ def retrieve(clsdecl, address):
+ if not address:
+ return None
+ addr_as_int = int(rffi.cast(rffi.LONG, address))
+ assert isinstance(clsdecl, W_CPPClassDecl)
+ return clsdecl.cppobjects.get(addr_as_int)
memory_regulator = MemoryRegulator()
@@ -1600,8 +1649,11 @@
# try to recycle existing object if this one is not newly created
if not fresh and rawobject:
- obj = memory_regulator.retrieve(rawobject)
- if obj is not None and obj.clsdecl is clsdecl:
+ address = rawobject
+ if is_ref:
+ address = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, address)[0])
+ obj = memory_regulator.retrieve(clsdecl, address)
+ if obj is not None:
return obj
# fresh creation
@@ -1652,7 +1704,7 @@
"""Casts the given instance into an C++-style rvalue."""
obj = space.interp_w(W_CPPInstance, w_obj)
if obj:
- obj.flags |= INSTANCE_FLAGS_IS_RVALUE
+ obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE
return w_obj
diff --git a/pypy/module/_cppyy/lowlevelviews.py b/pypy/module/_cppyy/lowlevelviews.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cppyy/lowlevelviews.py
@@ -0,0 +1,54 @@
+# Naked C++ pointers carry no useful size or layout information, but often
+# such information is externnally available. Low level views are arrays with
+# a few more methods allowing such information to be set. Afterwards, it is
+# simple to pass these views on to e.g. numpy (w/o the need to copy).
+
+from pypy.interpreter.error import oefmt
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty_w
+from pypy.module._rawffi.array import W_ArrayInstance
+
+
+class W_LowLevelView(W_ArrayInstance):
+ def __init__(self, space, shape, length, address):
+ assert address # if not address, base class will allocate memory
+ W_ArrayInstance.__init__(self, space, shape, length, address)
+
+ @unwrap_spec(args_w='args_w')
+ def reshape(self, space, args_w):
+ # llviews are only created from non-zero addresses, so we only need
+ # to adjust length and shape
+
+ nargs = len(args_w)
+ if nargs == 0:
+ raise oefmt(space.w_TypeError, "reshape expects a tuple argument")
+
+ newshape_w = args_w
+ if nargs != 1 or not space.isinstance_w(args_w[0], space.w_tuple) or \
+ not space.len_w(args_w[0]) == 1:
+ raise oefmt(space.w_TypeError,
+ "tuple object of length 1 expected, received %T", args_w[0])
+
+ w_shape = args_w[0]
+
+ # shape in W_ArrayInstance-speak is somewhat different from what
+ # e.g. numpy thinks of it: self.shape contains the info (itemcode,
+ # size, etc.) of a single entry; length is user-facing shape
+ self.length = space.int_w(space.getitem(w_shape, space.newint(0)))
+
+
+W_LowLevelView.typedef = TypeDef(
+ 'LowLevelView',
+ __repr__ = interp2app(W_LowLevelView.descr_repr),
+ __setitem__ = interp2app(W_LowLevelView.descr_setitem),
+ __getitem__ = interp2app(W_LowLevelView.descr_getitem),
+ __len__ = interp2app(W_LowLevelView.getlength),
+ buffer = GetSetProperty(W_LowLevelView.getbuffer),
+ shape = interp_attrproperty_w('shape', W_LowLevelView),
+ free = interp2app(W_LowLevelView.free),
+ byptr = interp2app(W_LowLevelView.byptr),
+ itemaddress = interp2app(W_LowLevelView.descr_itemaddress),
+ reshape = interp2app(W_LowLevelView.reshape),
+)
+W_ArrayInstance.typedef.acceptable_as_base_class = False
+
diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py
--- a/pypy/module/_cppyy/pythonify.py
+++ b/pypy/module/_cppyy/pythonify.py
@@ -73,7 +73,8 @@
# C++ namespace base class (the C++ class base class defined in _post_import_startup)
class CPPNamespace(with_metaclass(CPPNamespaceMeta, object)):
- pass
+ def __init__(self):
+ raise TypeError("cannot instantiate namespace '%s'", self.__cppname__)
# TODO: this can be moved to the interp level (and share template argument
@@ -450,6 +451,14 @@
return self.__real_init__(*args)
pyclass.__init__ = vector_init
+ # size-up the return of data()
+ pyclass.__real_data = pyclass.data
+ def data_with_len(self):
+ arr = self.__real_data()
+ arr.reshape((len(self),))
+ return arr
+ pyclass.data = data_with_len
+
# combine __getitem__ and __len__ to make a pythonized __getitem__
if '__getitem__' in pyclass.__dict__ and '__len__' in pyclass.__dict__:
pyclass._getitem__unchecked = pyclass.__getitem__
diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx
--- a/pypy/module/_cppyy/test/advancedcpp.cxx
+++ b/pypy/module/_cppyy/test/advancedcpp.cxx
@@ -4,20 +4,32 @@
// for testing of default arguments
-#define IMPLEMENT_DEFAULTER_CLASS(type, tname) \
+#define IMPLEMENT_DEFAULTERS(type, tname) \
tname##_defaulter::tname##_defaulter(type a, type b, type c) { \
- m_a = a; m_b = b; m_c = c; \
+ m_a = a; m_b = b; m_c = c; \
+} \
+type tname##_defaulter_func(int idx, type a, type b, type c) { \
+ if (idx == 0) return a; \
+ if (idx == 1) return b; \
+ if (idx == 2) return c; \
+ return (type)idx; \
}
-IMPLEMENT_DEFAULTER_CLASS(short, short)
-IMPLEMENT_DEFAULTER_CLASS(unsigned short, ushort)
-IMPLEMENT_DEFAULTER_CLASS(int, int)
-IMPLEMENT_DEFAULTER_CLASS(unsigned, uint)
-IMPLEMENT_DEFAULTER_CLASS(long, long)
-IMPLEMENT_DEFAULTER_CLASS(unsigned long, ulong)
-IMPLEMENT_DEFAULTER_CLASS(long long, llong)
-IMPLEMENT_DEFAULTER_CLASS(unsigned long long, ullong)
-IMPLEMENT_DEFAULTER_CLASS(float, float)
-IMPLEMENT_DEFAULTER_CLASS(double, double)
+IMPLEMENT_DEFAULTERS(short, short)
+IMPLEMENT_DEFAULTERS(unsigned short, ushort)
+IMPLEMENT_DEFAULTERS(int, int)
+IMPLEMENT_DEFAULTERS(unsigned, uint)
+IMPLEMENT_DEFAULTERS(long, long)
+IMPLEMENT_DEFAULTERS(unsigned long, ulong)
+IMPLEMENT_DEFAULTERS(long long, llong)
+IMPLEMENT_DEFAULTERS(unsigned long long, ullong)
+IMPLEMENT_DEFAULTERS(float, float)
+IMPLEMENT_DEFAULTERS(double, double)
+
+std::string string_defaulter_func(int idx, const std::string& name1, std::string name2) {
+ if (idx == 0) return name1;
+ if (idx == 1) return name2;
+ return "mies";
+}
// for esoteric inheritance testing
@@ -77,11 +89,11 @@
double my_global_array[500];
static double sd = 1234.;
double* my_global_ptr = &sd;
+const char my_global_string2[] = "zus jet teun";
some_int_holder my_global_int_holders[5] = {
some_int_holder(13), some_int_holder(42), some_int_holder(88),
some_int_holder(-1), some_int_holder(17) };
-
// for life-line and identity testing
int some_class_with_data::some_data::s_num_data = 0;
diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h
--- a/pypy/module/_cppyy/test/advancedcpp.h
+++ b/pypy/module/_cppyy/test/advancedcpp.h
@@ -4,24 +4,27 @@
//===========================================================================
-#define DECLARE_DEFAULTER_CLASS(type, tname) \
+#define DECLARE_DEFAULTERS(type, tname) \
class tname##_defaulter { \
public: \
tname##_defaulter(type a = 11, type b = 22, type c = 33); \
\
public: \
type m_a, m_b, m_c; \
-};
-DECLARE_DEFAULTER_CLASS(short, short) // for testing of default arguments
-DECLARE_DEFAULTER_CLASS(unsigned short, ushort)
-DECLARE_DEFAULTER_CLASS(int, int)
-DECLARE_DEFAULTER_CLASS(unsigned, uint)
-DECLARE_DEFAULTER_CLASS(long, long)
-DECLARE_DEFAULTER_CLASS(unsigned long, ulong)
-DECLARE_DEFAULTER_CLASS(long long, llong)
-DECLARE_DEFAULTER_CLASS(unsigned long long, ullong)
-DECLARE_DEFAULTER_CLASS(float, float)
-DECLARE_DEFAULTER_CLASS(double, double)
+}; \
+type tname##_defaulter_func(int idx = 0, type a = 11, type b = 22, type c = 33);
+DECLARE_DEFAULTERS(short, short) // for testing of default arguments
+DECLARE_DEFAULTERS(unsigned short, ushort)
+DECLARE_DEFAULTERS(int, int)
+DECLARE_DEFAULTERS(unsigned, uint)
+DECLARE_DEFAULTERS(long, long)
+DECLARE_DEFAULTERS(unsigned long, ulong)
+DECLARE_DEFAULTERS(long long, llong)
+DECLARE_DEFAULTERS(unsigned long long, ullong)
+DECLARE_DEFAULTERS(float, float)
+DECLARE_DEFAULTERS(double, double)
+
+std::string string_defaulter_func(int idx, const std::string& name1 = "aap", std::string name2 = "noot");
//===========================================================================
@@ -274,7 +277,8 @@
extern double my_global_double; // a couple of globals for access testing
extern double my_global_array[500];
extern double* my_global_ptr;
-static const char my_global_string[] = "aap " " noot " " mies";
+static const char my_global_string1[] = "aap " " noot " " mies";
+extern const char my_global_string2[];
class some_int_holder {
public:
@@ -311,6 +315,11 @@
some_data m_data;
};
+class refers_to_self { // for data member reuse testing
+public:
+ refers_to_self* m_other = nullptr;
+};
+
//===========================================================================
class pointer_pass { // for testing passing of void*'s
@@ -419,3 +428,34 @@
void throw_anything();
void throw_exception();
};
+
+
+//===========================================================================
+class UsingBase { // using declaration testing
+public:
+ UsingBase(int n = 13) : m_int(n) {}
+ virtual char vcheck() { return 'A'; }
+ int m_int;
+};
+
+class UsingDerived : public UsingBase {
+public:
+ using UsingBase::UsingBase;
+ virtual char vcheck() { return 'B'; }
+ int m_int2 = 42;
+};
+
+
+//===========================================================================
+class TypedefToPrivateClass { // typedef resolution testing
+private:
+ class PC {
+ public:
+ PC(int i) : m_val(i) {}
+ int m_val;
+ };
+
+public:
+ typedef PC PP;
+ PP f() { return PC(42); }
+};
diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml
--- a/pypy/module/_cppyy/test/advancedcpp.xml
+++ b/pypy/module/_cppyy/test/advancedcpp.xml
@@ -1,6 +1,7 @@
<lcgdict>
<class pattern="*_defaulter" />
+ <function pattern="*_defaulter_func" />
<class name="base_class" />
<class name="derived_class" />
@@ -32,6 +33,7 @@
<class name="some_convertible" />
<class name="some_class_with_data" />
<class name="some_class_with_data::some_data" />
+ <class name="refers_to_self" />
<class name="some_comparable" />
<function name="operator==" />
@@ -59,5 +61,7 @@
<class name="overload_the_other_way" />
<class name="Thrower" />
+ <class pattern="Using*" />
+ <class name="TypedefToPrivateClass" />
</lcgdict>
diff --git a/pypy/module/_cppyy/test/stltypes.cxx b/pypy/module/_cppyy/test/stltypes.cxx
--- a/pypy/module/_cppyy/test/stltypes.cxx
+++ b/pypy/module/_cppyy/test/stltypes.cxx
@@ -1,6 +1,20 @@
#include "stltypes.h"
+//- explicit instantiations of used comparisons
+#if defined __clang__
+namespace std {
+#define ns_prefix std::
+#elif defined(__GNUC__) || defined(__GNUG__)
+namespace __gnu_cxx {
+#define ns_prefix
+#endif
+template bool ns_prefix operator==(const std::vector<int>::iterator&,
+ const std::vector<int>::iterator&);
+template bool ns_prefix operator!=(const std::vector<int>::iterator&,
+ const std::vector<int>::iterator&);
+}
+
//- class with lots of std::string handling
stringy_class::stringy_class(const char* s) : m_string(s) {}
@@ -9,3 +23,33 @@
void stringy_class::set_string1(const std::string& s) { m_string = s; }
void stringy_class::set_string2(std::string s) { m_string = s; }
+
+
+//- helpers for testing array
+int ArrayTest::get_pp_px(Point** p, int idx) {
+ return p[idx]->px;
+}
+
+int ArrayTest::get_pp_py(Point** p, int idx) {
+ return p[idx]->py;
+}
+
+int ArrayTest::get_pa_px(Point* p[], int idx) {
+ return p[idx]->px;
+}
+
+int ArrayTest::get_pa_py(Point* p[], int idx) {
+ return p[idx]->py;
+}
+
+
+// helpers for string testing
+std::string str_array_1[3] = {"a", "b", "c"};
+std::string str_array_2[] = {"d", "e", "f", "g"};
+std::string str_array_3[3][2] = {{"a", "b"}, {"c", "d"}, {"e", "f"}};
+std::string str_array_4[4][2][2] = {
+ {{"a", "b"}, {"c", "d"}},
+ {{"e", "f"}, {"g", "h"}},
+ {{"i", "j"}, {"k", "l"}},
+ {{"m", "n"}, {"o", "p"}},
+};
diff --git a/pypy/module/_cppyy/test/stltypes.h b/pypy/module/_cppyy/test/stltypes.h
--- a/pypy/module/_cppyy/test/stltypes.h
+++ b/pypy/module/_cppyy/test/stltypes.h
@@ -37,3 +37,49 @@
std::string operator[](double) { return "double"; }
std::string operator[](const std::string&) { return "string"; }
};
+
+
+//- instantiations of used STL types
+namespace {
+
+ stl_like_class<int> stlc_1;
+
+} // unnamed namespace
+
+
+// comps for int only to allow testing: normal use of vector is looping over a
+// range-checked version of __getitem__
+#if defined(__clang__) && defined(__APPLE__)
+namespace std {
+#define ns_prefix std::
+#elif defined(__GNUC__) || defined(__GNUG__)
+namespace __gnu_cxx {
+#define ns_prefix
+#endif
+extern template bool ns_prefix operator==(const std::vector<int>::iterator&,
+ const std::vector<int>::iterator&);
+extern template bool ns_prefix operator!=(const std::vector<int>::iterator&,
+ const std::vector<int>::iterator&);
+}
+
+
+//- helpers for testing array
+namespace ArrayTest {
+
+struct Point {
+ int px, py;
+};
+
+int get_pp_px(Point** p, int idx);
+int get_pp_py(Point** p, int idx);
+int get_pa_px(Point* p[], int idx);
+int get_pa_py(Point* p[], int idx);
+
+} // namespace ArrayTest
+
+
+// helpers for string testing
+extern std::string str_array_1[3];
+extern std::string str_array_2[];
+extern std::string str_array_3[3][2];
+extern std::string str_array_4[4][2][2];
diff --git a/pypy/module/_cppyy/test/stltypes.xml b/pypy/module/_cppyy/test/stltypes.xml
--- a/pypy/module/_cppyy/test/stltypes.xml
+++ b/pypy/module/_cppyy/test/stltypes.xml
@@ -1,10 +1,13 @@
<lcgdict>
- <namespace name="std" />
+ <class name="just_a_class" />
+ <class name="stringy_class" />
+ <class pattern="stl_like_class<*>" />
- <class name="just_a_class" />
+ <namespace name="ArrayTest" />
+ <class pattern="ArrayTest::*" />
+ <function pattern="ArrayTest::*" />
- <class name="std::string" />
- <class name="stringy_class" />
+ <variable pattern="str_array_*" />
</lcgdict>
diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py b/pypy/module/_cppyy/test/test_advancedcpp.py
--- a/pypy/module/_cppyy/test/test_advancedcpp.py
+++ b/pypy/module/_cppyy/test/test_advancedcpp.py
@@ -56,6 +56,12 @@
assert d.m_b == t(4)
assert d.m_c == t(5)
d.__destruct__()
+
+ defaulter_func = getattr(cppyy.gbl, '%s_defaulter_func' %n)
+ answers = [11, 22, 33, 3]
+ for idx in range(4):
+ assert defaulter_func(idx) == answers[idx]
+
test_defaulter('short', int)
test_defaulter('ushort', int)
test_defaulter('int', int)
@@ -67,6 +73,13 @@
test_defaulter('float', float)
test_defaulter('double', float)
+ assert cppyy.gbl.string_defaulter_func(0) == "aap"
+ assert cppyy.gbl.string_defaulter_func(0, "zus") == "zus"
+ assert cppyy.gbl.string_defaulter_func(1) == "noot"
+ assert cppyy.gbl.string_defaulter_func(1, "zus") == "noot"
+ assert cppyy.gbl.string_defaulter_func(1, "zus", "jet") == "jet"
+ assert cppyy.gbl.string_defaulter_func(2) == "mies"
+
def test02_simple_inheritance(self):
"""Test binding of a basic inheritance structure"""
@@ -148,6 +161,8 @@
assert gbl.a_ns.d_ns.e_class.f_class.s_f == 66
assert gbl.a_ns.d_ns.e_class.f_class().m_f == -6
+ raises(TypeError, gbl.a_ns)
+
def test03a_namespace_lookup_on_update(self):
"""Test whether namespaces can be shared across dictionaries."""
@@ -474,6 +489,23 @@
d2.__destruct__()
d1.__destruct__()
+ RTS = cppyy.gbl.refers_to_self
+
+ r1 = RTS()
+ r2 = RTS()
+ r1.m_other = r2
+
+ r3 = r1.m_other
+ r4 = r1.m_other
+ assert r3 is r4
+
+ assert r3 == r2
+ assert r3 is r2
+
+ r3.extra = 42
+ assert r2.extra == 42
+ assert r4.extra == 42
+
def test11_multi_methods(self):
"""Test calling of methods from multiple inheritance"""
@@ -653,10 +685,18 @@
assert cppyy.gbl.my_global_double == 12.
assert len(cppyy.gbl.my_global_array) == 500
- assert cppyy.gbl.my_global_string == "aap noot mies"
+ assert cppyy.gbl.my_global_string1 == "aap noot mies"
+ return # next line currently crashes
+ assert cppyy.gbl.my_global_string2 == "zus jet teun"
# TODO: currently fails b/c double** not understood as &double*
#assert cppyy.gbl.my_global_ptr[0] == 1234.
+ v = cppyy.gbl.my_global_int_holders
+ assert len(v) == 5
+ expected_vals = [13, 42, 88, -1, 17]
+ for i in range(len(v)):
+ assert v[i].m_val == expected_vals[i]
+
def test22_exceptions(self):
"""Catching of C++ exceptions"""
@@ -675,3 +715,35 @@
t.throw_exception()
except Exception as e:
"C++ function failed" in str(e)
+
+ def test23_using(self):
+ """Accessibility of using declarations"""
+
+ import _cppyy as cppyy
+
+ assert cppyy.gbl.UsingBase().vcheck() == 'A'
+
+ B = cppyy.gbl.UsingDerived
+ assert not 'UsingBase' in B.__init__.__doc__
+
+ b1 = B()
+ assert b1.m_int == 13
+ assert b1.m_int2 == 42
+ assert b1.vcheck() == 'B'
+
+ b2 = B(10)
+ assert b2.m_int == 10
+ assert b2.m_int2 == 42
+ assert b2.vcheck() == 'B'
+
+ b3 = B(b2)
+ assert b3.m_int == 10
+ assert b3.m_int2 == 42
+ assert b3.vcheck() == 'B'
+
+ def test24_typedef_to_private_class(self):
+ """Typedefs to private classes should not resolve"""
+
+ import _cppyy as cppyy
+
+ assert cppyy.gbl.TypedefToPrivateClass().f().m_val == 42
diff --git a/pypy/module/_cppyy/test/test_datatypes.py b/pypy/module/_cppyy/test/test_datatypes.py
--- a/pypy/module/_cppyy/test/test_datatypes.py
+++ b/pypy/module/_cppyy/test/test_datatypes.py
@@ -181,7 +181,7 @@
names = ['uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong']
import array
a = range(self.N)
- atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L' ]
+ atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L']
for j in range(len(names)):
b = array.array(atypes[j], a)
setattr(c, 'm_'+names[j]+'_array', b) # buffer copies
@@ -189,6 +189,7 @@
assert eval('c.m_%s_array[i]' % names[j]) == b[i]
setattr(c, 'm_'+names[j]+'_array2', b) # pointer copies
+ assert 3 < self.N
b[3] = 28
for i in range(self.N):
assert eval('c.m_%s_array2[i]' % names[j]) == b[i]
@@ -210,7 +211,7 @@
a = range(self.N)
# test arrays in mixed order, to give overload resolution a workout
- for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l' ]:
+ for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l']:
b = array.array(t, a)
# typed passing
@@ -689,9 +690,6 @@
'get_long_array', 'get_long_array2',
'get_ulong_array', 'get_ulong_array2']:
arr = getattr(c, func)()
- arr = arr.shape.fromaddress(arr.itemaddress(0), self.N)
-
- """TODO: interface change ...
arr.reshape((self.N,))
assert len(arr) == self.N
@@ -703,7 +701,7 @@
l = list(arr)
for i in range(self.N):
- assert arr[i] == l[i]"""
+ assert arr[i] == l[i]
def test20_voidp(self):
"""Test usage of void* data"""
@@ -756,6 +754,15 @@
def test21_function_pointers(self):
"""Function pointer passing"""
+ import os
+
+ # TODO: currently crashes if fast path disabled
+ try:
+ if os.environ['CPPYY_DISABLE_FASTPATH']:
+ return
+ except KeyError:
+ pass
+
import _cppyy as cppyy
f1 = cppyy.gbl.sum_of_int
diff --git a/pypy/module/_cppyy/test/test_overloads.py b/pypy/module/_cppyy/test/test_overloads.py
--- a/pypy/module/_cppyy/test/test_overloads.py
+++ b/pypy/module/_cppyy/test/test_overloads.py
@@ -58,15 +58,19 @@
c = c_overload()
raises(TypeError, c.__dispatch__, 'get_int', 12)
raises(LookupError, c.__dispatch__, 'get_int', 'does_not_exist')
- assert c.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42
- assert c.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13
+ assert c.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42
+ assert c_overload.get_int.__overload__('a_overload*')(c, a_overload()) == 42
+ assert c.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13
+ assert c_overload.get_int.__overload__('b_overload*')(c, b_overload()) == 13
assert c_overload().__dispatch__('get_int', 'a_overload*')(a_overload()) == 42
# TODO: #assert c_overload.__dispatch__('get_int', 'b_overload*')(c, b_overload()) == 13
d = d_overload()
- assert d.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42
- assert d.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13
+ assert d.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42
+ assert d_overload.get_int.__overload__('a_overload*')(d, a_overload()) == 42
+ assert d.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13
+ assert d_overload.get_int.__overload__('b_overload*')(d, b_overload()) == 13
nb = ns_a_overload.b_overload()
raises(TypeError, nb.f, c_overload())
diff --git a/pypy/module/_cppyy/test/test_stltypes.py b/pypy/module/_cppyy/test/test_stltypes.py
--- a/pypy/module/_cppyy/test/test_stltypes.py
+++ b/pypy/module/_cppyy/test/test_stltypes.py
@@ -22,12 +22,12 @@
def test01_builtin_type_vector_types(self):
"""Test access to std::vector<int>/std::vector<double>"""
- import _cppyy
+ import _cppyy as cppyy
- assert _cppyy.gbl.std is _cppyy.gbl.std
- assert _cppyy.gbl.std.vector is _cppyy.gbl.std.vector
+ assert cppyy.gbl.std is cppyy.gbl.std
+ assert cppyy.gbl.std.vector is cppyy.gbl.std.vector
- assert callable(_cppyy.gbl.std.vector)
+ assert callable(cppyy.gbl.std.vector)
type_info = (
("int", int),
@@ -36,14 +36,14 @@
)
for c_type, p_type in type_info:
- tv1 = getattr(_cppyy.gbl.std, 'vector<%s>' % c_type)
- tv2 = _cppyy.gbl.std.vector(p_type)
+ tv1 = getattr(cppyy.gbl.std, 'vector<%s>' % c_type)
+ tv2 = cppyy.gbl.std.vector(p_type)
assert tv1 is tv2
- assert tv1.iterator is _cppyy.gbl.std.vector(p_type).iterator
+ assert tv1.iterator is cppyy.gbl.std.vector(p_type).iterator
#-----
- v = tv1(); v += range(self.N) # default args from Reflex are useless :/
- if p_type == int: # only type with == and != reflected in .xml
+ v = tv1(); v += range(self.N)
+ if p_type == int:
assert v.begin().__eq__(v.begin())
assert v.begin() == v.begin()
assert v.end() == v.end()
@@ -58,6 +58,7 @@
assert v.size() == self.N
assert len(v) == self.N
+ assert len(v.data()) == self.N
#-----
v = tv1()
@@ -73,16 +74,16 @@
def test02_user_type_vector_type(self):
"""Test access to an std::vector<just_a_class>"""
- import _cppyy
+ import _cppyy as cppyy
- assert _cppyy.gbl.std is _cppyy.gbl.std
- assert _cppyy.gbl.std.vector is _cppyy.gbl.std.vector
+ assert cppyy.gbl.std is cppyy.gbl.std
+ assert cppyy.gbl.std.vector is cppyy.gbl.std.vector
- assert callable(_cppyy.gbl.std.vector)
+ assert callable(cppyy.gbl.std.vector)
- tv1 = getattr(_cppyy.gbl.std, 'vector<just_a_class>')
- tv2 = _cppyy.gbl.std.vector('just_a_class')
- tv3 = _cppyy.gbl.std.vector(_cppyy.gbl.just_a_class)
+ tv1 = getattr(cppyy.gbl.std, 'vector<just_a_class>')
+ tv2 = cppyy.gbl.std.vector('just_a_class')
+ tv3 = cppyy.gbl.std.vector(cppyy.gbl.just_a_class)
assert tv1 is tv2
assert tv2 is tv3
@@ -95,7 +96,7 @@
assert hasattr(v, 'end' )
for i in range(self.N):
- v.push_back(_cppyy.gbl.just_a_class())
+ v.push_back(cppyy.gbl.just_a_class())
v[i].m_i = i
assert v[i].m_i == i
@@ -105,9 +106,9 @@
def test03_empty_vector_type(self):
"""Test behavior of empty std::vector<int>"""
- import _cppyy
+ import _cppyy as cppyy
- v = _cppyy.gbl.std.vector(int)()
+ v = cppyy.gbl.std.vector(int)()
for arg in v:
pass
v.__destruct__()
@@ -115,9 +116,9 @@
def test04_vector_iteration(self):
"""Test iteration over an std::vector<int>"""
- import _cppyy
+ import _cppyy as cppyy
- v = _cppyy.gbl.std.vector(int)()
+ v = cppyy.gbl.std.vector(int)()
for i in range(self.N):
v.push_back(i)
@@ -140,9 +141,9 @@
def test05_push_back_iterables_with_iadd(self):
"""Test usage of += of iterable on push_back-able container"""
- import _cppyy
+ import _cppyy as cppyy
- v = _cppyy.gbl.std.vector(int)()
+ v = cppyy.gbl.std.vector(int)()
v += [1, 2, 3]
assert len(v) == 3
@@ -159,7 +160,7 @@
raises(TypeError, v.__iadd__, (7, '8')) # string shouldn't pass
assert len(v) == 7 # TODO: decide whether this should roll-back
- v2 = _cppyy.gbl.std.vector(int)()
+ v2 = cppyy.gbl.std.vector(int)()
v2 += [8, 9]
assert len(v2) == 2
assert v2[0] == 8
@@ -174,9 +175,9 @@
def test06_vector_indexing(self):
"""Test python-style indexing to an std::vector<int>"""
- import _cppyy
+ import _cppyy as cppyy
- v = _cppyy.gbl.std.vector(int)()
+ v = cppyy.gbl.std.vector(int)()
for i in range(self.N):
v.push_back(i)
@@ -209,9 +210,9 @@
def test01_string_argument_passing(self):
"""Test mapping of python strings and std::string"""
- import _cppyy
- std = _cppyy.gbl.std
- stringy_class = _cppyy.gbl.stringy_class
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
+ stringy_class = cppyy.gbl.stringy_class
c, s = stringy_class(""), std.string("test1")
@@ -240,9 +241,9 @@
def test02_string_data_access(self):
"""Test access to std::string object data members"""
- import _cppyy
- std = _cppyy.gbl.std
- stringy_class = _cppyy.gbl.stringy_class
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
+ stringy_class = cppyy.gbl.stringy_class
c, s = stringy_class("dummy"), std.string("test string")
@@ -261,9 +262,9 @@
return # don't bother; is fixed in cling-support
- import _cppyy
- std = _cppyy.gbl.std
- stringy_class = _cppyy.gbl.stringy_class
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
+ stringy_class = cppyy.gbl.stringy_class
t0 = "aap\0noot"
assert t0 == "aap\0noot"
@@ -288,8 +289,8 @@
def test01_builtin_list_type(self):
"""Test access to a list<int>"""
- import _cppyy
- std = _cppyy.gbl.std
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
type_info = (
("int", int),
@@ -299,9 +300,9 @@
for c_type, p_type in type_info:
tl1 = getattr(std, 'list<%s>' % c_type)
- tl2 = _cppyy.gbl.std.list(p_type)
+ tl2 = cppyy.gbl.std.list(p_type)
assert tl1 is tl2
- assert tl1.iterator is _cppyy.gbl.std.list(p_type).iterator
+ assert tl1.iterator is cppyy.gbl.std.list(p_type).iterator
#-----
a = tl1()
@@ -323,8 +324,8 @@
def test02_empty_list_type(self):
"""Test behavior of empty list<int>"""
- import _cppyy
- std = _cppyy.gbl.std
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
a = std.list(int)()
for arg in a:
@@ -344,8 +345,8 @@
def test01_builtin_map_type(self):
"""Test access to a map<int,int>"""
- import _cppyy
- std = _cppyy.gbl.std
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
a = std.map(int, int)()
for i in range(self.N):
@@ -373,8 +374,8 @@
def test02_keyed_maptype(self):
"""Test access to a map<std::string,int>"""
- import _cppyy
- std = _cppyy.gbl.std
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
a = std.map(std.string, int)()
for i in range(self.N):
@@ -386,8 +387,8 @@
def test03_empty_maptype(self):
"""Test behavior of empty map<int,int>"""
- import _cppyy
- std = _cppyy.gbl.std
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
m = std.map(int, int)()
for key, value in m:
@@ -396,8 +397,9 @@
def test04_unsignedvalue_typemap_types(self):
"""Test assignability of maps with unsigned value types"""
- import _cppyy, math, sys
- std = _cppyy.gbl.std
+ import _cppyy as cppyy
+ import math, sys
+ std = cppyy.gbl.std
mui = std.map(str, 'unsigned int')()
mui['one'] = 1
@@ -420,8 +422,8 @@
def test05_STL_like_class_indexing_overloads(self):
"""Test overloading of operator[] in STL like class"""
- import _cppyy
- stl_like_class = _cppyy.gbl.stl_like_class
+ import _cppyy as cppyy
+ stl_like_class = cppyy.gbl.stl_like_class
a = stl_like_class(int)()
assert a["some string" ] == 'string'
@@ -430,8 +432,8 @@
def test06_STL_like_class_iterators(self):
"""Test the iterator protocol mapping for an STL like class"""
- import _cppyy
- stl_like_class = _cppyy.gbl.stl_like_class
+ import _cppyy as cppyy
+ stl_like_class = cppyy.gbl.stl_like_class
a = stl_like_class(int)()
for i in a:
@@ -452,8 +454,8 @@
def test01_builtin_vector_iterators(self):
"""Test iterator comparison with operator== reflected"""
- import _cppyy
- std = _cppyy.gbl.std
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
v = std.vector(int)()
v.resize(1)
@@ -489,9 +491,9 @@
def test01_explicit_templates(self):
"""Explicit use of Template class"""
- import _cppyy
+ import _cppyy as cppyy
- vector = _cppyy.Template('vector', _cppyy.gbl.std)
+ vector = cppyy.Template('vector', cppyy.gbl.std)
assert vector[int] == vector(int)
v = vector[int]()
@@ -501,3 +503,54 @@
assert len(v) == N
for i in range(N):
assert v[i] == i
+
+
+class AppTestSTLARRAY:
+ spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools'])
+
+ def setup_class(cls):
+ cls.w_test_dct = cls.space.newtext(test_dct)
+ cls.w_stlarray = cls.space.appexec([], """():
+ import ctypes, _cppyy
+ _cppyy._post_import_startup()
+ return ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)""" % (test_dct, ))
+
+ def test01_array_of_basic_types(self):
+ """Usage of std::array of basic types"""
+
+ import _cppyy as cppyy
+ std = cppyy.gbl.std
+
+ a = std.array[int, 4]()
+ assert len(a) == 4
+ for i in range(len(a)):
+ a[i] = i
+ assert a[i] == i
+
+ def test02_array_of_pods(self):
+ """Usage of std::array of PODs"""
+
+ import _cppyy as cppyy
+ gbl, std = cppyy.gbl, cppyy.gbl.std
+
+ a = std.array[gbl.ArrayTest.Point, 4]()
+ assert len(a) == 4
+ for i in range(len(a)):
+ a[i].px = i
+ assert a[i].px == i
+ a[i].py = i**2
+ assert a[i].py == i**2
+
+ def test03_array_of_pointer_to_pods(self):
+ """Usage of std::array of pointer to PODs"""
+
+ import cppyy
+ from cppyy import gbl
+ from cppyy.gbl import std
+
+ ll = [gbl.ArrayTest.Point() for i in range(4)]
+ for i in range(len(ll)):
+ ll[i].px = 13*i
+ ll[i].py = 42*i
+
+ # more tests in cppyy/test/test_stltypes.py, but currently not supported
diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py
--- a/pypy/module/cpyext/cdatetime.py
+++ b/pypy/module/cpyext/cdatetime.py
@@ -12,18 +12,19 @@
from pypy.module.__pypy__.interp_pypydatetime import (W_DateTime_Date,
W_DateTime_Time, W_DateTime_Delta)
from rpython.tool.sourcetools import func_renamer
+from pypy.module.cpyext.state import State
cts.parse_header(parse_dir / 'cpyext_datetime.h')
PyDateTime_CAPI = cts.gettype('PyDateTime_CAPI')
-datetimeAPI_global = []
@cpython_api([], lltype.Ptr(PyDateTime_CAPI))
def _PyDateTime_Import(space):
- if len(datetimeAPI_global) >0:
- return datetimeAPI_global[0]
+ state = space.fromcache(State)
+ if len(state.datetimeAPI) > 0:
+ return state.datetimeAPI[0]
datetimeAPI = lltype.malloc(PyDateTime_CAPI, flavor='raw',
track_allocation=False)
@@ -66,8 +67,8 @@
_PyDelta_FromDelta.api_func.functype,
_PyDelta_FromDelta.api_func.get_wrapper(space))
- datetimeAPI_global.append(datetimeAPI)
- return datetimeAPI
+ state.datetimeAPI.append(datetimeAPI)
+ return state.datetimeAPI[0]
PyDateTime_Time = cts.gettype('PyDateTime_Time*')
PyDateTime_DateTime = cts.gettype('PyDateTime_DateTime*')
@@ -120,21 +121,10 @@
dealloc=type_dealloc,
)
- # why do we need date_dealloc? Since W_DateTime_Date is the base class for
- # app level datetime.date. If a c-extension class uses datetime.date for its
- # base class and defines a tp_dealloc, we will get this:
- # c_class->tp_dealloc == tp_dealloc_func
- # c_class->tp_base == datetime.date,
- # datetime.date->tp_dealloc = _PyPy_subtype_dealloc
- # datetime.date->tp_base = W_DateTime_Date
- # W_DateTime_Date->tp_dealloc = _PyPy_subtype_dealloc
- # but _PyPy_subtype_dealloc will call tp_dealloc_func, which can call its
- # base's tp_dealloc and we get recursion. So break the recursion by setting
- # W_DateTime_Date->tp_dealloc
make_typedescr(W_DateTime_Date.typedef,
basestruct=PyDateTime_DateTime.TO,
attach=type_attach,
- dealloc=date_dealloc,
+ dealloc=type_dealloc,
)
make_typedescr(W_DateTime_Delta.typedef,
@@ -144,30 +134,45 @@
def type_attach(space, py_obj, w_obj, w_userdata=None):
'''Fills a newly allocated py_obj from the w_obj
+ If it is a datetime.time or datetime.datetime, it may have tzinfo
'''
- if space.type(w_obj).name == 'date':
- # No tzinfo
- return
- py_datetime = rffi.cast(PyDateTime_Time, py_obj)
- w_tzinfo = space.getattr(w_obj, space.newtext('tzinfo'))
- if space.is_none(w_tzinfo):
- py_datetime.c_hastzinfo = cts.cast('unsigned char', 0)
- py_datetime.c_tzinfo = lltype.nullptr(PyObject.TO)
- else:
- py_datetime.c_hastzinfo = cts.cast('unsigned char', 1)
- py_datetime.c_tzinfo = make_ref(space, w_tzinfo)
+ state = space.fromcache(State)
+ # cannot raise here, so just crash
+ assert len(state.datetimeAPI) > 0
+ if state.datetimeAPI[0].c_TimeType == py_obj.c_ob_type:
+ py_datetime = rffi.cast(PyDateTime_Time, py_obj)
+ w_tzinfo = space.getattr(w_obj, space.newtext('tzinfo'))
+ if space.is_none(w_tzinfo):
+ py_datetime.c_hastzinfo = cts.cast('unsigned char', 0)
+ py_datetime.c_tzinfo = lltype.nullptr(PyObject.TO)
+ else:
+ py_datetime.c_hastzinfo = cts.cast('unsigned char', 1)
+ py_datetime.c_tzinfo = make_ref(space, w_tzinfo)
+ elif state.datetimeAPI[0].c_DateTimeType == py_obj.c_ob_type:
+ # For now this is exactly the same structure as PyDateTime_Time
+ py_datetime = rffi.cast(PyDateTime_DateTime, py_obj)
+ w_tzinfo = space.getattr(w_obj, space.newtext('tzinfo'))
+ if space.is_none(w_tzinfo):
+ py_datetime.c_hastzinfo = cts.cast('unsigned char', 0)
+ py_datetime.c_tzinfo = lltype.nullptr(PyObject.TO)
+ else:
+ py_datetime.c_hastzinfo = cts.cast('unsigned char', 1)
+ py_datetime.c_tzinfo = make_ref(space, w_tzinfo)
@slot_function([PyObject], lltype.Void)
def type_dealloc(space, py_obj):
- py_datetime = rffi.cast(PyDateTime_Time, py_obj)
- if (widen(py_datetime.c_hastzinfo) != 0):
- decref(space, py_datetime.c_tzinfo)
from pypy.module.cpyext.object import _dealloc
- _dealloc(space, py_obj)
-
- at slot_function([PyObject], lltype.Void)
-def date_dealloc(space, py_obj):
- from pypy.module.cpyext.object import _dealloc
+ state = space.fromcache(State)
+ # cannot raise here, so just crash
+ assert len(state.datetimeAPI) > 0
+ if state.datetimeAPI[0].c_TimeType == py_obj.c_ob_type:
+ py_datetime = rffi.cast(PyDateTime_Time, py_obj)
+ if (widen(py_datetime.c_hastzinfo) != 0):
+ decref(space, py_datetime.c_tzinfo)
+ elif state.datetimeAPI[0].c_DateTimeType == py_obj.c_ob_type:
+ py_datetime = rffi.cast(PyDateTime_DateTime, py_obj)
+ if (widen(py_datetime.c_hastzinfo) != 0):
+ decref(space, py_datetime.c_tzinfo)
_dealloc(space, py_obj)
def timedeltatype_attach(space, py_obj, w_obj, w_userdata=None):
diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py
--- a/pypy/module/cpyext/state.py
+++ b/pypy/module/cpyext/state.py
@@ -42,6 +42,8 @@
# A mapping {filename: copy-of-the-w_dict}, similar to CPython's
# variable 'extensions' in Python/import.c.
self.extensions = {}
+ # XXX will leak if _PyDateTime_Import already called
+ self.datetimeAPI = []
def set_exception(self, operror):
self.clear_exception()
diff --git a/pypy/module/cpyext/test/test_datetime.py b/pypy/module/cpyext/test/test_datetime.py
--- a/pypy/module/cpyext/test/test_datetime.py
+++ b/pypy/module/cpyext/test/test_datetime.py
@@ -307,18 +307,20 @@
from datetime import tzinfo, datetime, timedelta, time
# copied from datetime documentation
class GMT1(tzinfo):
- def utcoffset(self, dt):
- return timedelta(hours=1) + self.dst(dt)
- def dst(self, dt):
- return timedelta(0)
- def tzname(self,dt):
+ def __del__(self):
+ print 'deleting GMT1'
+ def utcoffset(self, dt):
+ return timedelta(hours=1) + self.dst(dt)
+ def dst(self, dt):
+ return timedelta(0)
+ def tzname(self,dt):
return "GMT +1"
gmt1 = GMT1()
dt1 = module.time_with_tzinfo(gmt1)
assert dt1 == time(6, 6, 6, 6, gmt1)
assert '+01' in str(dt1)
- assert module.datetime_with_tzinfo(gmt1) == datetime(
- 2000, 6, 6, 6, 6, 6, 6, gmt1)
+ dt_tz = module.datetime_with_tzinfo(gmt1)
+ assert dt_tz == datetime(2000, 6, 6, 6, 6, 6, 6, gmt1)
def test_checks(self):
module = self.import_extension('foo', [
More information about the pypy-commit
mailing list