[pyassimp] Fix py3.3 + 64bits issues

64-bit Compatibility:

The first four characters of a String material property would be cut
off. A String's length is defined in structs.py as a c_size_t
variable, which is 8 bytes wide on 64-bit Python. However, when an
aiString is used as an aiMaterial property in C/C++, the length is
truncated down to a 4-byte value on 64-bit machines (see
MaterialSystem.cpp aiMaterial::AddProperty() for details). A new
struct was declared in structs.py (MaterialPropertyString) and used
in core._get_properties().

Python 3.3 Compatibility:

The built-in function hasattr() changed in Python 3.2 to not
trap exceptions, which means a NULL pointer ValueException now
escaped when checking if a pointer was valid (hasattr(obj,
'contents') in core.call_init()) (see
http://bugs.python.org/issue9666 for details). A new helper
function was defined that preserves the legacy functionality of
trapping the exceptions (helper.hasattr_silent()) and used
throughout the code as a replacement for hasattr().

String objects would import as "bytes" rather than as a
string. This was most noticeable in the key names for
material properties, where the trailing ' of a bytes object
would remain after it was converted to a string. The
solution was to call decode() on the bytes object using
utf-8 decoding. This applies to various parts of core.py.

Closes #35
pull/49/head
Bill Roeske 2013-06-03 10:38:30 +02:00 committed by Séverin Lemaignan
parent 43dd6fb3bc
commit 2494608927
3 changed files with 41 additions and 11 deletions

View File

@ -59,12 +59,12 @@ def make_tuple(ai_obj, type = None):
def call_init(obj, caller = None):
# init children
if hasattr(obj, '_init'):
if helper.hasattr_silent(obj, '_init'):
obj._init(parent = caller)
# pointers
elif hasattr(obj, 'contents'):
if hasattr(obj.contents, '_init'):
elif helper.hasattr_silent(obj, 'contents'):
if helper.hasattr_silent(obj.contents, '_init'):
obj.contents._init(target = obj, parent = caller)
@ -76,7 +76,7 @@ def _init(self, target = None, parent = None):
:param target: set the object which receive the added methods. Useful when manipulating
pointers, to skip the intermediate 'contents' deferencing.
"""
if hasattr(self, '_is_init'):
if helper.hasattr_silent(self, '_is_init'):
return self
self._is_init = True
@ -108,7 +108,7 @@ def _init(self, target = None, parent = None):
if isinstance(obj, structs.String):
setattr(target, 'name', str(obj.data))
setattr(target, 'name', obj.data.decode("utf-8"))
setattr(target.__class__, '__repr__', lambda x: str(x.__class__) + "(" + x.name + ")")
setattr(target.__class__, '__str__', lambda x: x.name)
continue
@ -121,7 +121,7 @@ def _init(self, target = None, parent = None):
logger.debug("Added a parent as self." + name)
continue
if hasattr(self, 'mNum' + m[1:]):
if helper.hasattr_silent(self, 'mNum' + m[1:]):
length = getattr(self, 'mNum' + m[1:])
@ -346,7 +346,7 @@ def _get_properties(properties, length):
for p in [properties[i] for i in range(length)]:
#the name
p = p.contents
key = str(p.mKey.data).split('.')[1]
key = str(p.mKey.data.decode("utf-8")).split('.')[1]
#the data
from ctypes import POINTER, cast, c_int, c_float, sizeof
@ -354,7 +354,7 @@ def _get_properties(properties, length):
arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents
value = [x for x in arr]
elif p.mType == 3: #string can't be an array
value = cast(p.mData, POINTER(structs.String)).contents.data
value = cast(p.mData, POINTER(structs.MaterialPropertyString)).contents.data.decode("utf-8")
elif p.mType == 4:
arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents
value = [x for x in arr]

View File

@ -154,5 +154,15 @@ def search_library():
# XXX: take version postfix of the .so on linux?
return res[1:]
def hasattr_silent(object, name):
"""
Calls hasttr() with the given parameters and preserves the legacy (pre-Python 3.2)
functionality of silently catching exceptions.
Returns the result of hasatter() or False if an exception was raised.
"""
try:
return hasattr(object, name)
except:
return False

View File

@ -1,6 +1,6 @@
#-*- coding: UTF-8 -*-
from ctypes import POINTER, c_void_p, c_int, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t
from ctypes import POINTER, c_void_p, c_int, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t, c_uint32
class Vector2D(Structure):
@ -82,6 +82,26 @@ class String(Structure):
("data", c_char*MAXLEN),
]
class MaterialPropertyString(Structure):
"""
See 'aiTypes.h' for details.
The size of length is truncated to 4 bytes on 64-bit platforms when used as a
material property (see MaterialSystem.cpp aiMaterial::AddProperty() for details).
"""
MAXLEN = 1024
_fields_ = [
# Binary length of the string excluding the terminal 0. This is NOT the
# logical length of strings containing UTF-8 multibyte sequences! It's
# the number of bytes from the beginning of the string to its end.
("length", c_uint32),
# String buffer. Size limit is MAXLEN
("data", c_char*MAXLEN),
]
class MemoryInfo(Structure):
"""
See 'aiTypes.h' for details.