0

Having some trouble translating these two methods from python2 to python3.

Python2:

def send(self, data):
    if self.debug:
        print 'Send:',
        print ':'.join('%02x' % ord(c) for c in data)
    l0 = len(data) & 0xFF
    l1 = (len(data) >> 8) & 0xFF
    d = chr(l0) + chr(l1) + data
    self.sock.send(d)

def recv(self):
    data = self.sock.recv(2)
    l0 = ord(data[0])
    l1 = ord(data[1])
    plen = l0 + (l1 << 8)
    data = self.sock.recv(plen)
    if self.debug:
        print 'Recv:',
        print ':'.join('%02x' % ord(c) for c in data)
    return data

Python 3 This what I got so far :

def send(self, data):
    if self.debug:
        print('Send:', end=' ')
        print(':'.join('%02x' % ord(c) for c in data))
    l0 = len(data.encode('utf-8')) & 0xFF
    l1 = (len(data.encode('utf-8')) >> 8) & 0xFF
    d = chr(l0) + chr(l1) + data
    self.sock.send(d)

def recv(self):
    data = self.sock.recv(2)
    l0 = ord(data[0])
    l1 = ord(data[1])
    plen = l0 + (l1 << 8)
    data = self.sock.recv(plen)
    if self.debug:
        print('Recv:', end=' ')
        print(':'.join('%02x' % ord(c) for c in data))
    return data

I keep getting this error:

TypeError: ord() expected string of length 1, but int found

Any help would be appreciated. Thank you

Dawzer
  • 169
  • 1
  • 2
  • 13
  • have you already consulted the [`2to3`](https://docs.python.org/3.0/library/2to3.html) tool? The issue that is raising the error is that you have a `bytes` object instead of `str` which when iterated gives the ints directly so no need to call `ord` on it. – Tadhg McDonald-Jensen Mar 13 '17 at 23:37

2 Answers2

2

There are two main types of issues you need to address to make your code work in Python 3.

The biggest issue is that strings and bytes are no longer represented by the same types in Python 3. The str type is for Unicode strings, the bytes type is for binary data. Your data argument looks like it should probably be bytes (since you're sending it directly out on a socket). If you did want to support Unicode strings, you'd need to encode() them with some encoding (e.g. "UTF-8") before sending them over the socket.

Anyway, assuming data is a bytes instance, you'll need to make a few small changes to the code to address how a few APIs work different for str and bytes:

  1. Iterating on a bytes yields the individual byte values, but as integers, not as one-character bytestrings. This basically means you don't need the ord in the print calls, nor in the first parts of recv.

  2. The chr function creates a str, not a bytes instance, and you can't concatenate the different types together. Creating a bytes instance from an integer is a bit awkward (bytes(some_number) doesn't do what you want), but it is possible.

The other issue you have is much simpler to understand. In Python 3, print is a function rather than a statement, so you need parentheses around its arguments. It also uses different syntax to suppress line endings.

Here's a fully fixed version of your code:

def send(self, data):
    if self.debug:
        print('Send:', end='')                     # new way to suppress the newline
        print(':'.join('%02x' % c for c in data))  # add parentheses, no need for ord
    l0 = len(data) & 0xFF
    l1 = (len(data) >> 8) & 0xFF
    d = bytes([l0, l1]) + data                     # build prefix bytestring differently
    self.sock.send(d)

def recv(self):
    l0, l1 = self.sock.recv(2)              # no need for ord, unpack directly as ints
    plen = l0 + (l1 << 8)
    data = self.sock.recv(plen)
    if self.debug:
        print('Recv:', end='')
        print(':'.join('%02x' % c for c in data))
    return data

Note that the struct module may offer a more elegant way of encoding and decoding the length of your data to a bytestring. For instance, struct.pack("<H", len(data)) could replace several lines of the code in send (you wouldn't need l0 and l1).

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • `help(bytes)` indicates "_bytes(iterable_of_ints) -> bytes_" and "_bytes(int) -> bytes object of size given by the parameter initialized with null bytes_" so to make a single character bytes object you just do `bytes([num])` instead of `bytes(num)` – Tadhg McDonald-Jensen Mar 13 '17 at 23:41
  • @TadhgMcDonald-Jensen: Yep, that's the awkwardness I was referring to. There's no need for `bytes([l0]) + bytes([l1])`. You can just build a single pre-concatentated bytestring from a list with both integers in it. The `struct.pack` function is even nicer (though it appears I got the sign for the required endianness wrong). – Blckknght Mar 13 '17 at 23:45
  • Yes but you never actually state why `bytes(num)` doesn't make a single byte, just that it doesn't do it. I figured I should mention what it actually does for the OP (Occurs to me I should have said that from the get go) Still this is a very well formed answer. – Tadhg McDonald-Jensen Mar 13 '17 at 23:49
  • @Blckknght Thank you very much, this is very helpful ! – Dawzer Mar 14 '17 at 00:12
0

insteed of print a use print (a) like: python 2.x:

def send(self, data):
    if self.debug:
        print 'Send:',
        print ':'.join('%02x' % ord(c) for c in data)
    l0 = len(data) & 0xFF
    l1 = (len(data) >> 8) & 0xFF
    d = chr(l0) + chr(l1) + data
    self.sock.send(d)

def recv(self):
    data = self.sock.recv(2)
    l0 = ord(data[0])
    l1 = ord(data[1])
    plen = l0 + (l1 << 8)
    data = self.sock.recv(plen)
    if self.debug:
        print 'Recv:',
        print ':'.join('%02x' % ord(c) for c in data)
    return data

python 3.x:

def send(self, data):
    if self.debug:
        print ('Send:'),
        print (':'.join('%02x' % ord(c) for c in data))
    l0 = len(data) & 0xFF
    l1 = (len(data) >> 8) & 0xFF
    d = chr(l0) + chr(l1) + data
    self.sock.send(d)

def recv(self):
    data = self.sock.recv(2)
    l0 = ord(data[0])
    l1 = ord(data[1])
    plen = l0 + (l1 << 8)
    data = self.sock.recv(plen)
    if self.debug:
        print ('Recv:'),
        print (':'.join('%02x' % ord(c) for c in data))
    return data
NINO Cikoo
  • 84
  • 1
  • 9