Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python 3 compatibility #22

Merged
merged 1 commit into from
Dec 19, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/genpy/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def generate_dynamic(core_type, msg_cat):

# write the entire text to a file and import it (it will get deleted when tmp_dir goes - above)
tmp_file = tempfile.NamedTemporaryFile(suffix=".py",dir=tmp_dir,delete=False)
tmp_file.file.write(full_text)
tmp_file.file.write(full_text.encode())
tmp_file.file.close()

# import our temporary file as a python module, which requires modifying sys.path
Expand Down
5 changes: 4 additions & 1 deletion src/genpy/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,10 @@ def string_serializer_generator(package, type_, name, serialize):
yield INDENT+"%s = %s.encode('utf-8')"%(var,var) #For unicode-strings in Python2, encode using utf-8
yield INDENT+"length = len(%s)"%(var) # Update the length after utf-8 conversion

yield pack2("'<I%ss'%length", "length, %s"%var)
yield "if python3:"
yield INDENT+pack2("'<I%sB'%length", "length, *%s"%var)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since in Python 3 the str is encoded it must be written as bytes.

yield "else:"
yield INDENT+pack2("'<I%ss'%length", "length, %s"%var)
else:
yield "start = end"
if array_len is not None:
Expand Down
7 changes: 6 additions & 1 deletion src/genpy/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,12 @@ def check_type(field_name, field_type, field_val):
elif field_type == 'string':
if sys.hexversion > 0x03000000:
if type(field_val) == str:
raise SerializationError('field %s is a unicode string instead of an ascii string'%field_name)
try:
field_val.encode('ascii')
except UnicodeEncodeError:
raise SerializationError('field %s is a non-ascii string'%field_name)
elif not type(field_val) == bytes:
raise SerializationError('field %s must be of type bytes or an ascii string'%field_name)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mimics the same behavior as the Python 2 code path: bail out on non-ascii strings.

else:
if type(field_val) == unicode:
raise SerializationError('field %s is a unicode string instead of an ascii string'%field_name)
Expand Down
14 changes: 13 additions & 1 deletion src/genpy/rostime.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,15 @@ def __str__(self):
def __repr__(self):
return "genpy.TVal[%d]"%self.to_nsec()

def __bool__(self):
"""
Return if time value is not zero
"""
return self.secs != 0 or self.nsecs != 0

def __nonzero__(self):
"""
Check if time value is zero
Check if time value is not zero
"""
return self.secs or self.nsecs

Expand Down Expand Up @@ -278,6 +284,9 @@ def __eq__(self, other):
return False
return self.secs == other.secs and self.nsecs == other.nsecs

def __hash__(self):
return super(Time, self).__hash__()

class Duration(TVal):
"""
Duration represents the ROS 'duration' primitive, which consists
Expand Down Expand Up @@ -429,3 +438,6 @@ def __eq__(self, other):
if not isinstance(other, Duration):
return False
return self.secs == other.secs and self.nsecs == other.nsecs

def __hash__(self):
return super(Duration, self).__hash__()
5 changes: 4 additions & 1 deletion test/files/array/string_fixed_ser.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ for val0 in data:
if python3 or type(val0) == unicode:
val0 = val0.encode('utf-8')
length = len(val0)
buff.write(struct.pack('<I%ss'%length, length, val0))
if python3:
buff.write(struct.pack('<I%sB'%length, length, *val0))
else:
buff.write(struct.pack('<I%ss'%length, length, val0))
5 changes: 4 additions & 1 deletion test/files/array/string_varlen_ser.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ for val0 in data:
if python3 or type(val0) == unicode:
val0 = val0.encode('utf-8')
length = len(val0)
buff.write(struct.pack('<I%ss'%length, length, val0))
if python3:
buff.write(struct.pack('<I%sB'%length, length, *val0))
else:
buff.write(struct.pack('<I%ss'%length, length, val0))
30 changes: 24 additions & 6 deletions test/test_genpy_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,38 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import cStringIO
try:
from cStringIO import StringIO
except ImportError:
from io import BytesIO as StringIO
import sys
import time

def test_generate_dynamic():
import genpy
from genpy.dynamic import generate_dynamic
msgs = generate_dynamic("gd_msgs/EasyString", "string data\n")
assert ['gd_msgs/EasyString'] == msgs.keys()
assert ['gd_msgs/EasyString'] == list(msgs.keys())
m_cls = msgs['gd_msgs/EasyString']
m_instance = m_cls()
m_instance.data = 'foo'
buff = cStringIO.StringIO()
buff = StringIO()
m_instance.serialize(buff)
m_cls().deserialize(buff.getvalue())
m_instance2 = m_cls().deserialize(buff.getvalue())
assert m_instance == m_instance2

try:
char = unichr
except NameError:
char = chr
m_instance.data = 'foo' + char(1234)
buff = StringIO()
m_instance.serialize(buff)
m_instance2 = m_cls().deserialize(buff.getvalue())
if sys.hexversion < 0x03000000:
# python 2 requires manual decode into unicode
m_instance2.data = m_instance2.data.decode('utf-8')
assert m_instance == m_instance2
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a serialize / deserialize test with unicode characters.


# 'probot_msgs' is a test for #1183, failure if the package no longer exists
msgs = generate_dynamic("gd_msgs/MoveArmState", """Header header
Expand Down Expand Up @@ -102,7 +120,7 @@ def test_generate_dynamic():
m_instance1 = msgs['probot_msgs/ControllerStatus']()
m_instance2 = msgs['probot_msgs/ControllerStatus'](value=4, comment=str(time.time()))
d = {'UNDEFINED':0,'SUCCESS':1,'ABORTED':2,'PREEMPTED':3,'ACTIVE':4}
for k, v in d.iteritems():
for k, v in d.items():
assert v == getattr(m_instance1, k)
_test_ser_deser(m_instance2, m_instance1)

Expand All @@ -124,7 +142,7 @@ def test_generate_dynamic():
_test_ser_deser(m_instance2, m_instance1)

def _test_ser_deser(m_instance1, m_instance2):
buff = cStringIO.StringIO()
buff = StringIO()
m_instance1.serialize(buff)
m_instance2.deserialize(buff.getvalue())
assert m_instance1 == m_instance2
Expand Down
5 changes: 4 additions & 1 deletion test/test_genpy_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,10 @@ def test_string_serializer_generator():
if python3 or type(var_name) == unicode:
var_name = var_name.encode('utf-8')
length = len(var_name)
buff.write(struct.pack('<I%ss'%length, length, var_name))""" == val, val
if python3:
buff.write(struct.pack('<I%sB'%length, length, *var_name))
else:
buff.write(struct.pack('<I%ss'%length, length, var_name))""" == val, val

for t in ['uint8[]', 'byte[]', 'uint8[10]', 'byte[20]']:
g = genpy.generator.string_serializer_generator('foo', 'uint8[]', 'b_name', True)
Expand Down
27 changes: 19 additions & 8 deletions test/test_genpy_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import sys
import time
import unittest
import traceback
Expand Down Expand Up @@ -213,7 +214,7 @@ def test_fill_message_args_embed_time(self):
m = TestFillEmbedTime()
try:
fill_message_args(m, test)
except Exception, e:
except Exception as e:
self.fail("failed to fill with : %s\n%s"%(str(test), traceback.format_exc()))

self.assertEquals(m.t, Time(10, 20))
Expand Down Expand Up @@ -389,15 +390,22 @@ def test_check_types_invalid(self):
'test', 'bool', -2)
self.assertRaises(SerializationError, genpy.message.check_type,
'test', 'bool', 2)
try:
u = unichr(1234)
except NameError:
u = chr(1234)
self.assertRaises(SerializationError, genpy.message.check_type,
'test', 'string', u'UnicodeString')
'test', 'string', u)

def test_Message(self):
import cStringIO
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
from genpy import Message, SerializationError
self.assert_(isinstance(Message(), Message))
m = Message()
b = cStringIO.StringIO()
b = StringIO()
m.serialize(b)
m.deserialize('')

Expand Down Expand Up @@ -560,14 +568,17 @@ def __init__(self, t, d):
nsecs: 456""", strify_message(M5(Time(987, 654), Duration(123, 456))))

# test final clause of strify -- str anything that isn't recognized
self.assertEquals("set([1])", strify_message(set([1])))
if sys.hexversion > 0x03000000: # Python3
self.assertEquals("{1}", strify_message(set([1])))
else:
self.assertEquals("set([1])", strify_message(set([1])))

def test_strify_yaml(self):
def roundtrip(m):
yaml_text = strify_message(m)
print yaml_text
print(yaml_text)
loaded = yaml.load(yaml_text)
print "loaded", loaded
print("loaded", loaded)
new_inst = m.__class__()
if loaded is not None:
fill_message_args(new_inst, [loaded])
Expand Down Expand Up @@ -629,7 +640,7 @@ def test_check_type(self):
for t, v in valids:
try:
check_type('n', t, v)
except Exception, e:
except Exception as e:
traceback.print_exc()
raise Exception("failure type[%s] value[%s]: %s"%(t, v, str(e)))

Expand Down