Saturday 15 June 2019

Python2 to Python3 migration few issues identified

In this post, I have listed below few of the python issues while migrating from python 2 to python3

Install python3-tools package to get 2to3 script.

Use 2to3 script to convert all python2 source code to python3.

1) Change in subprocess module functions output

Following program fails in python3.

cat test_file
COMPANY

cat test.py
import subprocess cmd = "cat test_file" out = subprocess.check_output(cmd, shell=True) print(out)
if "NO" in out:     print("YES") else:     print("NO")
Above program works fine in python2. But it fails in python3 with below exception
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    if "NO" in out:
TypeError: a bytes-like object is required, not 'str'

In python3, by default output returned is in encoded bytes. So in above example, out variable is of type bytes and "NO" is a string.
To solve above problem,  universal_newlines to True (This argument has to be passed to check_ouput function.

In general, most of the functions available in subprocess module return bytes. So better to include argument "universal_newlines=True" to get output in string format.

2) Change in socket module
most of the functions in socket module accepts arguments in bytes format in python3, whereas arguments are passed as strings in python2.
Ex:
python2 → sock.sendall(message)
python3 → sock.sendall(message.encode('utf-8'))
python2 → client_socket.send(response_msg)
python3 → client_socket.send(response_msg.encode('utf-8'))

3) read() function returns bytes class type when file is read in binary mode. 
 
[root@sandeep ~]# cat binary_data.py
with open("random", "wb") as f:
    f.write(b'COMPANY')

out = None
with open("random", "rb") as f:
    out = f.read()

print(out)
print(out.decode())
print(type(out))

Output in Python3
root@sandeep ~]# python3 binary_data.py
b'COMPANY'
COMPANY
<class 'bytes'>

Output in Python2
[root@sandeep ~]# python2 binary_data.py
COMPANY
COMPANY
<type 'str'>

In case of python3,  If the file reading is in binary mode, then the output returned by read() function is in bytes object type.

b’COMPANY’

so the code has to explicitly decode the value to string for comparison.

if out == "COMPANY":

Above if condition fails in python3 if it is not decoded explicitly. 

I will update this post as and when I encounter new migration issues. Comment if you have guys have found any other issues with example.

Monday 10 June 2019

NamedTuple in python

Today I learnt about python container like dictionaries called "namedtuples" which is present in module called "collections"

Some of the operations performed on namedtuples are
  1. Access by index
  2. Access by keyname
  3. Access by getattr

Conversion Operations supported
  1. _make()  - returns namedtuple() from iterable passed as an argument.
  2. _asdict() - returns OrderedDict() as constructed from the mapped values of namedtuple()
  3. ** -  This operator is used to convert a dictionary into the namedtuple().

Extra Operations
  1. _fields gives the information about all the key names declared. 

Example Code:


"""

Below examples discusses about namedtuple from collections module

"""

import collections


#Declaring a namedtuple
employee = collections.namedtuple("Employee", ["Name", "Age", "DOB"])



#Adding Values
emp = employee("Johnny Depp", "35", "02011983")


#initializing iterable
ex_list = ["Abraham", "24", "20091994"]


#initializing dict
ex_dict = {"Name" : "Moti", "Age" : 29, "DOB" : "16091987"}


#Below 3print statements print Same output

print(emp.Name) #Access by key
print(emp[0]) #Access by index
print(getattr(emp, "Name")) #Access via getattr



#using _make() to return namedtuple()
print ("The namedtuple instance using iterable is : ")
print(employee._make(ex_list))


#using _asdict() to return an OrderedDict()
print("The OrderedDict instance using namedtuple is :")
print(emp._asdict())



#using ** operator to return namedtuple from dictionary
print("The namedtuple instance from dict is :")
print(employee(**ex_dict))


#print fields of the named tuple
print(emp._fields)