Merge PR #11, prep 0.9.6 . Clobbered the PR's predictionio/__init__.py conflicting changes, fixed the rest
diff --git a/.gitignore b/.gitignore
index 66fa4a6..1822229 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
+*.egg-info
+.idea
+.tox
+.coverage
 *.pyc
 .project
 .pydevproject
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 0d87655..d52d83e 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -11,13 +11,13 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-#sys.path.append("..")
+# sys.path.insert(0, os.path.abspath('.'))
+# sys.path.append("..")
 # Use path in the SDK. Hence need to override system package path by inserting
 # to position 0.
 sys.path.insert(0, "..")
@@ -25,7 +25,7 @@
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
@@ -38,7 +38,7 @@
 source_suffix = '.rst'
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
 master_doc = 'index'
@@ -54,41 +54,41 @@
 # The short X.Y version.
 version = '0.9'
 # The full version, including alpha/beta/rc tags.
-release = '0.9.2'
+release = '0.9.6'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
@@ -100,72 +100,72 @@
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'PredictionIO-Python-SDKdoc'
@@ -174,42 +174,42 @@
 # -- Options for LaTeX output --------------------------------------------------
 
 latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
+    # The paper size ('letterpaper' or 'a4paper').
+    # 'papersize': 'letterpaper',
 
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
+    # The font size ('10pt', '11pt' or '12pt').
+    # 'pointsize': '10pt',
 
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
+    # Additional stuff for the LaTeX preamble.
+    # 'preamble': '',
 }
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'PredictionIO-Python-SDK.tex', u'PredictionIO-Python-SDK Documentation',
-   u'TappingStone', 'manual'),
+    ('index', 'PredictionIO-Python-SDK.tex', u'PredictionIO-Python-SDK Documentation',
+     u'TappingStone', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output --------------------------------------------
@@ -222,27 +222,27 @@
 ]
 
 # If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
 
 
 # -- Options for Texinfo output ------------------------------------------------
 
 # Grouping the document tree into Texinfo files. List of tuples
 # (source start file, target name, title, author,
-#  dir menu entry, description, category)
+# dir menu entry, description, category)
 texinfo_documents = [
-  ('index', 'PredictionIO-Python-SDK', u'PredictionIO-Python-SDK Documentation',
-   u'TappingStone', 'PredictionIO-Python-SDK', 'One line description of project.',
-   'Miscellaneous'),
+    ('index', 'PredictionIO-Python-SDK', u'PredictionIO-Python-SDK Documentation',
+     u'TappingStone', 'PredictionIO-Python-SDK', 'One line description of project.',
+     'Miscellaneous'),
 ]
 
 # Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
 
 # If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
 
 autodoc_member_order = 'bysource'
diff --git a/examples/demo-movielens/appdata.py b/examples/demo-movielens/appdata.py
index d9637ef..354f31e 100644
--- a/examples/demo-movielens/appdata.py
+++ b/examples/demo-movielens/appdata.py
@@ -1,6 +1,5 @@
-
 import datetime
-from operator import itemgetter, attrgetter
+from operator import attrgetter
 
 # can get sample data here:
 # wget http://www.grouplens.org/system/files/ml-100k.zip
@@ -15,169 +14,170 @@
 
 
 class User:
-  def __init__(self, uid):
-    self.uid = uid
-    self.rec = [] # recommendations, list of iid
+    def __init__(self, uid):
+        self.uid = uid
+        self.rec = []  # recommendations, list of iid
 
-  def __str__(self):
-    return "User[uid=%s,rec=%s]" % (self.uid, self.rec)
+    def __str__(self):
+        return "User[uid=%s,rec=%s]" % (self.uid, self.rec)
+
 
 class Item:
-  def __init__(self, iid, name, release_date, genres, year):
-    self.iid = iid
-    self.name = name
-    self.release_date = release_date # datetime.datetime object
-    self.genres = genres
-    self.year = year
+    def __init__(self, iid, name, release_date, genres, year):
+        self.iid = iid
+        self.name = name
+        self.release_date = release_date  # datetime.datetime object
+        self.genres = genres
+        self.year = year
 
-  def __str__(self):
-    return "Item[iid=%s,name=%s,release_date=%s,genres=%s]" % (self.iid, self.name, self.release_date, self.genres)
+    def __str__(self):
+        return "Item[iid=%s,name=%s,release_date=%s,genres=%s]" % (self.iid, self.name, self.release_date, self.genres)
+
 
 class RateAction:
-  def __init__(self, uid, iid, rating, t):
-    self.uid = uid
-    self.iid = iid
-    self.rating = rating
-    self.t = t
+    def __init__(self, uid, iid, rating, t):
+        self.uid = uid
+        self.iid = iid
+        self.rating = rating
+        self.t = t
 
-  def __str__(self):
-    return "RateAction[uid=%s,iid=%s,rating=%s,t=%s]" % (self.uid, self.iid, self.rating, self.t)
+    def __str__(self):
+        return "RateAction[uid=%s,iid=%s,rating=%s,t=%s]" % (self.uid, self.iid, self.rating, self.t)
 
 
 class AppData:
+    def __init__(self):
+        self._users = {}  # dict of User obj
+        self._items = {}  # dict of Item obj
+        self._rate_actions = []  # list of RateAction obj
 
-  def __init__(self):
-    self._users = {} # dict of User obj
-    self._items = {} # dict of Item obj
-    self._rate_actions = [] # list of RateAction obj
+        self._users_file = "%s/%s" % (APPDATA_DIRNAME, USERS_FILENAME)
+        self._items_file = "%s/%s" % (APPDATA_DIRNAME, ITEMS_FILENAME)
+        self._rate_actions_file = "%s/%s" % (APPDATA_DIRNAME, RATE_ACTIONS_FILENAME)
+        self.__init_users()
+        self.__init_items()
+        self.__init_rate_actions()
 
-    self._users_file = "%s/%s" % (APPDATA_DIRNAME, USERS_FILENAME)
-    self._items_file = "%s/%s" % (APPDATA_DIRNAME, ITEMS_FILENAME)
-    self._rate_actions_file = "%s/%s" % (APPDATA_DIRNAME, RATE_ACTIONS_FILENAME)
-    self.__init_users()
-    self.__init_items()
-    self.__init_rate_actions()
+    def __init_users(self):
+        """
+        uid|
+        """
+        print("[Info] Initializing users...")
+        f = open(self._users_file, 'r')
+        for line in f:
+            data = line.rstrip('\r\n').split(USERS_FILE_DELIMITER)
+            self.add_user(User(data[0]))
+        f.close()
+        print("[Info] %s users were initialized." % len(self._users))
 
-  def __init_users(self):
-    """
-    uid|
-    """
-    print "[Info] Initializing users..."
-    f = open(self._users_file, 'r')
-    for line in f:
-      data = line.rstrip('\r\n').split(USERS_FILE_DELIMITER)
-      self.add_user(User(data[0]))
-    f.close()
-    print "[Info] %s users were initialized." % len(self._users)
+    def __init_items(self):
+        """
+        movie id | movie title | release date | video release date |
+            IMDb URL | unknown | Action | Adventure | Animation |
+            Children's | Comedy | Crime | Documentary | Drama | Fantasy |
+            Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi |
+            Thriller | War | Western |
+            The last 19 fields are the genres, a 1 indicates the movie
+            is of that genre, a 0 indicates it is not; movies can be in
+            several genres at once.
 
-  def __init_items(self):
-    """
-    movie id | movie title | release date | video release date |
-        IMDb URL | unknown | Action | Adventure | Animation |
-        Children's | Comedy | Crime | Documentary | Drama | Fantasy |
-        Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi |
-        Thriller | War | Western |
-        The last 19 fields are the genres, a 1 indicates the movie
-        is of that genre, a 0 indicates it is not; movies can be in
-        several genres at once.
+        """
+        genre_names = ["unknown", "Action", "Adventure", "Animation",
+                       "Children's", "Comedy", "Crime", "Documentary", "Drama", "Fantasy",
+                       "Film-Noir", "Horror", "Musical", "Mystery", "Romance", "Sci-Fi",
+                       "Thriller", "War", "Western"]
 
-    """
-    genre_names = [ "unknown", "Action", "Adventure", "Animation",
-      "Children's", "Comedy", "Crime", "Documentary", "Drama", "Fantasy",
-      "Film-Noir", "Horror", "Musical", "Mystery", "Romance", "Sci-Fi",
-      "Thriller", "War", "Western"]
+        print("[Info] Initializing items...")
+        f = open(self._items_file, 'r')
+        for line in f:
+            data = line.rstrip('\r\n').split(ITEMS_FILE_DELIMITER)
+            genres_flags = data[5:24]
 
-    print "[Info] Initializing items..."
-    f = open(self._items_file, 'r')
-    for line in f:
-      data = line.rstrip('\r\n').split(ITEMS_FILE_DELIMITER)
-      genres_flags = data[5:24]
+            genres = ()  # tuple of genres
+            for g, flag in zip(genre_names, genres_flags):
+                if flag == '1':
+                    genres = genres + (g,)
 
-      genres = () # tuple of genres
-      for g,flag in zip(genre_names, genres_flags):
-        if flag == '1':
-          genres = genres + (g,)
+            try:
+                # eg. 01-Jan-1994
+                release_date = datetime.datetime.strptime(data[2], "%d-%b-%Y").replace(microsecond=1)
+                (day, month, year) = data[2].split('-')
+            except:
+                print("[Note] item %s %s doesn't have release date. Skip it." % (data[0], data[1]))
+            else:
+                self.add_item(Item(
+                    iid=data[0],
+                    name=data[1],
+                    release_date=release_date,
+                    genres=genres,
+                    year=year))
+        f.close()
+        print("[Info] %s items were initialized." % len(self._items))
 
-      try:
-        # eg. 01-Jan-1994
-        release_date = datetime.datetime.strptime(data[2], "%d-%b-%Y").replace(microsecond=1)
-        (day, month, year) = data[2].split('-')
-      except:
-        print "[Note] item %s %s doesn't have release date. Skip it." % (data[0], data[1])
-      else:
-        self.add_item(Item(
-          iid=data[0],
-          name=data[1],
-          release_date=release_date,
-          genres=genres,
-          year=year))
-    f.close()
-    print "[Info] %s items were initialized." % len(self._items)
+    def __init_rate_actions(self):
+        """
+        uid|iid|rating|timestamp
+        """
+        print("[Info] Initializing rate actions...")
+        f = open(self._rate_actions_file, 'r')
+        for line in f:
+            data = line.rstrip('\r\n').split(RATE_ACTIONS_DELIMITER)
+            t = datetime.datetime.utcfromtimestamp(int(data[3])).replace(microsecond=1)
+            self.add_rate_action(RateAction(data[0], data[1], data[2], t))
+        f.close()
+        print("[Info] %s rate actions were initialized." % len(self._rate_actions))
 
-  def __init_rate_actions(self):
-    """
-    uid|iid|rating|timestamp
-    """
-    print "[Info] Initializing rate actions..."
-    f = open(self._rate_actions_file, 'r')
-    for line in f:
-      data = line.rstrip('\r\n').split(RATE_ACTIONS_DELIMITER)
-      t = datetime.datetime.utcfromtimestamp(int(data[3])).replace(microsecond=1)
-      self.add_rate_action(RateAction(data[0], data[1], data[2], t))
-    f.close()
-    print "[Info] %s rate actions were initialized." % len(self._rate_actions)
+    def add_user(self, user):
+        self._users[user.uid] = user
 
-  def add_user(self, user):
-    self._users[user.uid] = user
+    def add_item(self, item):
+        self._items[item.iid] = item
 
-  def add_item(self, item):
-    self._items[item.iid] = item
+    def add_rate_action(self, action):
+        self._rate_actions.append(action)
 
-  def add_rate_action(self, action):
-    self._rate_actions.append(action)
+    def get_users(self):
+        return self._users
 
-  def get_users(self):
-    return self._users
+    def get_items(self):
+        return self._items
 
-  def get_items(self):
-    return self._items
+    def get_rate_actions(self):
+        return self._rate_actions
 
-  def get_rate_actions(self):
-    return self._rate_actions
+    def get_user(self, uid):
+        """return single user
+        """
+        if uid in self._users:
+            return self._users[uid]
+        else:
+            return None
 
-  def get_user(self, uid):
-    """return single user
-    """
-    if uid in self._users:
-      return self._users[uid]
-    else:
-      return None
+    def get_item(self, iid):
+        """return single item
+        """
+        if iid in self._items:
+            return self._items[iid]
+        else:
+            return None
 
-  def get_item(self, iid):
-    """return single item
-    """
-    if iid in self._items:
-      return self._items[iid]
-    else:
-      return None
+    def get_top_rated_items(self, uid, n):
+        """get top n rated iids by this uid
+        """
+        if uid in self._users:
+            actions = filter(lambda u: u.uid == uid, self._rate_actions)
+            top = sorted(actions, key=attrgetter('rating'), reverse=True)
+            topn_iids = map(lambda a: a.iid, top[:n])
+            return topn_iids
+        else:
+            return None
 
-  def get_top_rated_items(self, uid, n):
-    """get top n rated iids by this uid
-    """
-    if uid in self._users:
-      actions = filter(lambda u: u.uid==uid, self._rate_actions)
-      top = sorted(actions, key=attrgetter('rating'), reverse=True)
-      topn_iids = map(lambda a: a.iid, top[:n])
-      return topn_iids
-    else:
-      return None
-
-  def get_top_rate_actions(self, uid, n):
-    """get top n rated actions by this uid
-    """
-    if uid in self._users:
-      actions = filter(lambda u: u.uid==uid, self._rate_actions)
-      top = sorted(actions, key=attrgetter('rating'), reverse=True)
-      return top[:n]
-    else:
-      return None
+    def get_top_rate_actions(self, uid, n):
+        """get top n rated actions by this uid
+        """
+        if uid in self._users:
+            actions = filter(lambda u: u.uid == uid, self._rate_actions)
+            top = sorted(actions, key=attrgetter('rating'), reverse=True)
+            return top[:n]
+        else:
+            return None
diff --git a/examples/demo-movielens/batch_import.py b/examples/demo-movielens/batch_import.py
index f0fcb7c..af9c4f5 100644
--- a/examples/demo-movielens/batch_import.py
+++ b/examples/demo-movielens/batch_import.py
@@ -1,133 +1,135 @@
+import sys
+import datetime
+
+import pytz
 
 from appdata import AppData
 import predictionio
-import sys
-import pytz
-import datetime
+
 
 def batch_import_task(app_data, client, all_info=False):
-  # event_time is an important properties used by the PredictionIO platform. It
-  # is particularly useful in generating training and testing set, which uses
-  # event_time for splitting. Hence, when we import data, better to make the
-  # event_time as approximate to fact as possible.
-  #
-  # However, in many cases, the data doesn't come with a time. Movie-lens' user
-  # data, for example, only reveals the age, gender, occupation, and zip code of
-  # a user. It doesn't report when the user is "created". Likewise, for items,
-  # it only reports the release date.
-  #
-  # To remedy this problem, we have to make some assumptions to the data. In
-  # this import script, the event_time for user is set to epoch=0, and the
-  # event_time for item is set to the release_date + 00:00:00 UTC.
+    # event_time is an important properties used by the PredictionIO platform. It
+    # is particularly useful in generating training and testing set, which uses
+    # event_time for splitting. Hence, when we import data, better to make the
+    # event_time as approximate to fact as possible.
+    #
+    # However, in many cases, the data doesn't come with a time. Movie-lens' user
+    # data, for example, only reveals the age, gender, occupation, and zip code of
+    # a user. It doesn't report when the user is "created". Likewise, for items,
+    # it only reports the release date.
+    #
+    # To remedy this problem, we have to make some assumptions to the data. In
+    # this import script, the event_time for user is set to epoch=0, and the
+    # event_time for item is set to the release_date + 00:00:00 UTC.
 
-  print "[Info] Importing users to PredictionIO..."
-  user_create_time = datetime.datetime.fromtimestamp(0, tz=pytz.utc)
-  count = 0
-  set_user_request_list = []
-  for k, v in app_data.get_users().iteritems():
-    count += 1
-    if all_info:
-      print "[Info] Importing %s..." % v
-    else:
-      if (count % 32 == 0):
-        sys.stdout.write('\r[Info] %s' % count)
-        sys.stdout.flush()
+    print("[Info] Importing users to PredictionIO...")
+    user_create_time = datetime.datetime.fromtimestamp(0, tz=pytz.utc)
+    count = 0
+    set_user_request_list = []
+    for k, v in app_data.get_users().iteritems():
+        count += 1
+        if all_info:
+            print("[Info] Importing %s..." % v)
+        else:
+            if count % 32 == 0:
+                sys.stdout.write('\r[Info] %s' % count)
+                sys.stdout.flush()
 
-    set_user_request_list.append(
-        client.aset_user(uid=v.uid, event_time=user_create_time))
+        set_user_request_list.append(
+            client.aset_user(uid=v.uid, event_time=user_create_time))
 
-  [r.get_response() for r in set_user_request_list]
-  sys.stdout.write('\r[Info] %s users were imported.\n' % count)
-  sys.stdout.flush()
+    [r.get_response() for r in set_user_request_list]
+    sys.stdout.write('\r[Info] %s users were imported.\n' % count)
+    sys.stdout.flush()
 
-  print "[Info] Importing items to PredictionIO..."
-  count = 0
-  set_item_request_list = []
-  # event_time is a datetime, hence need to add a time component to the release
-  # date.
-  midnight_utc = datetime.time(0, 0, 0, tzinfo=pytz.utc)
-  epoch = datetime.datetime.fromtimestamp(0, tz=pytz.utc)
-  for k, v in app_data.get_items().iteritems():
-    count += 1
-    if all_info:
-      print "[Info] Importing %s..." % v
-    else:
-      if (count % 32 == 0):
-        sys.stdout.write('\r[Info] %s' % count)
-        sys.stdout.flush()
+    print("[Info] Importing items to PredictionIO...")
+    count = 0
+    set_item_request_list = []
+    # event_time is a datetime, hence need to add a time component to the release
+    # date.
+    midnight_utc = datetime.time(0, 0, 0, tzinfo=pytz.utc)
+    epoch = datetime.datetime.fromtimestamp(0, tz=pytz.utc)
+    for k, v in app_data.get_items().iteritems():
+        count += 1
+        if all_info:
+            print("[Info] Importing %s..." % v)
+        else:
+            if count % 32 == 0:
+                sys.stdout.write('\r[Info] %s' % count)
+                sys.stdout.flush()
 
-    itypes = ("movie",) + v.genres
+        itypes = ("movie",) + v.genres
 
-    release_datetime = datetime.datetime.combine(
-        v.release_date,
-        midnight_utc)
+        release_datetime = datetime.datetime.combine(
+            v.release_date,
+            midnight_utc)
 
-    # event_time must be after epoch.
-    event_time = release_datetime if release_datetime > epoch else epoch
+        # event_time must be after epoch.
+        event_time = release_datetime if release_datetime > epoch else epoch
 
-    utf8_name = v.name.decode('utf-8', 'ignore')
-    
-    set_item_request = client.aset_item(
-        iid=v.iid,
-        event_time=event_time,
-        properties={ 
-          "itypes": list(itypes),
-          "starttime": release_datetime.isoformat(),
-          "name": utf8_name,
-          "year": v.year } )
+        utf8_name = v.name.decode('utf-8', 'ignore')
 
-    set_item_request_list.append(set_item_request)
+        set_item_request = client.aset_item(
+            iid=v.iid,
+            event_time=event_time,
+            properties={
+                "itypes": list(itypes),
+                "starttime": release_datetime.isoformat(),
+                "name": utf8_name,
+                "year": v.year})
 
-  [r.get_response() for r in set_item_request_list]
-  sys.stdout.write('\r[Info] %s items were imported.\n' % count)
-  sys.stdout.flush()
+        set_item_request_list.append(set_item_request)
 
-  print "[Info] Importing rate actions to PredictionIO..."
-  count = 0
-  create_event_request_list = []
-  for v in app_data.get_rate_actions():
-    count += 1
-    if all_info:
-      print "[Info] Importing %s..." % v
-    else:
-      if (count % 32 == 0):
-        sys.stdout.write('\r[Info] %s' % count)
-        sys.stdout.flush()
+    [r.get_response() for r in set_item_request_list]
+    sys.stdout.write('\r[Info] %s items were imported.\n' % count)
+    sys.stdout.flush()
 
-    properties = { "rating" : int(v.rating) }
-    req = client.acreate_event(
-        event="rate",
-        entity_type="user",
-        entity_id=v.uid,
-        target_entity_type="item",
-        target_entity_id=v.iid,
-        properties=properties,
-        event_time=v.t.replace(tzinfo=pytz.utc),
+    print("[Info] Importing rate actions to PredictionIO...")
+    count = 0
+    create_event_request_list = []
+    for v in app_data.get_rate_actions():
+        count += 1
+        if all_info:
+            print("[Info] Importing %s..." % v)
+        else:
+            if count % 32 == 0:
+                sys.stdout.write('\r[Info] %s' % count)
+                sys.stdout.flush()
+
+        properties = {"rating": int(v.rating)}
+        req = client.acreate_event(
+            event="rate",
+            entity_type="user",
+            entity_id=v.uid,
+            target_entity_type="item",
+            target_entity_id=v.iid,
+            properties=properties,
+            event_time=v.t.replace(tzinfo=pytz.utc),
         )
 
-    create_event_request_list.append(req)
+        create_event_request_list.append(req)
 
-  [r.get_response() for r in create_event_request_list]
-  sys.stdout.write('\r[Info] %s rate actions were imported.\n' % count)
-  sys.stdout.flush()
+    [r.get_response() for r in create_event_request_list]
+    sys.stdout.write('\r[Info] %s rate actions were imported.\n' % count)
+    sys.stdout.flush()
 
 
 if __name__ == '__main__':
-  if len(sys.argv) < 3:
-    sys.exit("Usage: python -m examples.demo-movielens.batch_import "
-        "<access_key> <url>")
+    if len(sys.argv) < 3:
+        sys.exit("Usage: python -m examples.demo-movielens.batch_import "
+                 "<access_key> <url>")
 
-  access_key = sys.argv[1]
+    access_key = sys.argv[1]
 
-  client = predictionio.EventClient(
-      access_key=access_key,
-      url=sys.argv[2],
-      threads=5,
-      qsize=500)
+    client = predictionio.EventClient(
+        access_key=access_key,
+        url=sys.argv[2],
+        threads=5,
+        qsize=500)
 
-  # Test connection
-  print "Status:", client.get_status()
-  
-  app_data = AppData()
-  batch_import_task(app_data, client)
-  client.close()
+    # Test connection
+    print("Status:", client.get_status())
+
+    app_data = AppData()
+    batch_import_task(app_data, client)
+    client.close()
diff --git a/examples/event_sample.py b/examples/event_sample.py
index 9c3fe25..b76bcf7 100644
--- a/examples/event_sample.py
+++ b/examples/event_sample.py
@@ -1,10 +1,12 @@
+from datetime import datetime
+
+import pytz
+
 from predictionio import EventClient
 from predictionio import NotFoundError
-from datetime import datetime
-import pytz
-import sys
 
-access_key = None
+
+access_key = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"
 assert access_key is not None, "Please create an access key with 'pio app new'"
 
 client = EventClient(access_key=access_key, url="http://localhost:7070")
@@ -15,13 +17,13 @@
 
 # First event
 first_event_properties = {
-    "prop1" : 1,
-    "prop2" : "value2",
-    "prop3" : [1, 2, 3],
-    "prop4" : True,
-    "prop5" : ["a", "b", "c"],
-    "prop6" : 4.56 ,
-    }
+    "prop1": 1,
+    "prop2": "value2",
+    "prop3": [1, 2, 3],
+    "prop4": True,
+    "prop5": ["a", "b", "c"],
+    "prop6": 4.56,
+}
 first_event_time = datetime(
     2004, 12, 13, 21, 39, 45, 618000, pytz.timezone('US/Mountain'))
 first_event_response = client.create_event(
@@ -30,16 +32,16 @@
     entity_id="uid",
     properties=first_event_properties,
     event_time=first_event_time,
-    )
+)
 print("First Event response")
 print(first_event_response)
-print
+print()
 
 # Second event
 second_event_properties = {
-    "someProperty" : "value1",
-    "anotherProperty" : "value2",
-    }
+    "someProperty": "value1",
+    "anotherProperty": "value2",
+}
 second_event_response = client.create_event(
     event="my_event",
     entity_type="user",
@@ -50,7 +52,7 @@
     event_time=datetime(2014, 12, 13, 21, 38, 45, 618000, pytz.utc))
 print("Second Event response")
 print(second_event_response)
-print
+print()
 
 
 # Get the first event from Event Server
@@ -58,21 +60,21 @@
 print("Get Event")
 event = client.get_event(first_event_id)
 print(event)
-print
+print()
 
 # Delete the first event from Event Server
 print("Delete Event")
 delete_response = client.delete_event(first_event_id)
 print(delete_response)
-print
+print()
 
 # Delete the first event from Event Server again should yield exception.
 print("Delete Event Again")
 try:
-  delete_response = client.delete_event(first_event_id)
-except NotFoundError, ex:
-  print("The expected error: {0}".format(ex))
-print
+    delete_response = client.delete_event(first_event_id)
+except NotFoundError as ex:
+    print("The expected error: {0}".format(ex))
+print()
 
 # "user"-helper methods
 
diff --git a/examples/import_yahoo.py b/examples/import_yahoo.py
index 93b367d..584a8c9 100644
--- a/examples/import_yahoo.py
+++ b/examples/import_yahoo.py
@@ -3,13 +3,14 @@
 """
 
 from datetime import datetime
+import sys
+
+import pytz
+
 from pandas.io import data as pdata
-import argparse
 import numpy
 import predictionio
-import pytz
-import sys
-import time
+
 
 EPOCH = datetime(1970, 1, 1, tzinfo=pytz.utc)
 
@@ -63,151 +64,151 @@
     "WU", "WY", "WYN", "WYNN", "X", "XEL", "XL", "XLNX", "XOM", "XRAY", "XRX",
     "XYL", "YHOO", "YUM", "ZION", "ZMH", "ZTS"]
 
-ETF_LIST = ["QQQ", "SPY", "XLY", "XLP", "XLE", "XLF", "XLV", 
-    "XLI", "XLB", "XLK", "XLU"]
+ETF_LIST = ["QQQ", "SPY", "XLY", "XLP", "XLE", "XLF", "XLV",
+            "XLI", "XLB", "XLK", "XLU"]
 
 
 def since_epoch(dt):
-  return (dt - EPOCH).total_seconds()
+    return (dt - EPOCH).total_seconds()
 
 
 def import_data(client, access_key, ticker, start_time, end_time, event_time):
-  print "Importing:", ticker, start_time, end_time
+    print("Importing:", ticker, start_time, end_time)
 
-  try:
-    df = pdata.DataReader(ticker, 'yahoo', start_time, end_time)
-    print "Extracted:", df.index[0], df.index[-1]
-  except IOError, ex:
-    print ex
-    print "Data not exist. Returning"
-    return
+    try:
+        df = pdata.DataReader(ticker, 'yahoo', start_time, end_time)
+        print("Extracted:", df.index[0], df.index[-1])
+    except IOError as ex:
+        print(ex)
+        print("Data not exist. Returning")
+        return
 
-  # assume we only extract US data
-  eastern = pytz.timezone('US/Eastern')
+    # assume we only extract US data
+    eastern = pytz.timezone('US/Eastern')
 
-  columns = [
-      ('Open', 'open'),
-      ('High', 'high'),
-      ('Low', 'low'),
-      ('Close', 'close'),
-      ('Volume', 'volume'),
-      ('Adj Close', 'adjclose')]
+    columns = [
+        ('Open', 'open'),
+        ('High', 'high'),
+        ('Low', 'low'),
+        ('Close', 'close'),
+        ('Volume', 'volume'),
+        ('Adj Close', 'adjclose')]
 
-  yahoo_data = dict()
-  yahoo_data['ticker'] = ticker
-  yahoo_data['t'] = [
-      # hour=16 to indicate market close time
-      since_epoch(eastern.localize(date_.to_pydatetime().replace(hour=16)))
-      for date_ in df.index]
+    yahoo_data = dict()
+    yahoo_data['ticker'] = ticker
+    yahoo_data['t'] = [
+        # hour=16 to indicate market close time
+        since_epoch(eastern.localize(date_.to_pydatetime().replace(hour=16)))
+        for date_ in df.index]
 
-  for column in columns:
-    yahoo_data[column[1]] = map(numpy.asscalar, df[column[0]].values)
+    for column in columns:
+        yahoo_data[column[1]] = map(numpy.asscalar, df[column[0]].values)
 
-  properties = {'yahoo': yahoo_data}
+    properties = {'yahoo': yahoo_data}
 
-  response = client.create_event(
-      event='$set',
-      entity_type='yahoo',
-      entity_id=ticker,
-      properties=properties,
-      event_time=event_time.replace(tzinfo=pytz.utc))
-      
-  print(response)
+    response = client.create_event(
+        event='$set',
+        entity_type='yahoo',
+        entity_id=ticker,
+        properties=properties,
+        event_time=event_time.replace(tzinfo=pytz.utc))
+
+    print(response)
 
 
 def import_all(access_key):
-  """This method import all SP500 stocks and some SPDR ETFs."""
-  time_slices = [
-      (datetime(1999, 1, 1), datetime(2004, 1, 1), datetime(2004, 1, 2)),
-      (datetime(2003, 12, 1), datetime(2009, 1, 1), datetime(2009, 1, 2)),
-      (datetime(2008, 12, 1), datetime(2014, 9, 1), datetime(2014, 9, 2)),
-      ]
+    """This method import all SP500 stocks and some SPDR ETFs."""
+    time_slices = [
+        (datetime(1999, 1, 1), datetime(2004, 1, 1), datetime(2004, 1, 2)),
+        (datetime(2003, 12, 1), datetime(2009, 1, 1), datetime(2009, 1, 2)),
+        (datetime(2008, 12, 1), datetime(2014, 9, 1), datetime(2014, 9, 2)),
+    ]
 
-  url = 'http://localhost:7070'
-  client = predictionio.EventClient(access_key=access_key, threads=1, url=url)
+    url = 'http://localhost:7070'
+    client = predictionio.EventClient(access_key=access_key, threads=1, url=url)
 
-  tickers = SP500_LIST + ETF_LIST 
+    tickers = SP500_LIST + ETF_LIST
 
-  for ticker in tickers:
-    for time_slice in time_slices:
-      import_data(client, access_key, ticker, 
-          time_slice[0], time_slice[1], time_slice[2])
+    for ticker in tickers:
+        for time_slice in time_slices:
+            import_data(client, access_key, ticker,
+                        time_slice[0], time_slice[1], time_slice[2])
 
 
 def import_data_with_gaps(access_key):
-  """This method import data with time gaps. 
-  
-  Data imported by this method is used by stock engine, it demonsrates how it
-  can handle time series data with gaps.
-  """ 
+    """This method import data with time gaps.
 
-  # time_slices is discontinuted
-  # startTime, endTime, eventDate
-  time_slices = [
-      (datetime(2013, 12, 1), datetime(2014, 2, 1), datetime(2014, 2, 2)),
-      (datetime(2014, 1, 1), datetime(2014, 1, 20), datetime(2014, 2, 10)),
-      (datetime(2014, 1, 10), datetime(2014, 2, 20), datetime(2014, 2, 28)),
-      (datetime(2014, 2, 10), datetime(2014, 3, 31), datetime(2014, 4, 2)),
-      (datetime(2014, 5, 1), datetime(2014, 6, 15), datetime(2014, 6, 20)),
-      (datetime(2014, 6, 1), datetime(2014, 7, 1), datetime(2014, 7, 15)),
-      ]
+    Data imported by this method is used by stock engine, it demonsrates how it
+    can handle time series data with gaps.
+    """
 
-  tickers = ['SPY', 'AAPL', 'IBM', 'MSFT']
- 
-  url = 'http://localhost:7070'
-  client = predictionio.EventClient(access_key=access_key, threads=1, url=url)
+    # time_slices is discontinuted
+    # startTime, endTime, eventDate
+    time_slices = [
+        (datetime(2013, 12, 1), datetime(2014, 2, 1), datetime(2014, 2, 2)),
+        (datetime(2014, 1, 1), datetime(2014, 1, 20), datetime(2014, 2, 10)),
+        (datetime(2014, 1, 10), datetime(2014, 2, 20), datetime(2014, 2, 28)),
+        (datetime(2014, 2, 10), datetime(2014, 3, 31), datetime(2014, 4, 2)),
+        (datetime(2014, 5, 1), datetime(2014, 6, 15), datetime(2014, 6, 20)),
+        (datetime(2014, 6, 1), datetime(2014, 7, 1), datetime(2014, 7, 15)),
+    ]
 
-  for ticker in tickers:
-    for time_slice in time_slices:
-      import_data(client, access_key, ticker, 
-          time_slice[0], time_slice[1], time_slice[2])
+    tickers = ['SPY', 'AAPL', 'IBM', 'MSFT']
 
-  # below are data with holes
-  time_slices = [
-      (datetime(2014, 1, 1), datetime(2014, 1, 20), datetime(2014, 2, 10)),
-      (datetime(2014, 2, 10), datetime(2014, 3, 31), datetime(2014, 4, 2)),
-      (datetime(2014, 6, 1), datetime(2014, 7, 1), datetime(2014, 7, 15)),
-      ]
+    url = 'http://localhost:7070'
+    client = predictionio.EventClient(access_key=access_key, threads=1, url=url)
 
-  tickers = ['AMZN']
-  for ticker in tickers:
-    for time_slice in time_slices:
-      import_data(client, access_key, ticker, 
-          time_slice[0], time_slice[1], time_slice[2])
+    for ticker in tickers:
+        for time_slice in time_slices:
+            import_data(client, access_key, ticker,
+                        time_slice[0], time_slice[1], time_slice[2])
 
-  time_slices = [
-      (datetime(2014, 1, 10), datetime(2014, 2, 20), datetime(2014, 2, 28)),
-      (datetime(2014, 2, 10), datetime(2014, 3, 31), datetime(2014, 4, 2)),
-      ]
-  tickers = ['FB']
-  for ticker in tickers:
-    for time_slice in time_slices:
-      import_data(client, access_key, ticker, 
-          time_slice[0], time_slice[1], time_slice[2])
+    # below are data with holes
+    time_slices = [
+        (datetime(2014, 1, 1), datetime(2014, 1, 20), datetime(2014, 2, 10)),
+        (datetime(2014, 2, 10), datetime(2014, 3, 31), datetime(2014, 4, 2)),
+        (datetime(2014, 6, 1), datetime(2014, 7, 1), datetime(2014, 7, 15)),
+    ]
+
+    tickers = ['AMZN']
+    for ticker in tickers:
+        for time_slice in time_slices:
+            import_data(client, access_key, ticker,
+                        time_slice[0], time_slice[1], time_slice[2])
+
+    time_slices = [
+        (datetime(2014, 1, 10), datetime(2014, 2, 20), datetime(2014, 2, 28)),
+        (datetime(2014, 2, 10), datetime(2014, 3, 31), datetime(2014, 4, 2)),
+    ]
+    tickers = ['FB']
+    for ticker in tickers:
+        for time_slice in time_slices:
+            import_data(client, access_key, ticker,
+                        time_slice[0], time_slice[1], time_slice[2])
 
 
 def import_one(access_key):
-  """Import TSLA.
-  
-  Import data with from 2014-01-01 until 2014-03-01. event_time specifies when
-  this data is extracted. 
-  """
-  start_time = datetime(2014, 1, 1)
-  end_time = datetime(2014, 3, 1)
-  event_time = datetime(2014, 9, 1)
-  ticker = 'TSLA'
- 
-  url = 'http://localhost:7070'
-  client = predictionio.EventClient(access_key=access_key, threads=1, url=url)
+    """Import TSLA.
 
-  import_data(client, access_key, ticker, start_time, end_time, event_time)
+    Import data with from 2014-01-01 until 2014-03-01. event_time specifies when
+    this data is extracted.
+    """
+    start_time = datetime(2014, 1, 1)
+    end_time = datetime(2014, 3, 1)
+    event_time = datetime(2014, 9, 1)
+    ticker = 'TSLA'
+
+    url = 'http://localhost:7070'
+    client = predictionio.EventClient(access_key=access_key, threads=1, url=url)
+
+    import_data(client, access_key, ticker, start_time, end_time, event_time)
 
 
 if __name__ == '__main__':
-  if len(sys.argv) < 2:
-    sys.exit("Usage: python -m examples.import_yahoo <access_key>")
+    if len(sys.argv) < 2:
+        sys.exit("Usage: python -m examples.import_yahoo <access_key>")
 
-  access_key = sys.argv[1]
-  import_all(access_key=access_key)
-  #import_data_with_gaps(access_key=access_key)
-  #import_one(access_key=access_key)
+    access_key = sys.argv[1]
+    import_all(access_key=access_key)
+    # import_data_with_gaps(access_key=access_key)
+    # import_one(access_key=access_key)
diff --git a/examples/itemrank_quick_query.py b/examples/itemrank_quick_query.py
index 3c43713..2beb29b 100644
--- a/examples/itemrank_quick_query.py
+++ b/examples/itemrank_quick_query.py
@@ -10,14 +10,14 @@
 item_ids = [str(i) for i in range(1, 6)]
 user_ids = [str(x) for x in range(1, 6)] + ["NOT_EXIST_USER"]
 for user_id in user_ids:
-  print "Rank item 1 to 5 for user", user_id
-  try:
-    response = client.send_query({
-      "uid": user_id,
-      "iids": item_ids
-    })
-    print response
-  except predictionio.PredictionIOAPIError as e:
-    print 'Caught exception:', e
+    print("Rank item 1 to 5 for user ", user_id)
+    try:
+        response = client.send_query({
+            "uid": user_id,
+            "iids": item_ids
+        })
+        print(response)
+    except predictionio.PredictionIOAPIError as e:
+        print("Caught exception: ", e)
 
 client.close()
diff --git a/examples/itemrank_quick_start.py b/examples/itemrank_quick_start.py
index 6333ea0..a231373 100644
--- a/examples/itemrank_quick_start.py
+++ b/examples/itemrank_quick_start.py
@@ -2,44 +2,47 @@
 itemrank quickstart import data
 """
 
-import predictionio
-
 import random
 import sys
 
+import predictionio
+
+
 def import_itemrank(access_key):
+    random.seed()
 
-  random.seed()
-  
-  client = predictionio.EventClient(access_key)
+    client = predictionio.EventClient(access_key)
 
-  print client.get_status()
-  
-  # generate 10 users, with user ids 1,2,....,10
-  user_ids = [str(i) for i in range(1, 11)]
-  for user_id in user_ids:
-    print "Set user", user_id
-    client.set_user(user_id)
-  
-  # generate 50 items, with item ids 1,2,....,50
-  # assign type id 1 to all of them
-  item_ids = [str(i) for i in range(1, 51)]
-  for item_id in item_ids:
-    print "Set item", item_id
-    client.set_item(item_id, {
-      "itypes" : ['1']
-    })
-  
-  # each user randomly views 10 items
-  for user_id in user_ids:
-    for viewed_item in random.sample(item_ids, 10):
-      print "User", user_id ,"views item", viewed_item
-      client.record_user_action_on_item("view", user_id, viewed_item)
-  
-  client.close()
+    print(client.get_status())
+
+    # generate 10 users, with user ids 1,2,....,10
+    user_ids = [str(i) for i in range(1, 11)]
+    for user_id in user_ids:
+        print
+        "Set user", user_id
+        client.set_user(user_id)
+
+    # generate 50 items, with item ids 1,2,....,50
+    # assign type id 1 to all of them
+    item_ids = [str(i) for i in range(1, 51)]
+    for item_id in item_ids:
+        print
+        "Set item", item_id
+        client.set_item(item_id, {
+            "itypes": ['1']
+        })
+
+    # each user randomly views 10 items
+    for user_id in user_ids:
+        for viewed_item in random.sample(item_ids, 10):
+            print
+            "User", user_id, "views item", viewed_item
+            client.record_user_action_on_item("view", user_id, viewed_item)
+
+    client.close()
 
 
 if __name__ == '__main__':
-  if len(sys.argv) < 2:
-    sys.exit("Usage: python -m examples.itemrank_quick_start <access_key>")
-  import_itemrank(sys.argv[1])
+    if len(sys.argv) < 2:
+        sys.exit("Usage: python -m examples.itemrank_quick_start <access_key>")
+    import_itemrank(sys.argv[1])
diff --git a/examples/obsolete/itemrec/__init__.py b/examples/obsolete/itemrec/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/examples/obsolete/itemrec/__init__.py
+++ /dev/null
diff --git a/examples/obsolete/itemrec/movies/.gitignore b/examples/obsolete/itemrec/movies/.gitignore
deleted file mode 100644
index 7b1ef3e..0000000
--- a/examples/obsolete/itemrec/movies/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-test/
-ml-100k/
-ml-100k.zip
diff --git a/examples/obsolete/itemrec/movies/README.md b/examples/obsolete/itemrec/movies/README.md
deleted file mode 100644
index 9ef357a..0000000
--- a/examples/obsolete/itemrec/movies/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-PredictionIO Python SDK Example
-===============================
-
-Please execute all commands from repository root.
-
-Step 1. Get sample data and unzip it.
-
-    > wget http://www.grouplens.org/system/files/ml-100k.zip
-    > unzip ml-100k.zip
-
-Step 2. Configurate examples/itemrec/movies/appdata.py
-
-Step 3. Run this app:
-
-    > python -m examples.itemrec.movies.movie_rec_app
diff --git a/examples/obsolete/itemrec/movies/__init__.py b/examples/obsolete/itemrec/movies/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/examples/obsolete/itemrec/movies/__init__.py
+++ /dev/null
diff --git a/examples/obsolete/itemrec/movies/app_config.py b/examples/obsolete/itemrec/movies/app_config.py
deleted file mode 100644
index 5efa7a0..0000000
--- a/examples/obsolete/itemrec/movies/app_config.py
+++ /dev/null
@@ -1,2 +0,0 @@
-APP_KEY = 'uJKTKyUAFNZYQQO5yxkdrSo3XIlaf9LXejI63CWE0mtZVEYF89hyVtOwpMKfXXXX'
-API_URL = 'http://localhost:8000'
diff --git a/examples/obsolete/itemrec/movies/appdata.py b/examples/obsolete/itemrec/movies/appdata.py
deleted file mode 100644
index fb3c7fd..0000000
--- a/examples/obsolete/itemrec/movies/appdata.py
+++ /dev/null
@@ -1,148 +0,0 @@
-
-import datetime
-from operator import itemgetter, attrgetter
-
-# can get sample data here:
-# wget http://www.grouplens.org/system/files/ml-100k.zip
-# app data file config
-APPDATA_DIRNAME = "ml-100k"
-USERS_FILENAME = "u.user"
-USERS_FILE_DELIMITER = "|"
-ITEMS_FILENAME = "u.item"
-ITEMS_FILE_DELIMITER = "|"
-RATE_ACTIONS_FILENAME = "u.data"
-RATE_ACTIONS_DELIMITER = "\t"
-
-
-class User:
-  def __init__(self, uid):
-    self.uid = uid
-    self.rec = [] # recommendations, list of iid
-
-  def __str__(self):
-    return "User[uid=%s,rec=%s]" % (self.uid, self.rec)
-
-class Item:
-  def __init__(self, iid, name):
-    self.iid = iid
-    self.name = name
-
-  def __str__(self):
-    return "Item[iid=%s,name=%s]" % (self.iid, self.name)
-
-class RateAction:
-  def __init__(self, uid, iid, rating, t):
-    self.uid = uid
-    self.iid = iid
-    self.rating = rating
-    self.t = t
-
-  def __str__(self):
-    return "RateAction[uid=%s,iid=%s,rating=%s,t=%s]" % (self.uid, self.iid, self.rating, self.t)
-
-
-class AppData:
-
-  def __init__(self):
-    self._users = {} # dict of User obj
-    self._items = {} # dict of Item obj
-    self._rate_actions = [] # list of RateAction obj
-
-    self._users_file = "%s/%s" % (APPDATA_DIRNAME, USERS_FILENAME)
-    self._items_file = "%s/%s" % (APPDATA_DIRNAME, ITEMS_FILENAME)
-    self._rate_actions_file = "%s/%s" % (APPDATA_DIRNAME, RATE_ACTIONS_FILENAME)
-    self.__init_users()
-    self.__init_items()
-    self.__init_rate_actions()
-
-  def __init_users(self):
-    """
-    uid|
-    """
-    print "[Info] Initializing users..."
-    f = open(self._users_file, 'r')
-    for line in f:
-      data = line.rstrip('\r\n').split(USERS_FILE_DELIMITER)
-      self.add_user(User(data[0]))
-    f.close()
-    print "[Info] %s users were initialized." % len(self._users)
-
-  def __init_items(self):
-    """
-    iid|name
-    """
-    print "[Info] Initializing items..."
-    f = open(self._items_file, 'r')
-    for line in f:
-      data = line.rstrip('\r\n').split(ITEMS_FILE_DELIMITER)
-      self.add_item(Item(data[0], data[1]))
-    f.close()
-    print "[Info] %s items were initialized." % len(self._items)
-
-  def __init_rate_actions(self):
-    """
-    uid|iid|rating|timestamp
-    """
-    print "[Info] Initializing rate actions..."
-    f = open(self._rate_actions_file, 'r')
-    for line in f:
-      data = line.rstrip('\r\n').split(RATE_ACTIONS_DELIMITER)
-      t = datetime.datetime.utcfromtimestamp(int(data[3])).isoformat()
-      self.add_rate_action(RateAction(data[0], data[1], data[2], t))
-    f.close()
-    print "[Info] %s rate actions were initialized." % len(self._rate_actions)
-
-  def add_user(self, user):
-    self._users[user.uid] = user
-
-  def add_item(self, item):
-    self._items[item.iid] = item
-
-  def add_rate_action(self, action):
-    self._rate_actions.append(action)
-
-  def get_users(self):
-    return self._users
-
-  def get_items(self):
-    return self._items
-
-  def get_rate_actions(self):
-    return self._rate_actions
-
-  def get_user(self, uid):
-    """return single user
-    """
-    if uid in self._users:
-      return self._users[uid]
-    else:
-      return None
-
-  def get_item(self, iid):
-    """return single item
-    """
-    if iid in self._items:
-      return self._items[iid]
-    else:
-      return None
-
-  def get_top_rated_items(self, uid, n):
-    """get top n rated iids by this uid
-    """
-    if uid in self._users:
-      actions = filter(lambda u: u.uid==uid, self._rate_actions)
-      top = sorted(actions, key=attrgetter('rating'), reverse=True)
-      topn_iids = map(lambda a: a.iid, top[:n])
-      return topn_iids
-    else:
-      return None
-
-  def get_top_rate_actions(self, uid, n):
-    """get top n rated actions by this uid
-    """
-    if uid in self._users:
-      actions = filter(lambda u: u.uid==uid, self._rate_actions)
-      top = sorted(actions, key=attrgetter('rating'), reverse=True)
-      return top[:n]
-    else:
-      return None
diff --git a/examples/obsolete/itemrec/movies/batch_import.py b/examples/obsolete/itemrec/movies/batch_import.py
deleted file mode 100644
index b2e0e63..0000000
--- a/examples/obsolete/itemrec/movies/batch_import.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from appdata import AppData
-import predictionio
-import sys
-
-from app_config import APP_KEY, API_URL
-
-def batch_import_task(app_data, client, all_info=False):
-
-  print "[Info] Importing users to PredictionIO..."
-  count = 0 
-  for k, v in app_data.get_users().iteritems():
-    count += 1
-    if all_info:
-      print "[Info] Importing %s..." % v
-    else:
-      if (count % 32 == 0):
-        sys.stdout.write('\r[Info] %s' % count)
-        sys.stdout.flush()
-
-    client.create_user(v.uid)
-  
-  sys.stdout.write('\r[Info] %s users were imported.\n' % count)
-  sys.stdout.flush()
-
-  print "[Info] Importing items to PredictionIO..."
-  count = 0
-  for k, v in app_data.get_items().iteritems():
-    count += 1
-    if all_info:
-      print "[Info] Importing %s..." % v
-    else:
-      if (count % 32 == 0):
-        sys.stdout.write('\r[Info] %s' % count)
-        sys.stdout.flush()
-
-    client.create_item(v.iid, ("movie",))
-  
-  sys.stdout.write('\r[Info] %s items were imported.\n' % count)
-  sys.stdout.flush()
-
-  print "[Info] Importing rate actions to PredictionIO..."
-  count = 0
-  for v in app_data.get_rate_actions():
-    count += 1
-    if all_info:
-      print "[Info] Importing %s..." % v
-    else:
-      if (count % 32 == 0):
-        sys.stdout.write('\r[Info] %s' % count)
-        sys.stdout.flush()
-
-    client.identify(v.uid)
-    client.record_action_on_item("rate", v.iid, { "pio_rate": v.rating, "pio_t": v.t })
-
-  sys.stdout.write('\r[Info] %s rate actions were imported.\n' % count)
-  sys.stdout.flush()
-
-
-if __name__ == '__main__':
-
-  app_data = AppData()
-  client = predictionio.Client(APP_KEY, 1, API_URL)
-  batch_import_task(app_data, client)
-  client.close()
-
diff --git a/examples/obsolete/itemrec/movies/movie_rec_app.py b/examples/obsolete/itemrec/movies/movie_rec_app.py
deleted file mode 100644
index 436af38..0000000
--- a/examples/obsolete/itemrec/movies/movie_rec_app.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# To run this example app
-#
-# Please execute all commands from repository root.
-#
-# Step 1. Get sample data and unzip it.
-# > wget http://www.grouplens.org/system/files/ml-100k.zip
-# > unzip ml-100k.zip
-#
-# Step 2. Configurate examples/itemrec/movies/appdata.py
-#
-# Step 3. Run this app:
-# python -m examples.itemrec.movies.movie_rec_app
-
-from appdata import AppData
-import predictionio
-import sys
-
-from app_config import APP_KEY, API_URL
-
-ENGINE_NAME = 'movie-rec'
-
-class App:
-
-  def __init__(self):
-    self._app_data = AppData()
-    self._client = predictionio.Client(APP_KEY, 1, API_URL)
-
-  def run(self):
-    state = "[Main Menu]"
-
-    prompt = "\n"\
-      "%s\n"\
-      "%s\n"\
-      "Please input selection:\n"\
-      " 0: Quit application.\n"\
-      " 1: Get Recommendations from PredictionIO.\n"\
-      " 2: Display user's data." % (state, '-'*len(state))
-
-    while True:
-      print prompt
-      choice = raw_input().lower()
-      if choice == '0':
-        print "\nGood Bye!\n"
-        break
-      elif choice == '1':
-        self.recommend_task(state)
-      elif choice == '2':
-        self.display_user_task(state)
-      else:
-        print '[Error] \'%s\' is not a valid selection.' % choice
-
-    self._client.close()
-
-  def recommend_task(self, prev_state):
-    state = prev_state + " / [Get Recommendations]"
-    prompt = "\n"\
-      "%s\n"\
-      "%s\n"\
-      "Please enter user id:" % (state, '-'*len(state))
-
-    while True:
-      print prompt
-      choice = raw_input().lower()
-      u = self._app_data.get_user(choice)
-      if u:
-        n = 10
-        print "[Info] Getting top %s item recommendations for user %s..." % (n, u.uid)
-        try:
-          self._client.identify(u.uid)
-          rec = self._client.get_itemrec_topn(ENGINE_NAME, n)
-          u.rec = rec['pio_iids']
-          self.display_items(u.rec)
-        except predictionio.ItemRecNotFoundError:
-          print "[Info] Recommendation not found"
-
-        print "[Info] Go back to previous menu..."
-        break
-      else:
-        print "[Error] invalid user id %s. Go back to previous menu..." % choice
-        break
-
-  def display_user_task(self, prev_state):
-    state = prev_state + " / [Display User]"
-    prompt = "\n"\
-      "%s\n"\
-      "%s\n"\
-      "Please enter user id:" % (state, '-'*len(state))
-
-    while True:
-      print prompt
-      choice = raw_input().lower()
-      u = self._app_data.get_user(choice)
-      if u:
-        print "[Info] User %s:" % u.uid
-        n = 10
-        topn_rate_actions = self._app_data.get_top_rate_actions(u.uid, n)
-        print "\n[Info] Top %s movies rated by this user:" % n
-        self.display_rate_actions(topn_rate_actions)
-
-        print "\n[Info] Movies recommended to this user:"
-        self.display_items(u.rec)
-
-        self.wait_for_ack()
-        print "\n[Info] Go back to previous menu..."
-        break
-      else:
-        print "[Error] invalid user id %s. Go back to previous menu..." % choice
-        break
-  
-  def display_items(self, iids, all_info=False):
-    """print item info for each iid in the list
-    """
-    if iids:
-      for iid in iids:
-        item = self._app_data.get_item(iid)
-        if item:
-          if all_info:
-            print "[Info] %s" % item
-          else:
-            print "[Info] %s" % item.name
-        else:
-          print "[Error] Invalid item id %s" % iid
-    else:
-      print "[Info] Empty."
-
-  def display_rate_actions(self, actions):
-    """print iid and rating
-    """
-    if actions:
-      for a in actions:
-        item = self._app_data.get_item(a.iid)
-        if item:
-          print "[Info] %s, rating = %s" % (item.name, a.rating)
-        else:
-          print "[Error] Invalid item id %s" % a.iid
-    else:
-      print "[Info] Empty."
-
-  def wait_for_ack(self):
-
-    prompt = "\nPress enter to continue..."
-    print prompt
-    choice = raw_input().lower()
-
-
-if __name__ == '__main__':
-
-  print "\nWelcome To PredictionIO Python-SDK Demo App!"
-  print "============================================\n"
-  
-  my_app = App()
-  my_app.run()
diff --git a/predictionio/__init__.py b/predictionio/__init__.py
index 290c31e..e5e5387 100644
--- a/predictionio/__init__.py
+++ b/predictionio/__init__.py
@@ -1,11 +1,10 @@
-"""PredictoinIO Python SDK
-
-The PredictoinIO Python SDK provides easy-to-use functions for integrating
+"""PredictionIO Python SDK
+The PredictionIO Python SDK provides easy-to-use functions for integrating
 Python applications with PredictionIO REST API services.
 """
 
 
-__version__ = "0.9.2"
+__version__ = "0.9.6"
 
 # import deprecated libraries.
 from predictionio.obsolete import Client, InvalidArgumentError
@@ -64,7 +63,6 @@
 class BaseClient(object):
   def __init__(self, url, threads=1, qsize=0, timeout=5):
     """Constructor of Client object.
-
     """
     self.threads = threads
     self.url = url
@@ -90,7 +88,6 @@
 
   def close(self):
     """Close this client and the connection.
-
     Call this method when you want to completely terminate the connection
     with PredictionIO.
     It will wait for all pending requests to finish.
@@ -99,7 +96,6 @@
 
   def pending_requests(self):
     """Return the number of pending requests.
-
     :returns:
       The number of pending requests of this client.
     """
@@ -107,10 +103,8 @@
 
   def get_status(self):
     """Get the status of the PredictionIO API Server
-
     :returns:
       status message.
-
     :raises:
       ServerStatusError.
     """
@@ -157,10 +151,8 @@
 
 class EventClient(BaseClient):
   """Client for importing data into PredictionIO Event Server.
-
   Notice that app_id has been deprecated as of 0.8.2. Please use access_token
   instead.
-
   :param access_key: the access key for your application.
   :param url: the url of PredictionIO Event Server.
   :param threads: number of threads to handle PredictionIO API requests.
@@ -197,7 +189,6 @@
       target_entity_type=None, target_entity_id=None, properties=None,
       event_time=None):
     """Asynchronously create an event.
-
     :param event: event name. type str.
     :param entity_type: entity type. It is the namespace of the entityId and
       analogous to the table name of a relational database. The entityId must be
@@ -211,7 +202,6 @@
     :param properties: a custom dict associated with an event. type dict.
     :param event_time: the time of the event. type datetime, must contain
       timezone info.
-
     :returns:
       AsyncRequest object. You can call the get_response() method using this
       object to get the final resuls or status of this asynchronous request.
@@ -261,10 +251,8 @@
 
   def aget_event(self, event_id):
     """Asynchronouly get an event from Event Server.
-
     :param event_id: event id returned by the EventServer when creating the
       event.
-
     :returns:
       AsyncRequest object.
     """
@@ -288,14 +276,12 @@
 
   def aget_events(self, startTime=None, untilTime=None, entityType=None, entityId=None, limit=None, reversed=False):
     """Asynchronouly get events from Event Server. (Getting events through the Event Server API is used for debugging and not recommended for production)
-
     :param startTime: time in ISO8601 format. Return events with eventTime >= startTime.
     :param untilTime: time in ISO8601 format. Return events with eventTime < untilTime.
     :param entityId: String. The entityId. Return events for this entityId only.
     :param limit: Integer. The number of record events returned. Default is 20. -1 to get all.
     :param reversed: Boolean. Must be used with both entityType and entityId specified,
       returns events in reversed chronological order. Default is false.
-
     :returns:
       AsyncRequest object.
     """
@@ -340,10 +326,8 @@
 
   def adelete_event(self, event_id):
     """Asynchronouly delete an event from Event Server.
-
     :param event_id: event id returned by the EventServer when creating the
       event.
-
     :returns:
       AsyncRequest object.
     """
@@ -369,7 +353,6 @@
 
   def aset_user(self, uid, properties={}, event_time=None):
     """Set properties of a user.
-
     Wrapper of acreate_event function, setting event to "$set" and entity_type
     to "user".
     """
@@ -387,7 +370,6 @@
 
   def aunset_user(self, uid, properties, event_time=None):
     """Unset properties of an user.
-
     Wrapper of acreate_event function, setting event to "$unset" and entity_type
     to "user".
     """
@@ -406,7 +388,6 @@
 
   def adelete_user(self, uid, event_time=None):
     """Delete a user.
-
     Wrapper of acreate_event function, setting event to "$delete" and entity_type
     to "user".
     """
@@ -422,7 +403,6 @@
 
   def aset_item(self, iid, properties={}, event_time=None):
     """Set properties of an item.
-
     Wrapper of acreate_event function, setting event to "$set" and entity_type
     to "item".
     """
@@ -439,7 +419,6 @@
 
   def aunset_item(self, iid, properties={}, event_time=None):
     """Unset properties of an item.
-
     Wrapper of acreate_event function, setting event to "$unset" and entity_type
     to "item".
     """
@@ -456,7 +435,6 @@
 
   def adelete_item(self, iid, event_time=None):
     """Delete an item.
-
     Wrapper of acreate_event function, setting event to "$delete" and entity_type
     to "item".
     """
@@ -473,7 +451,6 @@
   def arecord_user_action_on_item(self, action, uid, iid, properties={},
       event_time=None):
     """Create a user-to-item action.
-
     Wrapper of acreate_event function, setting entity_type to "user" and
     target_entity_type to "item".
     """
@@ -496,7 +473,6 @@
 class EngineClient(BaseClient):
   """Client for extracting prediction results from an PredictionIO Engine
   Instance.
-
   :param url: the url of the PredictionIO Engine Instance.
   :param threads: number of threads to handle PredictionIO API requests.
           Must be >= 1.
@@ -507,7 +483,6 @@
   :param timeout: timeout for HTTP connection attempts and requests in
     seconds (optional).
     Default value is 5.
-
   """
   def __init__(self, url="http://localhost:8000", threads=1,
       qsize=0, timeout=5):
@@ -516,10 +491,8 @@
   def asend_query(self, data):
     """Asynchronously send a request to the engine instance with data as the
     query.
-
     :param data: the query: It is coverted to an json object using json.dumps
       method. type dict.
-
     :returns:
       AsyncRequest object. You can call the get_response() method using this
       object to get the final resuls or status of this asynchronous request.
@@ -532,22 +505,18 @@
 
   def send_query(self, data):
     """Synchronously send a request.
-
     :param data: the query: It is coverted to an json object using json.dumps
       method. type dict.
-
     :returns: the prediction.
     """
     return self.asend_query(data).get_response()
 
 class FileExporter(object):
   """File exporter to write events to JSON file for batch import
-
   :param file_name: the destination file name
   """
   def __init__(self, file_name):
     """Constructor of Exporter.
-
     """
     self._file = open(file_name, 'w')
 
@@ -555,9 +524,7 @@
       target_entity_type=None, target_entity_id=None, properties=None,
       event_time=None):
     """Create an event and write to the file.
-
     (please refer to EventClient's create_event())
-
     """
     data = {
         "event": event,
@@ -585,7 +552,6 @@
 
   def close(self):
     """Close the FileExporter
-
     Call this method when you finish writing all events to JSON file
     """
     self._file.close()
diff --git a/predictionio/connection.py b/predictionio/connection.py
index 04fe7b5..8205bb2 100644
--- a/predictionio/connection.py
+++ b/predictionio/connection.py
@@ -1,23 +1,22 @@
-
 try:
-  import Queue
+    import Queue
 except ImportError:
-  # pylint: disable=F0401
-  # http is a Python3 module, replacing httplib. Ditto.
-  import queue as Queue
+    # pylint: disable=F0401
+    # http is a Python3 module, replacing httplib. Ditto.
+    import queue as Queue
 import threading
 
 try:
-  import httplib
+    import httplib
 except ImportError:
-  # pylint: disable=F0401
-  from http import client as httplib
+    # pylint: disable=F0401
+    from http import client as httplib
 
 try:
-  from urllib import urlencode
+    from urllib import urlencode
 except ImportError:
-  # pylint: disable=F0401,E0611
-  from urllib.parse import urlencode
+    # pylint: disable=F0401,E0611
+    from urllib.parse import urlencode
 
 import datetime
 import json
@@ -25,9 +24,9 @@
 
 # use generators for python2 and python3
 try:
-  xrange
+    xrange
 except NameError:
-  xrange = range
+    xrange = range
 
 # some constants
 MAX_RETRY = 1  # 0 means no retry
@@ -39,81 +38,124 @@
 
 
 def enable_log(filename=None):
-  global logger
-  global DEBUG_LOG
-  timestamp = datetime.datetime.today()
-  if not filename:
-    logfile = "./log/predictionio_%s.log" % timestamp.strftime(
-      "%Y-%m-%d_%H:%M:%S.%f")
-  else:
-    logfile = filename
-  logging.basicConfig(filename=logfile,
-            filemode='w',
-            level=logging.DEBUG,
-            format='[%(levelname)s] %(name)s (%(threadName)s) %(message)s')
-  logger = logging.getLogger(__name__)
-  DEBUG_LOG = True
+    global logger
+    global DEBUG_LOG
+    timestamp = datetime.datetime.today()
+    if not filename:
+        logfile = "./log/predictionio_%s.log" % timestamp.strftime(
+            "%Y-%m-%d_%H:%M:%S.%f")
+    else:
+        logfile = filename
+    logging.basicConfig(filename=logfile,
+                        filemode='w',
+                        level=logging.DEBUG,
+                        format='[%(levelname)s] %(name)s (%(threadName)s) %(message)s')
+    logger = logging.getLogger(__name__)
+    DEBUG_LOG = True
 
 
 class PredictionIOAPIError(Exception):
-  pass
+    pass
 
 
 class NotSupportMethodError(PredictionIOAPIError):
-  pass
+    pass
 
 
 class ProgramError(PredictionIOAPIError):
-  pass
+    pass
 
 
 class AsyncRequest(object):
+    """AsyncRequest object
 
-  """AsyncRequest object
-
-  """
-
-  def __init__(self, method, path, **params):
-    self.method = method  # "GET" "POST" etc
-    # the sub path eg. POST /v1/users.json  GET /v1/users/1.json
-    self.path = path
-    # dictionary format eg. {"appkey" : 123, "id" : 3}
-    self.params = params
-    # use queue to implement response, store AsyncResponse object
-    self.response_q = Queue.Queue(1)
-    self.qpath = "%s?%s" % (self.path, urlencode(self.params))
-    self._response = None
-    # response function to be called to handle the response
-    self.rfunc = None
-
-  def __str__(self):
-    return "%s %s %s %s" % (self.method, self.path, self.params,
-                self.qpath)
-
-  def set_rfunc(self, func):
-    self.rfunc = func
-
-  def set_response(self, response):
-    """ store the response
-
-    NOTE: Must be only called once
     """
-    self.response_q.put(response)
 
-  def get_response(self):
-    """Get the response. Blocking.
+    def __init__(self, method, path, **params):
+        self.method = method  # "GET" "POST" etc
+        # the sub path eg. POST /v1/users.json  GET /v1/users/1.json
+        self.path = path
+        # dictionary format eg. {"appkey" : 123, "id" : 3}
+        self.params = params
+        # use queue to implement response, store AsyncResponse object
+        self.response_q = Queue.Queue(1)
+        self.qpath = "%s?%s" % (self.path, urlencode(self.params))
+        self._response = None
+        # response function to be called to handle the response
+        self.rfunc = None
 
-    :returns: self.rfunc's return type.
+    def __str__(self):
+        return "%s %s %s %s" % (self.method, self.path, self.params,
+                                self.qpath)
+
+    def set_rfunc(self, func):
+        self.rfunc = func
+
+    def set_response(self, response):
+        """ store the response
+
+        NOTE: Must be only called once
+        """
+        self.response_q.put(response)
+
+    def get_response(self):
+        """Get the response. Blocking.
+
+        :returns: self.rfunc's return type.
+        """
+        if self._response is None:
+            tmp_response = self.response_q.get(True)  # NOTE: blocking
+            if self.rfunc is None:
+                self._response = tmp_response
+            else:
+                self._response = self.rfunc(tmp_response)
+
+        return self._response
+
+
+class AsyncResponse(object):
+    """Store the response of asynchronous request
+
+    When get the response, user should check if error is None (which means no
+    Exception happens).
+    If error is None, then should check if the status is expected.
     """
-    if self._response is None:
-      tmp_response = self.response_q.get(True)  # NOTE: blocking
-      if self.rfunc is None:
-        self._response = tmp_response
-      else:
-        self._response = self.rfunc(tmp_response)
 
-    return self._response
+    def __init__(self):
+        #: exception object if any happens
+        self.error = None
 
+        self.version = None
+        self.status = None
+        self.reason = None
+        #: Response header. str
+        self.headers = None
+        #: Response body. str
+        self.body = None
+        #: Jsonified response body. Remains None if conversion is unsuccessful.
+        self.json_body = None
+        #: Point back to the AsyncRequest object
+        self.request = None
+
+    def __str__(self):
+        return "e:%s v:%s s:%s r:%s h:%s b:%s" % (self.error, self.version,
+                                                  self.status, self.reason,
+                                                  self.headers, self.body)
+
+    def set_resp(self, version, status, reason, headers, body):
+        self.version = version
+        self.status = status
+        self.reason = reason
+        self.headers = headers
+        self.body = body
+        # Try to extract the json.
+        try:
+            self.json_body = json.loads(body)
+        except ValueError as ex:  # noqa
+            self.json_body = None
+
+    def set_error(self, error):
+        self.error = error
 
 class AsyncResponse(object):
   """Store the response of asynchronous request
@@ -162,209 +204,206 @@
   def set_request(self, request):
     self.request = request
 
-
 class PredictionIOHttpConnection(object):
+    def __init__(self, host, https=True, timeout=5):
+        if https:  # https connection
+            self._connection = httplib.HTTPSConnection(host, timeout=timeout)
+        else:
+            self._connection = httplib.HTTPConnection(host, timeout=timeout)
 
-  def __init__(self, host, https=True, timeout=5):
-    if https:  # https connection
-      self._connection = httplib.HTTPSConnection(host, timeout=timeout)
-    else:
-      self._connection = httplib.HTTPConnection(host, timeout=timeout)
+    def connect(self):
+        self._connection.connect()
 
-  def connect(self):
-    self._connection.connect()
-
-  def close(self):
-    self._connection.close()
-
-  def request(self, method, url, body={}, headers={}):
-    """
-    http request wrapper function, with retry capability in case of error.
-    catch error exception and store it in AsyncResponse object
-    return AsyncResponse object
-
-    Args:
-      method: http method, type str
-      url: url path, type str
-      body: http request body content, type dict
-      header: http request header , type dict
-    """
-
-    response = AsyncResponse()
-
-    try:
-      # number of retry in case of error (minimum 0 means no retry)
-      retry_limit = MAX_RETRY
-      mod_headers = dict(headers)  # copy the headers
-      mod_headers["Connection"] = "keep-alive"
-      enc_body = None
-      if body:  # if body is not empty
-        #enc_body = urlencode(body)
-        #mod_headers[
-        #  "Content-type"] = "application/x-www-form-urlencoded"
-        enc_body = json.dumps(body)
-        mod_headers[
-          "Content-type"] = "application/json"
-        #mod_headers["Accept"] = "text/plain"
-    except Exception as e:
-      response.set_error(e)
-      return response
-
-    if DEBUG_LOG:
-      logger.debug("Request m:%s u:%s h:%s b:%s", method, url,
-             mod_headers, enc_body)
-    # retry loop
-    for i in xrange(retry_limit + 1):
-      try:
-        if i != 0:
-          if DEBUG_LOG:
-            logger.debug("retry request %s times" % i)
-        if self._connection.sock is None:
-          self._connection.connect()
-        self._connection.request(method, url, enc_body, mod_headers)
-      except Exception as e:
+    def close(self):
         self._connection.close()
-        if i == retry_limit:
-          # new copy of e created everytime??
-          response.set_error(e)
-      else:  # NOTE: this is try's else clause
-        # connect() and request() OK
+
+    def request(self, method, url, body={}, headers={}):
+        """
+        http request wrapper function, with retry capability in case of error.
+        catch error exception and store it in AsyncResponse object
+        return AsyncResponse object
+
+        Args:
+          method: http method, type str
+          url: url path, type str
+          body: http request body content, type dict
+          header: http request header , type dict
+        """
+
+        response = AsyncResponse()
+
         try:
-          resp = self._connection.getresponse()
+            # number of retry in case of error (minimum 0 means no retry)
+            retry_limit = MAX_RETRY
+            mod_headers = dict(headers)  # copy the headers
+            mod_headers["Connection"] = "keep-alive"
+            enc_body = None
+            if body:  # if body is not empty
+                # enc_body = urlencode(body)
+                # mod_headers[
+                # "Content-type"] = "application/x-www-form-urlencoded"
+                enc_body = json.dumps(body)
+                mod_headers[
+                    "Content-type"] = "application/json"
+                # mod_headers["Accept"] = "text/plain"
         except Exception as e:
-          self._connection.close()
-          if i == retry_limit:
             response.set_error(e)
-        else:  # NOTE: this is try's else clause
-          # getresponse() OK
-          resp_version = resp.version  # int
-          resp_status = resp.status  # int
-          resp_reason = resp.reason  # str
-          # resp.getheaders() returns list of tuples
-          # converted to dict format
-          resp_headers = dict(resp.getheaders())
-          # NOTE: have to read the response before sending out next
-          # http request
-          resp_body = resp.read()  # str
-          response.set_resp(version=resp_version, status=resp_status,
-                    reason=resp_reason, headers=resp_headers,
-                    body=resp_body)
-          break  # exit retry loop
-    # end of retry loop
-    if DEBUG_LOG:
-      logger.debug("Response %s", response)
-    return response  # AsyncResponse object
+            return response
+
+        if DEBUG_LOG:
+            logger.debug("Request m:%s u:%s h:%s b:%s", method, url,
+                         mod_headers, enc_body)
+        # retry loop
+        for i in xrange(retry_limit + 1):
+            try:
+                if i != 0:
+                    if DEBUG_LOG:
+                        logger.debug("retry request %s times" % i)
+                if self._connection.sock is None:
+                    self._connection.connect()
+                self._connection.request(method, url, enc_body, mod_headers)
+            except Exception as e:
+                self._connection.close()
+                if i == retry_limit:
+                    # new copy of e created everytime??
+                    response.set_error(e)
+            else:  # NOTE: this is try's else clause
+                # connect() and request() OK
+                try:
+                    resp = self._connection.getresponse()
+                except Exception as e:
+                    self._connection.close()
+                    if i == retry_limit:
+                        response.set_error(e)
+                else:  # NOTE: this is try's else clause
+                    # getresponse() OK
+                    resp_version = resp.version  # int
+                    resp_status = resp.status  # int
+                    resp_reason = resp.reason  # str
+                    # resp.getheaders() returns list of tuples
+                    # converted to dict format
+                    resp_headers = dict(resp.getheaders())
+                    # NOTE: have to read the response before sending out next
+                    # http request
+                    resp_body = resp.read()  # str
+                    response.set_resp(version=resp_version, status=resp_status,
+                                      reason=resp_reason, headers=resp_headers,
+                                      body=resp_body)
+                    break  # exit retry loop
+        # end of retry loop
+        if DEBUG_LOG:
+            logger.debug("Response %s", response)
+        return response  # AsyncResponse object
 
 
 def connection_worker(host, request_queue, https=True, timeout=5, loop=True):
-  """worker function which establishes connection and wait for request jobs
-  from the request_queue
+    """worker function which establishes connection and wait for request jobs
+    from the request_queue
 
-  Args:
-    request_queue: the request queue storing the AsyncRequest object
-      valid requests:
-        GET
-        POST
-        DELETE
-        KILL
-    https: HTTPS (True) or HTTP (False)
-    timeout: timeout for HTTP connection attempts and requests in seconds
-    loop: This worker function stays in a loop waiting for request
-      For testing purpose only. should always be set to True.
-  """
+    Args:
+      request_queue: the request queue storing the AsyncRequest object
+        valid requests:
+          GET
+          POST
+          DELETE
+          KILL
+      https: HTTPS (True) or HTTP (False)
+      timeout: timeout for HTTP connection attempts and requests in seconds
+      loop: This worker function stays in a loop waiting for request
+        For testing purpose only. should always be set to True.
+    """
 
-  connect = PredictionIOHttpConnection(host, https, timeout)
+    connect = PredictionIOHttpConnection(host, https, timeout)
 
-  # loop waiting for job form request queue
-  killed = not loop
+    # loop waiting for job form request queue
+    killed = not loop
 
-  while True:
-    # print "thread %s waiting for request" % thread.get_ident()
-    request = request_queue.get(True)  # NOTE: blocking get
-    # print "get request %s" % request
-    method = request.method
-    if method == "GET":
-      path = request.qpath
-      d = connect.request("GET", path)
-    elif method == "POST":
-      path = request.path
-      body = request.params
-      d = connect.request("POST", path, body)
-    elif method == "DELETE":
-      path = request.qpath
-      d = connect.request("DELETE", path)
-    elif method == "KILL":
-      # tell the thread to kill the connection
-      killed = True
-      d = AsyncResponse()
-    else:
-      d = AsyncResponse()
-      d.set_error(NotSupportMethodError(
-        "Don't Support the method %s" % method))
+    while True:
+        # print "thread %s waiting for request" % thread.get_ident()
+        request = request_queue.get(True)  # NOTE: blocking get
+        # print "get request %s" % request
+        method = request.method
+        if method == "GET":
+            path = request.qpath
+            d = connect.request("GET", path)
+        elif method == "POST":
+            path = request.path
+            body = request.params
+            d = connect.request("POST", path, body)
+        elif method == "DELETE":
+            path = request.qpath
+            d = connect.request("DELETE", path)
+        elif method == "KILL":
+            # tell the thread to kill the connection
+            killed = True
+            d = AsyncResponse()
+        else:
+            d = AsyncResponse()
+            d.set_error(NotSupportMethodError(
+                "Don't Support the method %s" % method))
 
-    d.set_request(request)
-    request.set_response(d)
-    request_queue.task_done()
-    if killed:
-      break
+        d.set_request(request)
+        request.set_response(d)
+        request_queue.task_done()
+        if killed:
+            break
 
-  # end of while loop
-  connect.close()
+    # end of while loop
+    connect.close()
 
 
 class Connection(object):
+    """abstract object for connection with server
 
-  """abstract object for connection with server
-
-  spawn multiple connection_worker threads to handle jobs in the queue q
-  """
-
-  def __init__(self, host, threads=1, qsize=0, https=True, timeout=5):
-    """constructor
-
-    Args:
-      host: host of the server.
-      threads: type int, number of threads to be spawn
-      qsize: size of the queue q
-      https: indicate it is httpS (True) or http connection (False)
-      timeout: timeout for HTTP connection attempts and requests in
-        seconds
+    spawn multiple connection_worker threads to handle jobs in the queue q
     """
-    self.host = host
-    self.https = https
-    self.q = Queue.Queue(qsize)  # if qsize=0, means infinite
-    self.threads = threads
-    self.timeout = timeout
-    # start thread based on threads number
-    self.tid = {}  # dictionary of thread object
 
-    for i in xrange(threads):
-      tname = "PredictionIOThread-%s" % i  # thread name
-      self.tid[i] = threading.Thread(
-        target=connection_worker, name=tname,
-        kwargs={'host': self.host, 'request_queue': self.q,
-            'https': self.https, 'timeout': self.timeout})
-      self.tid[i].setDaemon(True)
-      self.tid[i].start()
+    def __init__(self, host, threads=1, qsize=0, https=True, timeout=5):
+        """constructor
 
-  def make_request(self, request):
-    """put the request into the q
-    """
-    self.q.put(request)
+        Args:
+          host: host of the server.
+          threads: type int, number of threads to be spawn
+          qsize: size of the queue q
+          https: indicate it is httpS (True) or http connection (False)
+          timeout: timeout for HTTP connection attempts and requests in
+            seconds
+        """
+        self.host = host
+        self.https = https
+        self.q = Queue.Queue(qsize)  # if qsize=0, means infinite
+        self.threads = threads
+        self.timeout = timeout
+        # start thread based on threads number
+        self.tid = {}  # dictionary of thread object
 
-  def pending_requests(self):
-    """number of pending requests in the queue
-    """
-    return self.q.qsize()
+        for i in xrange(threads):
+            tname = "PredictionIOThread-%s" % i  # thread name
+            self.tid[i] = threading.Thread(
+                target=connection_worker, name=tname,
+                kwargs={'host': self.host, 'request_queue': self.q,
+                        'https': self.https, 'timeout': self.timeout})
+            self.tid[i].setDaemon(True)
+            self.tid[i].start()
 
-  def close(self):
-    """close this Connection. Call this when main program exits
-    """
-    # set kill message to q
-    for i in xrange(self.threads):
-      self.make_request(AsyncRequest("KILL", ""))
+    def make_request(self, request):
+        """put the request into the q
+        """
+        self.q.put(request)
 
-    self.q.join()  # wait for q empty
+    def pending_requests(self):
+        """number of pending requests in the queue
+        """
+        return self.q.qsize()
 
-    for i in xrange(self.threads):  # wait for all thread finish
-      self.tid[i].join()
+    def close(self):
+        """close this Connection. Call this when main program exits
+        """
+        # set kill message to q
+        for i in xrange(self.threads):
+            self.make_request(AsyncRequest("KILL", ""))
+
+        self.q.join()  # wait for q empty
+
+        for i in xrange(self.threads):  # wait for all thread finish
+            self.tid[i].join()
diff --git a/predictionio/obsolete.py b/predictionio/obsolete.py
deleted file mode 100644
index dc19a09..0000000
--- a/predictionio/obsolete.py
+++ /dev/null
@@ -1,1205 +0,0 @@
-# import packages
-import re
-try:
-  import httplib
-except ImportError:
-  # pylint: disable=F0401
-  # http is a Python3 module, replacing httplib
-  from http import client as httplib
-import json
-import urllib
-import warnings
-
-from predictionio.connection import Connection
-from predictionio.connection import AsyncRequest
-from predictionio.connection import PredictionIOAPIError
-
-"""Error exception defined for this API
-
-Should be handled by the user
-"""
-
-
-class ServerStatusError(PredictionIOAPIError):
-
-  "Error happened when tried to get status of the API server"
-  pass
-
-
-class UserNotCreatedError(PredictionIOAPIError):
-
-  "Error happened when tried to create user"
-  pass
-
-
-class UserNotFoundError(PredictionIOAPIError):
-
-  "Error happened when tried to get user"
-  pass
-
-
-class UserNotDeletedError(PredictionIOAPIError):
-
-  "Error happened when tried to delete user"
-  pass
-
-
-class ItemNotCreatedError(PredictionIOAPIError):
-
-  "Error happened when tried to create item"
-  pass
-
-
-class ItemNotFoundError(PredictionIOAPIError):
-
-  "Error happened when tried to get item"
-  pass
-
-
-class ItemNotDeletedError(PredictionIOAPIError):
-
-  "Error happened when tried to delete item"
-  pass
-
-
-class U2IActionNotCreatedError(PredictionIOAPIError):
-
-  "Error happened when tried to create user-to-item action"
-  pass
-
-
-class ItemRecNotFoundError(PredictionIOAPIError):
-
-  "Error happened when tried to get item recommendation"
-  pass
-
-
-class ItemSimNotFoundError(PredictionIOAPIError):
-
-  "Error happened when tried to get similar items"
-  pass
-
-class ItemRankNotFoundError(PredictionIOAPIError):
-
-  "Error happened when treid to rank item"
-  pass
-
-class InvalidArgumentError(PredictionIOAPIError):
-
-  "Arguments are not valid"
-  pass
-
-# map to API
-LIKE_API = "like"
-DISLIKE_API = "dislike"
-VIEW_API = "view"
-CONVERSION_API = "conversion"
-RATE_API = "rate"
-
-
-class Client(object):
-
-  """PredictionIO client object.
-
-  This is an object representing a PredictionIO's client. This object
-  provides methods for making PredictionIO API requests.
-
-  :param appkey: the App Key provided by PredictionIO.
-  :param threads: number of threads to handle PredictionIO API requests.
-          Must be >= 1.
-  :param apiurl: the PredictionIO API URL path.
-  :param apiversion: the PredictionIO API version. (optional)
-       (eg. "", or "/v1")
-  :param qsize: the max size of the request queue (optional).
-      The asynchronous request becomes blocking once this size has been
-      reached, until the queued requests are handled.
-      Default value is 0, which means infinite queue size.
-  :param timeout: timeout for HTTP connection attempts and requests in
-    seconds (optional).
-    Default value is 5.
-
-  """
-
-  def __init__(self, appkey, threads=1, apiurl="http://localhost:8000",
-         apiversion="", qsize=0, timeout=5):
-    """Constructor of Client object.
-
-    """
-    warnings.warn("predictionio.Client is deprecated. " + 
-      "Please consider upgrading to our latest version.")
-
-    self.appkey = appkey
-    self.threads = threads
-    self.apiurl = apiurl
-    self.apiversion = apiversion
-    self.qsize = qsize
-    self.timeout = timeout
-
-    # check connection type
-    https_pattern = r'^https://(.*)'
-    http_pattern = r'^http://(.*)'
-    m = re.match(https_pattern, apiurl)
-    self.https = True
-    if m is None:  # not matching https
-      m = re.match(http_pattern, apiurl)
-      self.https = False
-      if m is None:  # not matching http either
-        raise InvalidArgumentError("apiurl is not valid: %s" % apiurl)
-    self.host = m.group(1)
-
-    self._uid = None  # identified uid
-    self._connection = Connection(host=self.host, threads=self.threads,
-                    qsize=self.qsize, https=self.https,
-                    timeout=self.timeout)
-
-  def close(self):
-    """Close this client and the connection.
-
-    Call this method when you want to completely terminate the connection
-    with PredictionIO.
-    It will wait for all pending requests to finish.
-    """
-    self._connection.close()
-
-  def pending_requests(self):
-    """Return the number of pending requests.
-
-    :returns:
-      The number of pending requests of this client.
-    """
-    return self._connection.pending_requests()
-
-  def identify(self, uid):
-    """Identify the uid
-
-    :param uid: user id. type str.
-    """
-    self._uid = uid
-
-  def get_status(self):
-    """Get the status of the PredictionIO API Server
-
-    :returns:
-      status message.
-
-    :raises:
-      ServerStatusError.
-    """
-    path = "/"
-    request = AsyncRequest("GET", path)
-    request.set_rfunc(self._aget_status_resp)
-    self._connection.make_request(request)
-    result = self.aresp(request)
-    return result
-
-  def _aget_status_resp(self, response):
-    """Handle the AsyncResponse of get status request"""
-    if response.error is not None:
-      raise ServerStatusError("Exception happened: %s for request %s" %
-                  (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise ServerStatusError("request: %s status: %s body: %s" %
-                  (response.request, response.status,
-                   response.body))
-
-    # data = json.loads(response.body) # convert json string to dict
-    return response.body
-
-  def acreate_user(self, uid, params={}):
-    """Asynchronously create a user.
-
-    :param uid: user id. type str.
-    :param params: optional attributes. type dictionary.
-        For example,
-        { 'custom': 'value', 'pio_inactive' : True,
-        'pio_latlng': [4.5,67.8] }
-
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-
-    """
-
-    if "pio_latlng" in params:
-      params["pio_latlng"] = ",".join(map(str, params["pio_latlng"]))
-    if "pio_inactive" in params:
-      params["pio_inactive"] = str(params["pio_inactive"]).lower()
-
-    path = "%s/users.json" % self.apiversion
-    request = AsyncRequest(
-      "POST", path, pio_appkey=self.appkey, pio_uid=uid, **params)
-    request.set_rfunc(self._acreate_user_resp)
-    self._connection.make_request(request)
-
-    return request
-
-  def _acreate_user_resp(self, response):
-    """Private function to handle the AsyncResponse of the acreate_user
-    request.
-
-    :param response: AsyncResponse object.
-
-    :returns:
-      None.
-
-    :raises:
-      UserNotCreatedError.
-
-    """
-    if response.error is not None:
-      raise UserNotCreatedError("Exception happened: %s for request %s" %
-                    (response.error, response.request))
-    elif response.status != httplib.CREATED:
-      raise UserNotCreatedError("request: %s status: %s body: %s" %
-                    (response.request, response.status,
-                     response.body))
-
-    return None
-
-  def aget_user(self, uid):
-    """Asynchronously get user.
-
-    :param uid: user id. type str.
-
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-
-    enc_uid = urllib.quote(uid, "")  # replace special char with %xx
-    path = "%s/users/%s.json" % (self.apiversion, enc_uid)
-    request = AsyncRequest("GET", path, pio_appkey=self.appkey)
-    request.set_rfunc(self._aget_user_resp)
-    self._connection.make_request(request)
-
-    return request
-
-  def _aget_user_resp(self, response):
-    """Private function to handle the AsyncResponse of the aget_user
-    request .
-
-    :param response: AsyncResponse object.
-
-    :returns:
-      User data in Dictionary format.
-
-    :rasies:
-      UserNotFoundError.
-
-    """
-    if response.error is not None:
-      raise UserNotFoundError("Exception happened: %s for request %s" %
-                  (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise UserNotFoundError("request: %s status: %s body: %s" %
-                  (response.request, response.status,
-                   response.body))
-
-    data = json.loads(response.body)  # convert json string to dict
-    return data
-
-  def adelete_user(self, uid):
-    """Asynchronously delete user.
-
-    :param uid: user id. type str.
-
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-
-    enc_uid = urllib.quote(uid, "")  # replace special char with %xx
-    path = "%s/users/%s.json" % (self.apiversion, enc_uid)
-    request = AsyncRequest("DELETE", path, pio_appkey=self.appkey)
-    request.set_rfunc(self._adelete_user_resp)
-    self._connection.make_request(request)
-
-    return request
-
-  def _adelete_user_resp(self, response):
-    """Private function to handle the AsyncResponse of the adelete_user
-    request.
-
-    :param response: AsyncResponse object.
-
-    :returns:
-      None.
-
-    :raises:
-      UserNotDeletedError.
-
-    """
-    if response.error is not None:
-      raise UserNotDeletedError("Exception happened: %s for request %s" %
-                    (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise UserNotDeletedError("request: %s status: %s body: %s" %
-                    (response.request, response.status,
-                     response.body))
-    return None
-
-  def acreate_item(self, iid, itypes, params={}):
-    """Asynchronously create item.
-
-    :param iid: item id. type str.
-    :param itypes: item types. Tuple of Str.
-        For example, if this item belongs to item types "t1", "t2",
-        "t3", "t4",then itypes=("t1", "t2", "t3", "t4").
-        NOTE: if this item belongs to only one itype, use tuple of one
-        element, eg. itypes=("t1",)
-    :param params: optional attributes. type dictionary.
-        For example,
-        { 'custom': 'value', 'pio_inactive' : True,
-        'pio_latlng': [4.5,67.8] }
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    itypes_str = ",".join(itypes)  # join items with ","
-
-    if "pio_latlng" in params:
-      params["pio_latlng"] = ",".join(map(str, params["pio_latlng"]))
-    if "pio_inactive" in params:
-      params["pio_inactive"] = str(params["pio_inactive"]).lower()
-
-    path = "%s/items.json" % self.apiversion
-    request = AsyncRequest("POST", path, pio_appkey=self.appkey,
-                 pio_iid=iid, pio_itypes=itypes_str, **params)
-    request.set_rfunc(self._acreate_item_resp)
-    self._connection.make_request(request)
-    return request
-
-  def _acreate_item_resp(self, response):
-    """Private function to handle the AsyncResponse of the acreate_item
-    request
-
-    :param response: AsyncResponse object.
-
-    :returns:
-      None
-    :raises:
-      ItemNotCreatedError
-
-    """
-    if response.error is not None:
-      raise ItemNotCreatedError("Exception happened: %s for request %s" %
-                    (response.error, response.request))
-    elif response.status != httplib.CREATED:
-      raise ItemNotCreatedError("request: %s status: %s body: %s" %
-                    (response.request, response.status,
-                     response.body))
-    return None
-
-  def aget_item(self, iid):
-    """Asynchronously get item
-
-    :param iid: item id. type str.
-
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    enc_iid = urllib.quote(iid, "")
-    path = "%s/items/%s.json" % (self.apiversion, enc_iid)
-    request = AsyncRequest("GET", path, pio_appkey=self.appkey)
-    request.set_rfunc(self._aget_item_resp)
-    self._connection.make_request(request)
-    return request
-
-  def _aget_item_resp(self, response):
-    """Private function to handle the AsyncResponse of the aget_item
-    request
-
-    :param response: AsyncResponse object.
-
-    :returns:
-      item data in dictionary format.
-
-    :raises:
-      ItemNotFoundError.
-
-    """
-    if response.error is not None:
-      raise ItemNotFoundError("Exception happened: %s for request %s" %
-                  (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise ItemNotFoundError("request: %s status: %s body: %s" %
-                  (response.request, response.status,
-                   response.body))
-
-    data = json.loads(response.body)  # convert json string to dict
-    if "pio_itypes" in data:
-      # convert from list to tuple
-      data["pio_itypes"] = tuple(data["pio_itypes"])
-
-    return data
-
-  def adelete_item(self, iid):
-    """Asynchronously delete item
-
-    :param iid: item id. type str.
-
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-
-    enc_iid = urllib.quote(iid, "")
-    path = "%s/items/%s.json" % (self.apiversion, enc_iid)
-    request = AsyncRequest("DELETE", path, pio_appkey=self.appkey)
-    request.set_rfunc(self._adelete_item_resp)
-    self._connection.make_request(request)
-    return request
-
-  def _adelete_item_resp(self, response):
-    """Private function to handle the AsyncResponse of the adelete_item
-    request
-
-    :param response: AsyncResponse object
-
-    :returns:
-      None
-
-    :raises:
-      ItemNotDeletedError
-    """
-    if response.error is not None:
-      raise ItemNotDeletedError("Exception happened: %s for request %s" %
-                    (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise ItemNotDeletedError("request: %s status: %s body: %s" %
-                    (response.request, response.status,
-                     response.body))
-    return None
-
-  def _aget_user_itemrec_topn(self, engine, uid, n, params={}):
-    """Private function to asynchronously get recommendations for user
-
-    :param engine: name of the prediction engine. type str.
-    :param uid: user id. type str.
-    :param n: number of recommendation. type int.
-    :param params: optional parameters. type dictionary
-        For example, { 'pio_itypes' : ("t1","t2") }
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    if "pio_itypes" in params:
-      params["pio_itypes"] = ",".join(params["pio_itypes"])
-    if "pio_latlng" in params:
-      params["pio_latlng"] = ",".join(map(str, params["pio_latlng"]))
-    if "pio_attributes" in params:
-      params["pio_attributes"] = ",".join(params["pio_attributes"])
-
-    path = "%s/engines/itemrec/%s/topn.json" % (self.apiversion, engine)
-    request = AsyncRequest("GET", path, pio_appkey=self.appkey,
-                 pio_uid=uid, pio_n=n, **params)
-    request.set_rfunc(self._aget_user_itemrec_topn_resp)
-    self._connection.make_request(request)
-    return request
-
-  def _aget_user_itemrec_topn_resp(self, response):
-    """Private function to handle the AsyncResponse of the aget_itemrec
-    request
-
-    :param response: AsyncResponse object
-
-    :returns:
-      data in dictionary format.
-
-    :raises:
-      ItemRecNotFoundError.
-    """
-    if response.error is not None:
-      raise ItemRecNotFoundError(
-        "Exception happened: %s for request %s" %
-        (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise ItemRecNotFoundError("request: %s status: %s body: %s" %
-                    (response.request, response.status,
-                     response.body))
-
-    data = json.loads(response.body)  # convert json string to dict
-    return data
-
-  def aget_itemrec_topn(self, engine, n, params={}):
-    """Asynchronously get recommendations for the identified user
-
-    :param engine: name of the prediction engine. type str.
-    :param n: number of recommendation. type int.
-    :param params: optional parameters. type dictionary
-        For example, { 'pio_itypes' : ("t1",) }
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-
-    if self._uid is None:
-      raise InvalidArgumentError(
-        "uid is not identified. Please call identify(uid) first.")
-
-    request = self._aget_user_itemrec_topn(engine, self._uid, n, params)
-    return request
-
-  def aget_itemrec(self, uid, n, engine, **params):
-    """Deprecated. Asynchronously get recommendations
-
-    :param uid: user id. type str.
-    :param n: number of recommendation. type int.
-    :param engine: name of the prediction engine. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng="123.4, 56.7"
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    request = self._aget_user_itemrec_topn(engine, uid, n, params)
-    return request
-
-  def _aget_itemsim_topn(self, engine, iid, n, params={}):
-    """Private function to asynchronously get top n similar items of the
-    item
-
-    :param engine: name of the prediction engine. type str.
-    :param iid: item id. type str.
-    :param n: number of similar items. type int.
-    :param params: optional parameters. type dictionary
-        For example, { 'pio_itypes' : ("t1","t2") }
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    if "pio_itypes" in params:
-      params["pio_itypes"] = ",".join(params["pio_itypes"])
-    if "pio_latlng" in params:
-      params["pio_latlng"] = ",".join(map(str, params["pio_latlng"]))
-    if "pio_attributes" in params:
-      params["pio_attributes"] = ",".join(params["pio_attributes"])
-
-    path = "%s/engines/itemsim/%s/topn.json" % (self.apiversion, engine)
-    request = AsyncRequest("GET", path, pio_appkey=self.appkey,
-                 pio_iid=iid, pio_n=n, **params)
-    request.set_rfunc(self._aget_itemsim_topn_resp)
-    self._connection.make_request(request)
-    return request
-
-  def _aget_itemsim_topn_resp(self, response):
-    """Private function to handle the AsyncResponse of the aget_itemsim
-    request
-
-    :param response: AsyncResponse object
-
-    :returns:
-      data in dictionary format.
-
-    :raises:
-      ItemSimNotFoundError.
-    """
-    if response.error is not None:
-      raise ItemSimNotFoundError(
-        "Exception happened: %s for request %s" %
-        (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise ItemSimNotFoundError("request: %s status: %s body: %s" %
-                    (response.request, response.status,
-                     response.body))
-
-    data = json.loads(response.body)  # convert json string to dict
-    return data
-
-  def aget_itemsim_topn(self, engine, iid, n, params={}):
-    """Asynchronously get top n similar items of the item
-
-    :param engine: name of the prediction engine. type str.
-    :param iid: item id. type str.
-    :param n: number of similar items. type int.
-    :param params: optional parameters. type dictionary
-        For example, { 'pio_itypes' : ("t1",) }
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-
-    request = self._aget_itemsim_topn(engine, iid, n, params)
-    return request
-
-  def _aget_user_itemrank_ranked(self, engine, uid, iids, params={}):
-    """Private function to asynchronously get ranked item for user
-
-    :param engine: name of the prediction engine. type str.
-    :param uid: user id. type str.
-    :param iids: items to be ranked. type list of item ids.
-      For example, ["i0", "i1", "i2"]
-    :param params: optional parameters. type dictionary
-      For example. { 'pio_attributes' : "name" }
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    if "pio_attributes" in params:
-      params["pio_attributes"] = ",".join(params["pio_attributes"])
-
-    pio_iids = ",".join(iids)
-
-    path = "%s/engines/itemrank/%s/ranked.json" % \
-      (self.apiversion, engine)
-    request = AsyncRequest("GET", path, pio_appkey=self.appkey,
-                           pio_uid=uid, pio_iids=pio_iids, **params)
-    request.set_rfunc(self._aget_user_itemrank_ranked_resp)
-    self._connection.make_request(request)
-    return request
-
-  def _aget_user_itemrank_ranked_resp(self, response):
-    """Private function to handle the AsyncResponse of the aget_itemreoder
-      request
-
-    :param response: AsyncResponse object
-
-    :returns:
-      data in dictionary format.
-
-    :raises:
-      ItemRankNotFoundError.
-    """
-    if response.error is not None:
-      raise ItemRankNotFoundError(
-        "Exception happened: %s for request %s" %
-        (response.error, response.request))
-    elif response.status != httplib.OK:
-      raise ItemRankNotFoundError("request: %s status: %s body: %s" %
-                                  (response.request, response.status,
-                                   response.body))
-
-    data = json.loads(response.body)  # convert json string to dict
-    return data
-
-  def aget_itemrank_ranked(self, engine, iids, params={}):
-    """Asynchronously get ranked item for user
-
-    :param engine: name of the prediction engine. type str.
-    :param iids: items to be ranked. type list of item ids.
-      For example, ["i0", "i1", "i2"]
-    :param params: optional parameters. type dictionary
-      For example. { 'pio_attributes' : "name" }
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-
-    if self._uid is None:
-      raise InvalidArgumentError(
-        "uid is not identified. Please call identify(uid) first.")
-
-    request = self._aget_user_itemrank_ranked(engine,
-      self._uid, iids, params)
-    return request
-
-  def _auser_action_on_item(self, action, uid, iid, params):
-    """Private function to asynchronously create an user action on an item
-
-    :param action: action type. type str. ("like", "dislike", "conversion",
-                         "rate", "view")
-    :param uid: user id. type str or int.
-    :param iid: item id. type str or int.
-    :param params: optional attributes. type dictionary.
-        For example, { 'pio_rate' : 4, 'pio_latlng' : [1.23,4.56] }
-        NOTE: For "rate" action, pio_rate attribute is required.
-            integer value of 1-5 (1 is least preferred and 5 is most
-            preferred)
-
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    if "pio_latlng" in params:
-      params["pio_latlng"] = ",".join(map(str, params["pio_latlng"]))
-
-    path = "%s/actions/u2i.json" % (self.apiversion)
-    request = AsyncRequest("POST", path, pio_appkey=self.appkey,
-                 pio_action=action, pio_uid=uid, pio_iid=iid,
-                 **params)
-    request.set_rfunc(self._auser_action_on_item_resp)
-    self._connection.make_request(request)
-    return request
-
-  def _auser_action_on_item_resp(self, response):
-    """Private function to handle the AsyncResponse of the
-    _auser_action_on_item request
-
-    :param response: AsyncResponse object
-
-    :returns:
-      None
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-    if response.error is not None:
-      raise U2IActionNotCreatedError(
-        "Exception happened: %s for request %s" %
-        (response.error, response.request))
-    elif response.status != httplib.CREATED:
-      raise U2IActionNotCreatedError("request: %s status: %s body: %s" %
-                       (response.request, response.status,
-                      response.body))
-    return None
-
-  def arecord_action_on_item(self, action, iid, params={}):
-    """Asynchronously create action on item
-
-    :param action: action name. type String. For example, "rate", "like",
-             etc
-    :param iid: item id. type str or int.
-    :param params: optional attributes. type dictionary.
-        For example, { 'pio_rate' : 4, 'pio_latlng': [4.5,67.8] }
-        NOTE: For "rate" action, pio_rate attribute is required.
-        integer value of 1-5 (1 is least preferred and 5 is most
-        preferred)
-
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-
-    if self._uid is None:
-      raise InvalidArgumentError(
-        "uid is not identified. Please call identify(uid) first.")
-
-    request = self._auser_action_on_item(action, self._uid, iid, params)
-    return request
-
-  def auser_conversion_item(self, uid, iid, **params):
-    """Deprecated. Asynchronously create an user conversion action on an
-    item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    request = self._auser_action_on_item(CONVERSION_API, uid, iid, params)
-    return request
-
-  def auser_dislike_item(self, uid, iid, **params):
-    """Deprecated. Asynchronously create an user dislike action on an item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    request = self._auser_action_on_item(DISLIKE_API, uid, iid, params)
-    return request
-
-  def auser_like_item(self, uid, iid, **params):
-    """Deprecated. Asynchronously create an user like action on an item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    request = self._auser_action_on_item(LIKE_API, uid, iid, params)
-    return request
-
-  def auser_rate_item(self, uid, iid, rate, **params):
-    """Deprecated. Asynchronously create an user rate action on an item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param rate: rating. integer value of 1-5 (1 is least preferred and 5
-           is most preferred)
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-
-    params['pio_rate'] = rate
-    request = self._auser_action_on_item(RATE_API, uid, iid, params)
-    return request
-
-  def auser_view_item(self, uid, iid, **params):
-    """Deprecated. Asynchronously create an user view action on an item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-    :returns:
-      AsyncRequest object. You should call the aresp() method using this
-      AsyncRequest object as argument to get the final result or status
-      of this asynchronous request.
-    """
-    request = self._auser_action_on_item(VIEW_API, uid, iid, params)
-    return request
-
-  def aresp(self, request):
-    """Get the result of the asynchronous request
-
-    :param request: AsyncRequest object. This object must be returned by
-            the asynchronous request function
-            For example, to get the result of a aget_user()
-            request, call this aresp() with the argument of
-            AsyncRequest object returned by aget_user().
-
-    :returns:
-      The result of this AsyncRequest. The return type is the same as
-      the return type of corresponding blocking request.
-
-      For example,
-
-      Calling aresp() with acreate_user() AsyncRequest returns the same
-      type as create_user(), which is None.
-
-      Calling aresp() with aget_user() AsyncRequest returns the same
-      type as get_user(), which is dictionary data.
-
-    :raises:
-      Exception may be raised if there is error happened. The type of
-      exception is the same as exception type of the correspdoning
-      blocking request.
-
-      For example,
-
-      Calling aresp() with acreate_user() AsyncRequest may raise
-      UserNotCreatedError exception.
-
-      Calling aresp() with aget_user() AsyncRequest may raise
-      UserNotFoundError exception.
-
-    """
-    response = request.get_response()
-    result = request.rfunc(response)
-    return result
-
-  def create_user(self, uid, params={}):
-    """Blocking request to create user
-
-    :param uid: user id. type str.
-    :param params: optional attributes. type dictionary.
-      For example, { 'custom': 'value', 'pio_inactive' : True,
-      'pio_latlng': [4.5,67.8] }
-
-    :returns:
-      None.
-
-    :raises:
-      UserNotCreatedError.
-
-    """
-    request = self.acreate_user(uid, params)
-    result = self.aresp(request)
-    return result
-
-  def get_user(self, uid):
-    """Blocking request to get user
-
-    :param uid: user id. type str or int.
-
-    :returns:
-      User data in Dictionary format.
-
-    :rasies:
-      UserNotFoundError.
-
-    """
-    request = self.aget_user(uid)
-    result = self.aresp(request)
-    return result
-
-  def delete_user(self, uid):
-    """Blocking request to delete the user
-
-    :param uid: user id. type str.
-
-    :returns:
-      None.
-
-    :raises:
-      UserNotDeletedError.
-
-    """
-    request = self.adelete_user(uid)
-    result = self.aresp(request)
-    return result
-
-  def create_item(self, iid, itypes, params={}):
-    """Blocking request to create item
-
-    :param iid: item id. type str.
-    :param itypes: item types. Tuple of Str.
-        For example, if this item belongs to item types "t1", "t2",
-        "t3", "t4", then itypes=("t1", "t2", "t3", "t4").
-        NOTE: if this item belongs to only one itype, use tuple of one
-        element, eg. itypes=("t1",)
-    :param params: optional attributes. type dictionary.
-        For example,
-        { 'custom': 'value', 'pio_inactive' : True,
-        'pio_latlng': [4.5,67.8] }
-
-    :returns:
-      None
-
-    :raises:
-      ItemNotCreatedError
-
-    """
-    request = self.acreate_item(iid, itypes, params)
-    result = self.aresp(request)
-    return result
-
-  def get_item(self, iid):
-    """Blocking request to get item
-
-    :param iid: item id. type str.
-
-    :returns:
-      item data in dictionary format.
-
-    :raises:
-      ItemNotFoundError.
-
-    """
-    request = self.aget_item(iid)
-    result = self.aresp(request)
-    return result
-
-  def delete_item(self, iid):
-    """Blocking request to delete item
-
-    :param iid: item id. type str.
-
-    :returns:
-      None
-
-    :raises:
-      ItemNotDeletedError
-
-    """
-    request = self.adelete_item(iid)
-    result = self.aresp(request)
-    return result
-
-  def get_itemrec_topn(self, engine, n, params={}):
-    """Blocking request to get recommendations for the identified user
-
-    :param engine: name of the prediction engine. type str.
-    :param n: number of recommendation. type int.
-    :param params: optional parameters. type dictionary
-        For example, { 'pio_itypes' : ("t1", "t2") }
-    :returns:
-      data in dictionary format.
-
-    :raises:
-      ItemRecNotFoundError.
-    """
-    request = self.aget_itemrec_topn(engine, n, params)
-    result = self.aresp(request)
-    return result
-
-  def get_itemrec(self, uid, n, engine, **params):
-    """Deprecated. Blocking request to get recommendations
-
-    :param uid: user id. type str or int.
-    :param n: number of recommendation. type int.
-    :param engine: name of the prediction engine. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-
-    :returns:
-      data in dictionary format.
-
-    :raises:
-      ItemRecNotFoundError.
-
-    """
-    request = self.aget_itemrec(uid, n, engine, **params)
-    result = self.aresp(request)
-    return result
-
-  def get_itemsim_topn(self, engine, iid, n, params={}):
-    """Blocking request to get top n similar items of the item
-
-    :param engine: name of the prediction engine. type str.
-    :param iid: item id. type str.
-    :param n: number of similar items. type int.
-    :param params: optional parameters. type dictionary
-        For example, { 'pio_itypes' : ("t1",) }
-    :returns:
-      data in dictionary format.
-
-    :raises:
-      ItemSimNotFoundError.
-    """
-
-    request = self.aget_itemsim_topn(engine, iid, n, params)
-    result = self.aresp(request)
-    return result
-
-  def get_itemrank_ranked(self, engine, iids, params={}):
-    """Blocking request to get ranked item for user
-
-    :param engine: name of the prediction engine. type str.
-    :param iids: items to be ranked. type list of item ids.
-        For example, ["i0", "i1", "i2"]
-    :param params: optional parameters. type dictionary
-        For example. { 'pio_attributes' : "name" }
-    :returns:
-      data in dictionary format.
-
-    :raises:
-      ItemRankNotFoundError.
-    """
-    request = self.aget_itemrank_ranked(engine, iids, params)
-    result = self.aresp(request)
-    return result
-
-  def record_action_on_item(self, action, iid, params={}):
-    """Blocking request to create action on an item
-
-    :param action: action name. type String. For example, "rate", "like",
-             etc
-    :param iid: item id. type str.
-    :param params: optional attributes. type dictionary.
-        For example, { 'pio_rate' : 4, 'pio_latlng' : [1.23,4.56] }
-        NOTE: For "rate" action, pio_rate attribute is required.
-        integer value of 1-5 (1 is least preferred and 5 is most
-        preferred)
-
-    :returns:
-      None
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-    request = self.arecord_action_on_item(action, iid, params)
-    result = self.aresp(request)
-    return result
-
-  def user_conversion_item(self, uid, iid, **params):
-    """Deprecated. Blocking request to create user conversion action on an
-     item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-
-    :returns:
-      None
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-    request = self.auser_conversion_item(uid, iid, **params)
-    result = self.aresp(request)
-    return result
-
-  def user_dislike_item(self, uid, iid, **params):
-    """Deprecated. Blocking request to create user dislike action on an
-     item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-
-    :returns:
-      None
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-    request = self.auser_dislike_item(uid, iid, **params)
-    result = self.aresp(request)
-    return result
-
-  def user_like_item(self, uid, iid, **params):
-    """Deprecated. Blocking request to create user like action on an item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-
-    :returns:
-      None
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-    request = self.auser_like_item(uid, iid, **params)
-    result = self.aresp(request)
-    return result
-
-  def user_rate_item(self, uid, iid, rate, **params):
-    """Deprecated. Blocking request to create user rate action on an item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param rate: rating. integer value of 1-5 (1 is least preferred and 5
-           is most preferred)
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-
-    :returns:
-      None
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-    request = self.auser_rate_item(uid, iid, rate, **params)
-    result = self.aresp(request)
-    return result
-
-  def user_view_item(self, uid, iid, **params):
-    """Deprecated. Blocking request to create user view action on an item
-
-    :param uid: user id. type str.
-    :param iid: item id. type str.
-    :param params: keyword arguments for optional attributes.
-        For example, pio_latlng=[123.4, 56.7]
-
-    :returns:
-      None
-
-    :raises:
-      U2IActionNotCreatedError
-    """
-    request = self.auser_view_item(uid, iid, **params)
-    result = self.aresp(request)
-    return result
-
diff --git a/setup.py b/setup.py
index 15163d1..67b12e3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
 try:
-  from setuptools import setup
+    from setuptools import setup
 except ImportError:
-  from distutils.core import setup
+    from distutils.core import setup
 
 __author__ = "The PredictionIO Team"
 __email__ = "help@tappingstone.com"
@@ -10,7 +10,7 @@
 
 setup(
     name='PredictionIO',
-    version="0.9.2",
+    version="0.9.6",
     author=__author__,
     author_email=__email__,
     packages=['predictionio'],
@@ -18,17 +18,17 @@
     license='LICENSE.txt',
     description='PredictionIO Python SDK',
     classifiers=[
-      'Programming Language :: Python',
-      'License :: OSI Approved :: Apache Software License',
-      'Operating System :: OS Independent',
-      'Development Status :: 4 - Beta',
-      'Intended Audience :: Developers',
-      'Intended Audience :: Science/Research',
-      'Environment :: Web Environment',
-      'Topic :: Internet :: WWW/HTTP',
-      'Topic :: Scientific/Engineering :: Artificial Intelligence',
-      'Topic :: Scientific/Engineering :: Information Analysis',
-      'Topic :: Software Development :: Libraries :: Python Modules'],
+        'Programming Language :: Python',
+        'License :: OSI Approved :: Apache Software License',
+        'Operating System :: OS Independent',
+        'Development Status :: 4 - Beta',
+        'Intended Audience :: Developers',
+        'Intended Audience :: Science/Research',
+        'Environment :: Web Environment',
+        'Topic :: Internet :: WWW/HTTP',
+        'Topic :: Scientific/Engineering :: Artificial Intelligence',
+        'Topic :: Scientific/Engineering :: Information Analysis',
+        'Topic :: Software Development :: Libraries :: Python Modules'],
     long_description="""PredictionIO Python SDK
 
                        PredictionIO is a prediction server for building smart
@@ -50,5 +50,5 @@
                        PredictionIO API to Python programmers so that they
                        can focus on their application logic.
                        """,
-    install_requires=["pytz >= 2014.2",],
-    )
+    install_requires=["pytz >= 2014.2", ],
+)
diff --git a/tests-obsolete/conversion_test.py b/tests-obsolete/conversion_test.py
new file mode 100644
index 0000000..b9dca97
--- /dev/null
+++ b/tests-obsolete/conversion_test.py
@@ -0,0 +1,16 @@
+import timeit
+
+if __name__ == "__main__":
+    a = True
+
+    t = timeit.Timer("json.dumps(True)", "import json")
+
+    t_bool2json = t.timeit(1000) / 1000
+    print("bool 2 json")
+    print(t_bool2json)
+
+    t = timeit.Timer("str(True).lower()", "")
+
+    t_bool2string = t.timeit(1000) / 1000
+    print("bool 2 string")
+    print(t_bool2string)
diff --git a/tests-obsolete/import_testdata.py b/tests-obsolete/import_testdata.py
new file mode 100644
index 0000000..e5338c0
--- /dev/null
+++ b/tests-obsolete/import_testdata.py
@@ -0,0 +1,46 @@
+"""
+Import simple test data for testing getting itemrec
+"""
+import predictionio
+
+APP_KEY = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"
+API_URL = "http://localhost:7070"
+
+MIN_VERSION = '0.5.0'
+if predictionio.__version__ < MIN_VERSION:
+    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
+    raise Exception(err)
+
+if __name__ == "__main__":
+    client = predictionio.Client(APP_KEY, 1, API_URL)
+
+    client.create_user("u0")
+    client.create_user("u1")
+    client.create_user("u2")
+    client.create_user("u3")
+
+    client.create_item("i0", ("t1",), {"custom1": "i0c1"})
+    client.create_item("i1", ("t1", "t2"), {"custom1": "i1c1", "custom2": "i1c2"})
+    client.create_item("i2", ("t1", "t2"), {"custom2": "i2c2"})
+    client.create_item("i3", ("t1",))
+
+    client.identify("u0")
+    client.record_action_on_item("rate", "i0", {"pio_rate": 2})
+    client.record_action_on_item("rate", "i1", {"pio_rate": 3})
+    client.record_action_on_item("rate", "i2", {"pio_rate": 4})
+
+    client.identify("u1")
+    client.record_action_on_item("rate", "i2", {"pio_rate": 4})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 1})
+
+    client.identify("u2")
+    client.record_action_on_item("rate", "i1", {"pio_rate": 2})
+    client.record_action_on_item("rate", "i2", {"pio_rate": 1})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 3})
+
+    client.identify("u3")
+    client.record_action_on_item("rate", "i0", {"pio_rate": 5})
+    client.record_action_on_item("rate", "i1", {"pio_rate": 3})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 2})
+
+    client.close()
diff --git a/tests-obsolete/import_testdata_id_mismatch.py b/tests-obsolete/import_testdata_id_mismatch.py
new file mode 100644
index 0000000..104f75c
--- /dev/null
+++ b/tests-obsolete/import_testdata_id_mismatch.py
@@ -0,0 +1,46 @@
+"""
+Import simple test data for testing getting itemrec
+"""
+import predictionio
+
+APP_KEY = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"
+API_URL = "http://localhost:8000"
+
+MIN_VERSION = '0.5.0'
+if predictionio.__version__ < MIN_VERSION:
+    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
+    raise Exception(err)
+
+if __name__ == "__main__":
+    client = predictionio.Client(APP_KEY, 1, API_URL)
+
+    client.create_user("u0")
+    client.create_user("u1")
+    client.create_user("u2")
+    client.create_user("u3")
+
+    client.create_item("i0", ("t1",), {"custom1": "i0c1"})
+    client.create_item("i1", ("t1", "t2"), {"custom1": "i1c1", "custom2": "i1c2"})
+    client.create_item("i2", ("t1", "t2"), {"custom2": "i2c2"})
+    client.create_item("i3", ("t1",))
+
+    client.identify("u0x")
+    client.record_action_on_item("rate", "i0", {"pio_rate": 2})
+    client.record_action_on_item("rate", "i1", {"pio_rate": 3})
+    client.record_action_on_item("rate", "i2", {"pio_rate": 4})
+
+    client.identify("u1x")
+    client.record_action_on_item("rate", "i2", {"pio_rate": 4})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 1})
+
+    client.identify("u2x")
+    client.record_action_on_item("rate", "i1", {"pio_rate": 2})
+    client.record_action_on_item("rate", "i2", {"pio_rate": 1})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 3})
+
+    client.identify("u3x")
+    client.record_action_on_item("rate", "i0", {"pio_rate": 5})
+    client.record_action_on_item("rate", "i1", {"pio_rate": 3})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 2})
+
+    client.close()
diff --git a/tests-obsolete/import_testdata_special_char.py b/tests-obsolete/import_testdata_special_char.py
new file mode 100644
index 0000000..c047b6a
--- /dev/null
+++ b/tests-obsolete/import_testdata_special_char.py
@@ -0,0 +1,46 @@
+"""
+Import simple test data (id with special characters) for testing getting itemrec
+"""
+import predictionio
+
+APP_KEY = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"
+API_URL = "http://localhost:8000"
+
+MIN_VERSION = '0.5.0'
+if predictionio.__version__ < MIN_VERSION:
+    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
+    raise Exception(err)
+
+if __name__ == "__main__":
+    client = predictionio.Client(APP_KEY, 1, API_URL)
+
+    client.create_user("u0@u.n")
+    client.create_user("u1@u.n")
+    client.create_user("http://u2.com")
+    client.create_user("u3@u.n")
+
+    client.create_item("http://i0.com", ("t1",), {"custom1": "i0c1"})
+    client.create_item("i1@i1", ("t1", "t2"), {"custom1": "i1c1", "custom2": "i1c2"})
+    client.create_item("i2.org", ("t1", "t2"), {"custom2": "i2c2"})
+    client.create_item("i3", ("t1",))
+
+    client.identify("u0@u.n")
+    client.record_action_on_item("rate", "http://i0.com", {"pio_rate": 2})
+    client.record_action_on_item("rate", "i1@i1", {"pio_rate": 3})
+    client.record_action_on_item("rate", "i2.org", {"pio_rate": 4})
+
+    client.identify("u1@u.n")
+    client.record_action_on_item("rate", "i2.org", {"pio_rate": 4})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 1})
+
+    client.identify("http://u2.com")
+    client.record_action_on_item("rate", "i1@i1", {"pio_rate": 2})
+    client.record_action_on_item("rate", "i2.org", {"pio_rate": 1})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 3})
+
+    client.identify("u3@u.n")
+    client.record_action_on_item("rate", "http://i0.com", {"pio_rate": 5})
+    client.record_action_on_item("rate", "i1@i1", {"pio_rate": 3})
+    client.record_action_on_item("rate", "i3", {"pio_rate": 2})
+
+    client.close()
diff --git a/tests-obsolete/predictionio_itemrec_test.py b/tests-obsolete/predictionio_itemrec_test.py
new file mode 100644
index 0000000..3105f0f
--- /dev/null
+++ b/tests-obsolete/predictionio_itemrec_test.py
@@ -0,0 +1,336 @@
+"""
+Test getting itemrec after algo training completes.
+"""
+
+from __future__ import print_function
+
+import unittest
+
+import predictionio
+
+
+APP_KEY = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"  # replace this with your AppKey
+API_URL = "http://localhost:8000"  # PredictoinIO Server
+
+DEBUG = True
+
+MIN_VERSION = '0.6.0'
+if predictionio.__version__ < MIN_VERSION:
+    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
+    raise Exception(err)
+
+
+class TestPredictionIO(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_get_itemrec_exception_deprecated(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        try:
+            client.get_itemrec("uidwithoutrec", 10, "python-itemrec-engine")
+        except predictionio.ItemRecNotFoundError as e:  # noqa
+            pass  # expected exception
+        except:
+            raise
+
+        client.close()
+
+    def test_get_itemrec_exception(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        client.identify("uidwithoutrec")
+
+        try:
+            client.get_itemrec_topn("python-itemrec-engine", 10)
+        except predictionio.ItemRecNotFoundError as e:  # noqa
+            pass  # expected exception
+        except:
+            raise
+
+        try:
+            client.get_itemrec_topn("python-itemrec-engine", 10,
+                                    {"pio_itypes": ("t1",), "pio_latlng": [1.34, 5.67], "pio_within": 5.0,
+                                     "pio_unit": "km", "pio_attributes": ["custom1", "custom2"]})
+        except predictionio.ItemRecNotFoundError as e:  # noqa
+            pass  # expected exception
+        except:
+            raise
+
+        client.close()
+
+    def test_get_itemrec_deprecated(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        # request more
+        try:
+            itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine")
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
+
+        try:
+            itemrec = client.get_itemrec("u1", 10, "python-itemrec-engine")
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+
+        self.assertTrue((itemrec == {"pio_iids": ["i2", "i1", "i0", "i3"]}) or
+                        (itemrec == {"pio_iids": ["i2", "i0", "i1", "i3"]}))
+
+        try:
+            itemrec = client.get_itemrec("u2", 10, "python-itemrec-engine")
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertTrue((itemrec == {"pio_iids": ["i3", "i0", "i1", "i2"]}) or
+                        (itemrec == {"pio_iids": ["i3", "i1", "i0", "i2"]}))
+
+        try:
+            itemrec = client.get_itemrec("u3", 6, "python-itemrec-engine")
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertTrue((itemrec == {"pio_iids": ["i0", "i1", "i2", "i3"]}) or
+                        (itemrec == {"pio_iids": ["i0", "i2", "i1", "i3"]}))
+
+        # request less
+        try:
+            itemrec = client.get_itemrec("u0", 1, "python-itemrec-engine")
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2"]})
+
+        try:
+            itemrec = client.get_itemrec("u0", 2, "python-itemrec-engine")
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3"]})
+
+        # request with optional attributes
+
+        # pio_itypes
+        try:
+            itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("t1", "t2"))
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
+
+        # subset itypes
+        try:
+            itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("t2",))
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i1"]})
+
+        # nonexisting itypes
+        try:
+            itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("other-itype",))
+        except predictionio.ItemRecNotFoundError as e:
+            pass  # expected no recommendation
+        except:
+            raise
+
+        # pio_attributes
+        try:
+            itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("t1",),
+                                         pio_attributes=["custom1", "custom2"])
+        except predictionio.ItemRecNotFoundError as e:  # noqa
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"], "custom1": [None, None, "i1c1", "i0c1"],
+                                   "custom2": ["i2c2", None, "i1c2", None]})
+
+        client.close()
+
+    def test_get_itemrec(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        # request more
+        client.identify("u0")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 10)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
+
+        client.identify("u1")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 10)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertTrue((itemrec == {"pio_iids": ["i2", "i1", "i0", "i3"]}) or
+                        (itemrec == {"pio_iids": ["i2", "i0", "i1", "i3"]}))
+
+        client.identify("u2")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 10)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertTrue((itemrec == {"pio_iids": ["i3", "i0", "i1", "i2"]}) or
+                        (itemrec == {"pio_iids": ["i3", "i1", "i0", "i2"]}))
+
+        client.identify("u3")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 6)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertTrue((itemrec == {"pio_iids": ["i0", "i1", "i2", "i3"]}) or
+                        (itemrec == {"pio_iids": ["i0", "i2", "i1", "i3"]}))
+
+        # request less
+        client.identify("u0")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 1)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2"]})
+
+        client.identify("u0")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 2)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3"]})
+
+        # request with optional attributes
+
+        # pio_itypes
+        client.identify("u0")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, {"pio_itypes": ("t1", "t2")})
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
+
+        # subset itypes
+        client.identify("u0")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, {"pio_itypes": ("t2",)})
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+        if DEBUG:
+            print(itemrec)
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i1"]})
+
+        # nonexisting itypes
+        client.identify("u0")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, {"pio_itypes": ("other-itype",)})
+        except predictionio.ItemRecNotFoundError as e:
+            pass  # expected no recommendation
+        except:
+            raise
+
+        # pio_attributes
+        client.identify("u0")
+        try:
+            itemrec = client.get_itemrec_topn("python-itemrec-engine", 10,
+                                              {"pio_itypes": ("t1",), "pio_attributes": ["custom1", "custom2"]})
+        except predictionio.ItemRecNotFoundError as e:  # noqa
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"], "custom1": [None, None, "i1c1", "i0c1"],
+                                   "custom2": ["i2c2", None, "i1c2", None]})
+
+        # TODO pio_latlng
+        # TODO pio_within
+        # TODO pio_unit
+
+        client.close()
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests-obsolete/predictionio_itemrec_test_special_char.py b/tests-obsolete/predictionio_itemrec_test_special_char.py
new file mode 100644
index 0000000..a622800
--- /dev/null
+++ b/tests-obsolete/predictionio_itemrec_test_special_char.py
@@ -0,0 +1,366 @@
+"""
+Test getting itemrec after algo training completes.
+"""
+import unittest
+
+import predictionio
+
+
+APP_KEY = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"  # replace this with your AppKey
+API_URL = "http://localhost:8000"  # PredictoinIO Server
+
+DEBUG = True
+
+MIN_VERSION = '0.6.0'
+if predictionio.__version__ < MIN_VERSION:
+    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
+    raise Exception(err)
+
+
+class TestPredictionIO(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_get_itemrec_deprecated(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        uid0 = "u0@u.n"
+        uid1 = "u1@u.n"
+        uid2 = "http://u2.com"
+        uid3 = "u3@u.n"
+
+        iid0 = "http://i0.com"
+        iid1 = "i1@i1"
+        iid2 = "i2.org"
+        iid3 = "i3"
+
+        engine_name = "itemrec"
+
+        # request more
+        try:
+            itemrec = client.get_itemrec(uid0, 10, engine_name)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
+
+        try:
+            itemrec = client.get_itemrec(uid1, 10, engine_name)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertTrue((itemrec == {"pio_iids": [iid2, iid1, iid0, iid3]}) or
+                        (itemrec == {"pio_iids": [iid2, iid0, iid1, iid3]}))
+
+        try:
+            itemrec = client.get_itemrec(uid2, 10, engine_name)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertTrue((itemrec == {"pio_iids": [iid3, iid0, iid1, iid2]}) or
+                        (itemrec == {"pio_iids": [iid3, iid1, iid0, iid2]}))
+
+        try:
+            itemrec = client.get_itemrec(uid3, 6, engine_name)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertTrue((itemrec == {"pio_iids": [iid0, iid1, iid2, iid3]}) or
+                        (itemrec == {"pio_iids": [iid0, iid2, iid1, iid3]}))
+
+        # request less
+        try:
+            itemrec = client.get_itemrec(uid0, 1, engine_name)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2]})
+
+        try:
+            itemrec = client.get_itemrec(uid0, 2, engine_name)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3]})
+
+        # request with optional attributes
+
+        # pio_itypes
+        try:
+            itemrec = client.get_itemrec(uid0, 10, engine_name, pio_itypes=("t1", "t2"))
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
+
+        # subset itypes
+        try:
+            itemrec = client.get_itemrec(uid0, 10, engine_name, pio_itypes=("t2",))
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid1]})
+
+        # nonexisting itypes
+        try:
+            client.get_itemrec(uid0, 10, engine_name, pio_itypes=("other-itype",))
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            pass  # expected no recommendation
+        except:
+            raise
+
+        # pio_attributes
+        try:
+            itemrec = client.get_itemrec(uid0, 10, engine_name, pio_itypes=("t1",),
+                                         pio_attributes=["custom1", "custom2"])
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0], "custom1": [None, None, "i1c1", "i0c1"],
+                                   "custom2": ["i2c2", None, "i1c2", None]})
+
+        client.close()
+
+    def test_get_itemrec(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        uid0 = "u0@u.n"
+        uid1 = "u1@u.n"
+        uid2 = "http://u2.com"
+        uid3 = "u3@u.n"
+
+        iid0 = "http://i0.com"
+        iid1 = "i1@i1"
+        iid2 = "i2.org"
+        iid3 = "i3"
+
+        engine_name = "itemrec"
+
+        # request more
+        client.identify(uid0)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 10)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
+
+        client.identify(uid1)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 10)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertTrue((itemrec == {"pio_iids": [iid2, iid1, iid0, iid3]}) or
+                        (itemrec == {"pio_iids": [iid2, iid0, iid1, iid3]}))
+
+        client.identify(uid2)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 10)
+        except predictionio.ItemRecNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertTrue((itemrec == {"pio_iids": [iid3, iid0, iid1, iid2]}) or
+                        (itemrec == {"pio_iids": [iid3, iid1, iid0, iid2]}))
+
+        client.identify(uid3)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 6)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertTrue((itemrec == {"pio_iids": [iid0, iid1, iid2, iid3]}) or
+                        (itemrec == {"pio_iids": [iid0, iid2, iid1, iid3]}))
+
+        # request less
+        client.identify(uid0)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 1)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2]})
+
+        client.identify(uid0)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 2)
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3]})
+
+        # request with optional attributes
+
+        # pio_itypes
+        client.identify(uid0)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 10, {"pio_itypes": ("t1", "t2")})
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
+
+        # subset itypes
+        client.identify(uid0)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 10, {"pio_itypes": ("t2",)})
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid1]})
+
+        # nonexisting itypes
+        client.identify(uid0)
+        try:
+            client.get_itemrec_topn(engine_name, 10, {"pio_itypes": ("other-itype",)})
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            pass  # expected no recommendation
+        except:
+            raise
+
+        # pio_attributes
+        client.identify(uid0)
+        try:
+            itemrec = client.get_itemrec_topn(engine_name, 10,
+                                              {"pio_itypes": ("t1",), "pio_attributes": ["custom1", "custom2"]})
+        except predictionio.ItemRecNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemrec)
+
+        self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0], "custom1": [None, None, "i1c1", "i0c1"],
+                                   "custom2": ["i2c2", None, "i1c2", None]})
+
+        # TODO pio_latlng
+        # TODO pio_within
+        # TODO pio_unit
+
+        client.close()
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests-obsolete/predictionio_itemsim_test.py b/tests-obsolete/predictionio_itemsim_test.py
new file mode 100644
index 0000000..45fa151
--- /dev/null
+++ b/tests-obsolete/predictionio_itemsim_test.py
@@ -0,0 +1,200 @@
+"""
+Test getting itemsim after algo training completes (pdio-itemsimcf with cosine sim).
+"""
+import unittest
+
+import predictionio
+
+
+APP_KEY = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"  # replace this with your AppKey
+API_URL = "http://localhost:8000"  # PredictoinIO Server
+
+DEBUG = True
+
+MIN_VERSION = '0.6.0'
+if predictionio.__version__ < MIN_VERSION:
+    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
+    raise Exception(err)
+
+
+class TestPredictionIO(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_get_itemsim_exception(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        try:
+            client.get_itemsim_topn("python-itemsim-engine", "iidwithoutsim", 10)
+        except predictionio.ItemSimNotFoundError as e:
+            pass  # expected exception
+        except:
+            raise
+
+        try:
+            client.get_itemsim_topn("python-itemsim-engine", "iidwithoutsim", 10,
+                                    {"pio_itypes": ("t1",), "pio_latlng": [1.34, 5.67], "pio_within": 5.0,
+                                     "pio_unit": "km", "pio_attributes": ["custom1", "custom2"]})
+        except predictionio.ItemSimNotFoundError as e:
+            if DEBUG:
+                print(e)
+            pass  # expected exception
+        except:
+            raise
+
+        client.close()
+
+    def test_get_itemsim(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        # request more than what is available
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i0", 10)
+        except predictionio.ItemSimNotFoundError as e:
+            print
+            "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertTrue((itemsim == {"pio_iids": ["i1", "i2", "i3"]}) or
+                        (itemsim == {"pio_iids": ["i1", "i3", "i2"]}))
+
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10)
+        except predictionio.ItemSimNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertTrue((itemsim == {"pio_iids": ["i2", "i3", "i0"]}))
+
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i2", 10)
+        except predictionio.ItemSimNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertTrue((itemsim == {"pio_iids": ["i1", "i3", "i0"]}))
+
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i3", 10)
+        except predictionio.ItemSimNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertTrue((itemsim == {"pio_iids": ["i1", "i2", "i0"]}))
+
+        # request less
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 1)
+        except predictionio.ItemSimNotFoundError as e:
+            print
+            "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertEqual(itemsim, {"pio_iids": ["i2"]})
+
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 2)
+        except predictionio.ItemSimNotFoundError as e:
+            print
+            "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertEqual(itemsim, {"pio_iids": ["i2", "i3"]})
+
+        # request with optional attributes
+
+        # pio_itypes
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10, {"pio_itypes": ("t1", "t2")})
+        except predictionio.ItemSimNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertEqual(itemsim, {"pio_iids": ["i2", "i3", "i0"]})
+
+        # subset itypes
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10, {"pio_itypes": ("t2",)})
+        except predictionio.ItemSimNotFoundError as e:
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertEqual(itemsim, {"pio_iids": ["i2"]})
+
+        # nonexisting itypes
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i0", 10, {"pio_itypes": ("other-itype",)})
+        except predictionio.ItemSimNotFoundError as e:
+            pass  # expected no recommendation
+        except:
+            raise
+
+        # pio_attributes
+        try:
+            itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10,
+                                              {"pio_itypes": ("t1",), "pio_attributes": ["custom1", "custom2"]})
+        except predictionio.ItemSimNotFoundError as e:
+            print(e)
+            print("ERROR: have you run import_testdata.py and then wait for the algorithm training completion?")
+            raise
+        except:
+            raise
+
+        if DEBUG:
+            print(itemsim)
+
+        self.assertEqual(itemsim, {"pio_iids": ["i2", "i3", "i0"], "custom1": [None, None, "i0c1"],
+                                   "custom2": ["i2c2", None, None]})
+
+        # TODO pio_latlng
+        # TODO pio_within
+        # TODO pio_unit
+
+        client.close()
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests-obsolete/predictionio_test.py b/tests-obsolete/predictionio_test.py
new file mode 100644
index 0000000..696718c
--- /dev/null
+++ b/tests-obsolete/predictionio_test.py
@@ -0,0 +1,257 @@
+"""
+Test Python SDK
+"""
+import unittest
+
+import predictionio
+import time
+
+
+APP_KEY = "gDx1XuMUC9vu1YWWPRZkLRTftoq7m73mlj2MtnZEjncPlZ1JxUS2s7oajwP9xrZQ"  # replace this with your AppKey
+API_URL = "http://localhost:8000"  # PredictoinIO Server
+
+MIN_VERSION = '0.6.0'
+if predictionio.__version__ < MIN_VERSION:
+    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
+    raise Exception(err)
+
+# print predictionio.__version__
+# predictionio.connection.enable_log()
+
+
+class TestPredictionIO(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_status(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+        status = client.get_status()
+        self.assertEqual(status, "PredictionIO Output API is online.")
+        client.close()
+
+    def _test_user(self, uids):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        uid1 = uids[0]
+        uid2 = uids[1]
+        uid3 = uids[2]
+        uid4 = uids[3]
+        uid5 = uids[4]
+
+        # create users and get them back
+        client.create_user(uid1)
+        # create user with optional attributes
+        client.create_user(uid2, {"pio_latlng": [1.2, 33.3]})
+        client.create_user(uid3, {"pio_latlng": [4.5, 67.8], "pio_inactive": True})
+        # create user with custom attributes
+        client.create_user(uid4, {"pio_latlng": [1.2, 33.3], "custom1": "value1", "custom2": "value2"})
+        client.create_user(uid5, {"custom1": "u5c1", "custom2": "u5c2"})
+
+        user1 = client.get_user(uid1)
+        user2 = client.get_user(uid2)
+        user3 = client.get_user(uid3)
+        user4 = client.get_user(uid4)
+        user5 = client.get_user(uid5)
+
+        self.assertEqual(user1, {"pio_uid": uid1})
+        self.assertEqual(user2, {"pio_uid": uid2, "pio_latlng": [1.2, 33.3]})
+        self.assertEqual(user3, {"pio_uid": uid3, "pio_latlng": [4.5, 67.8], "pio_inactive": True})
+        self.assertEqual(user4, {"pio_uid": uid4, "pio_latlng": [1.2, 33.3], "custom1": "value1", "custom2": "value2"})
+        self.assertEqual(user5, {"pio_uid": uid5, "custom1": "u5c1", "custom2": "u5c2"})
+
+        # delete user and then try to get it
+        client.delete_user(uid1)
+
+        try:
+            client.get_user(uid1)
+        except predictionio.UserNotFoundError as e:  # noqa
+            pass  # expected exception
+        except:
+            raise
+
+        # other users still exist
+        user2 = client.get_user(uid2)
+        self.assertEqual(user2, {"pio_uid": uid2, "pio_latlng": [1.2, 33.3]})
+
+        # read, modify, write
+        user3 = client.get_user(uid3)
+        self.assertEqual(user3, {"pio_uid": uid3, "pio_latlng": [4.5, 67.8], "pio_inactive": True})
+        del user3["pio_uid"]
+        user3["pio_latlng"] = [5.6, 10.11]
+        user3["pio_inactive"] = False
+        user3["custom1"] = "food"
+        client.create_user(uid3, user3)
+        updated_user3 = client.get_user(uid3)
+        self.assertEqual(updated_user3,
+                         {"pio_uid": uid3, "pio_latlng": [5.6, 10.11], "pio_inactive": False, "custom1": "food"})
+
+        user4 = client.get_user(uid4)
+        self.assertEqual(user4, {"pio_uid": uid4, "pio_latlng": [1.2, 33.3], "custom1": "value1", "custom2": "value2"})
+        del user4["pio_uid"]
+        user4["custom1"] = "new value"
+        client.create_user(uid4, user4)
+        updated_user4 = client.get_user(uid4)
+        self.assertEqual(updated_user4,
+                         {"pio_uid": uid4, "pio_latlng": [1.2, 33.3], "custom1": "new value", "custom2": "value2"})
+
+        client.close()
+
+    def test_user(self):
+        self._test_user(["u1", "u2", "u3", "u4", "u5"])
+        # test special characters in uid
+        self._test_user(["u1@a.com", "u2@ap/ple", "u3@foo.bar", "u4/a/b", "&^%$()u5"])
+
+    def _test_item(self, iids):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        iid1 = iids[0]
+        iid2 = iids[1]
+        iid3 = iids[2]
+        iid4 = iids[3]
+        iid5 = iids[4]
+
+        # create items and read back
+        client.create_item(iid1, ("t1", "t2", "t3"))
+        client.create_item(iid2, ("t1",))
+        client.create_item(iid3, ("t2",),
+                           {"pio_price": 4.99, "pio_profit": 2.0, "pio_startT": 12345667, "pio_endT": 4567788,
+                            "pio_latlng": [1.345, 9.876], "pio_inactive": True})
+        client.create_item(iid4, ("t2",), {"pio_latlng": [1.2345, 10.11], "custom1": "value1"})
+        client.create_item(iid5, ("t1", "t2"), {"custom1": "i5value1", "custom2": "i5value2"})
+
+        item1 = client.get_item(iid1)
+        item2 = client.get_item(iid2)
+        item3 = client.get_item(iid3)
+        item4 = client.get_item(iid4)
+        item5 = client.get_item(iid5)
+
+        del item1["pio_startT"]  # pio_startT is automatically inserted, don't compare
+        self.assertEqual(item1, {"pio_iid": iid1, "pio_itypes": ("t1", "t2", "t3")})
+        del item2["pio_startT"]
+        self.assertEqual(item2, {"pio_iid": iid2, "pio_itypes": ("t1",)})
+        self.assertEqual(item3, {"pio_iid": iid3, "pio_itypes": ("t2",), "pio_price": 4.99, "pio_profit": 2.0,
+                                 "pio_startT": 12345667, "pio_endT": 4567788, "pio_latlng": [1.345, 9.876],
+                                 "pio_inactive": True})
+        del item4["pio_startT"]
+        self.assertEqual(item4,
+                         {"pio_iid": iid4, "pio_itypes": ("t2",), "pio_latlng": [1.2345, 10.11], "custom1": "value1"})
+        del item5["pio_startT"]
+        self.assertEqual(item5,
+                         {"pio_iid": iid5, "pio_itypes": ("t1", "t2"), "custom1": "i5value1", "custom2": "i5value2"})
+
+        # delete and then try to get it
+        client.delete_item(iid2)
+
+        try:
+            item2 = client.get_item(iid2)
+        except predictionio.ItemNotFoundError as e:  # noqa
+            pass  # expected exception
+        except:
+            raise
+
+        # others still exist
+        item3 = client.get_item(iid3)
+        self.assertEqual(item3, {"pio_iid": iid3, "pio_itypes": ("t2",), "pio_price": 4.99, "pio_profit": 2.0,
+                                 "pio_startT": 12345667, "pio_endT": 4567788, "pio_latlng": [1.345, 9.876],
+                                 "pio_inactive": True})
+
+        # read, modify, write
+        del item3["pio_iid"]
+        item3_itypes = item3.pop("pio_itypes")
+        item3["pio_price"] = 6.99
+        item3["custom1"] = "some value"
+        client.create_item(iid3, item3_itypes, item3)
+        updated_item3 = client.get_item(iid3)
+        self.assertEqual(updated_item3, {"pio_iid": iid3, "pio_itypes": ("t2",), "pio_price": 6.99, "pio_profit": 2.0,
+                                         "pio_startT": 12345667, "pio_endT": 4567788, "pio_latlng": [1.345, 9.876],
+                                         "pio_inactive": True, "custom1": "some value"})
+
+        client.close()
+
+    def test_item(self):
+        self._test_item(["i1", "i2", "i3", "i4", "i5"])
+        # test special characters in iid
+        self._test_item(["i1@abc.com", "i2/f/bar//@@foo", "$$i3%%$~~", "http://www.i4.com", "``i5/apple/"])
+
+    def test_u2iAction_deprecated(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        client.user_like_item("u1", "i1")
+        client.user_dislike_item("u2", "i2")
+        client.user_view_item("u3", "i3")
+        client.user_rate_item("u4", "i4", 4)
+        client.user_conversion_item("u5", "i5")
+
+        client.close()
+
+    def test_u2iAction(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        client.identify("u101")
+
+        # required param
+        client.record_action_on_item("like", "i1")
+        client.record_action_on_item("dislike", "i2")
+        client.record_action_on_item("view", "i3")
+        client.record_action_on_item("rate", "i4", {"pio_rate": 1})
+        client.record_action_on_item("conversion", "i5")
+
+        client.identify("u102")
+
+        # with optional param
+        client.record_action_on_item("like", "i1", {"pio_latlng": [1.23, 4.56]})
+        client.record_action_on_item("dislike", "i2", {"pio_t": 1234567689})
+        client.record_action_on_item("view", "i3", {"pio_latlng": [4.67, 1.44], "pio_t": 3445566778})
+        client.record_action_on_item("rate", "i4", {"pio_rate": 1, "pio_latlng": [66.78, 9.10]})
+        client.record_action_on_item("conversion", "i5", {"pio_price": 12.5})
+
+        # uid and iid with special characters
+        client.identify("u1@a.com")
+        client.record_action_on_item("view", "i3@bb.com")
+        client.record_action_on_item("view", "http://www.yahoo.com")
+
+        client.close()
+
+    def test_pending_requests(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL)
+
+        client.identify("u111")
+        for i in range(100):
+            client.arecord_action_on_item("like", str(i))
+
+        n = 1
+        while n > 0:
+            n = client.pending_requests()
+            time.sleep(0.1)
+            # print n
+
+        client.close()
+
+    def test_qsize(self):
+        client = predictionio.Client(APP_KEY, 1, API_URL, qsize=10)
+
+        client.identify("u222")
+        for i in range(100):
+            client.arecord_action_on_item("like", str(i))
+
+        n = 1
+        while n > 0:
+            n = client.pending_requests()
+            time.sleep(0.1)
+            # print n
+
+        client.close()
+
+
+"""
+to run individual test:
+$ python -m unittest predictionio_test.TestPredictionIO.test_user
+
+to run ALL tests:
+% python predictionio_test.py
+"""
+if __name__ == "__main__":
+    unittest.main()
diff --git a/examples/obsolete/__init__.py b/tests/.keep
similarity index 100%
rename from examples/obsolete/__init__.py
rename to tests/.keep
diff --git a/tests/conversion_test.py b/tests/conversion_test.py
deleted file mode 100644
index ffcd90f..0000000
--- a/tests/conversion_test.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import timeit
-import json
-
-if __name__ == "__main__":
-	a = True
-
-	t = timeit.Timer("json.dumps(True)", "import json")
-
-	t_bool2json = t.timeit(1000)/1000
-	print "bool 2 json"
-	print t_bool2json
-
-	t = timeit.Timer("str(True).lower()", "")
-
-	t_bool2string = t.timeit(1000)/1000
-	print "bool 2 string"
-	print t_bool2string
-
-
-
-
-
-
diff --git a/tests/import_testdata.py b/tests/import_testdata.py
deleted file mode 100644
index 57ec48c..0000000
--- a/tests/import_testdata.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
-Import simple test data for testing getting itemrec
-"""
-import predictionio
-
-APP_KEY = "tGgZ7bJDpSyxLndJUBWgyAUwfBgVSTjO6KkhjnMpzCi7vSgPoYnXYptyVlg3vjLH"
-API_URL = "http://localhost:8000"
-
-MIN_VERSION = '0.5.0'
-if predictionio.__version__ < MIN_VERSION:
-    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
-    raise Exception(err)
-
-if __name__ == "__main__":
-	client = predictionio.Client(APP_KEY, 1, API_URL)
-
-	client.create_user("u0")
-	client.create_user("u1")
-	client.create_user("u2")
-	client.create_user("u3")
-
-	client.create_item("i0", ("t1",), {"custom1": "i0c1"})
-	client.create_item("i1", ("t1","t2"), {"custom1": "i1c1", "custom2": "i1c2"})
-	client.create_item("i2", ("t1","t2"), {"custom2": "i2c2"})
-	client.create_item("i3", ("t1",))
-
-	client.identify("u0")
-	client.record_action_on_item("rate", "i0", { "pio_rate": 2 })
-	client.record_action_on_item("rate", "i1", { "pio_rate": 3 })
-	client.record_action_on_item("rate", "i2", { "pio_rate": 4 })
-	
-	client.identify("u1")
-	client.record_action_on_item("rate", "i2", { "pio_rate": 4 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 1 })
-
-	client.identify("u2")
-	client.record_action_on_item("rate", "i1", { "pio_rate": 2 })
-	client.record_action_on_item("rate", "i2", { "pio_rate": 1 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 3 })
-
-	client.identify("u3")
-	client.record_action_on_item("rate", "i0", { "pio_rate": 5 })
-	client.record_action_on_item("rate", "i1", { "pio_rate": 3 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 2 })
-
-	client.close()
-	
-
-	
-
-
-
diff --git a/tests/import_testdata_id_mismatch.py b/tests/import_testdata_id_mismatch.py
deleted file mode 100644
index 17d707a..0000000
--- a/tests/import_testdata_id_mismatch.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
-Import simple test data for testing getting itemrec
-"""
-import predictionio
-
-APP_KEY = "7zwXYnroz52gemLdHEU20Nn8c2SyobFpnrzoTGOolCe8ZRH2zmGyhVknj9Sa7P6x"
-API_URL = "http://localhost:8000"
-
-MIN_VERSION = '0.5.0'
-if predictionio.__version__ < MIN_VERSION:
-    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
-    raise Exception(err)
-
-if __name__ == "__main__":
-	client = predictionio.Client(APP_KEY, 1, API_URL)
-
-	client.create_user("u0")
-	client.create_user("u1")
-	client.create_user("u2")
-	client.create_user("u3")
-
-	client.create_item("i0", ("t1",), {"custom1": "i0c1"})
-	client.create_item("i1", ("t1","t2"), {"custom1": "i1c1", "custom2": "i1c2"})
-	client.create_item("i2", ("t1","t2"), {"custom2": "i2c2"})
-	client.create_item("i3", ("t1",))
-
-	client.identify("u0x")
-	client.record_action_on_item("rate", "i0", { "pio_rate": 2 })
-	client.record_action_on_item("rate", "i1", { "pio_rate": 3 })
-	client.record_action_on_item("rate", "i2", { "pio_rate": 4 })
-	
-	client.identify("u1x")
-	client.record_action_on_item("rate", "i2", { "pio_rate": 4 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 1 })
-
-	client.identify("u2x")
-	client.record_action_on_item("rate", "i1", { "pio_rate": 2 })
-	client.record_action_on_item("rate", "i2", { "pio_rate": 1 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 3 })
-
-	client.identify("u3x")
-	client.record_action_on_item("rate", "i0", { "pio_rate": 5 })
-	client.record_action_on_item("rate", "i1", { "pio_rate": 3 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 2 })
-
-	client.close()
-	
-
-	
-
-
-
diff --git a/tests/import_testdata_special_char.py b/tests/import_testdata_special_char.py
deleted file mode 100644
index e9445be..0000000
--- a/tests/import_testdata_special_char.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
-Import simple test data (id with special characters) for testing getting itemrec
-"""
-import predictionio
-
-APP_KEY = "GToKwk78As0LBp2fAx2YNUBPZFZvtwy6MJkGwRASiD6Q77JjBnTaXBxzBTd52ICE"
-API_URL = "http://localhost:8000"
-
-MIN_VERSION = '0.5.0'
-if predictionio.__version__ < MIN_VERSION:
-    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
-    raise Exception(err)
-
-if __name__ == "__main__":
-	client = predictionio.Client(APP_KEY, 1, API_URL)
-
-	client.create_user("u0@u.n")
-	client.create_user("u1@u.n")
-	client.create_user("http://u2.com")
-	client.create_user("u3@u.n")
-
-	client.create_item("http://i0.com", ("t1",), {"custom1": "i0c1"})
-	client.create_item("i1@i1", ("t1","t2"), {"custom1": "i1c1", "custom2": "i1c2"})
-	client.create_item("i2.org", ("t1","t2"), {"custom2": "i2c2"})
-	client.create_item("i3", ("t1",))
-
-	client.identify("u0@u.n")
-	client.record_action_on_item("rate", "http://i0.com", { "pio_rate": 2 })
-	client.record_action_on_item("rate", "i1@i1", { "pio_rate": 3 })
-	client.record_action_on_item("rate", "i2.org", { "pio_rate": 4 })
-	
-	client.identify("u1@u.n")
-	client.record_action_on_item("rate", "i2.org", { "pio_rate": 4 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 1 })
-
-	client.identify("http://u2.com")
-	client.record_action_on_item("rate", "i1@i1", { "pio_rate": 2 })
-	client.record_action_on_item("rate", "i2.org", { "pio_rate": 1 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 3 })
-
-	client.identify("u3@u.n")
-	client.record_action_on_item("rate", "http://i0.com", { "pio_rate": 5 })
-	client.record_action_on_item("rate", "i1@i1", { "pio_rate": 3 })
-	client.record_action_on_item("rate", "i3", { "pio_rate": 2 })
-
-	client.close()
-	
-
-	
-
-
-
diff --git a/tests/predictionio_itemrec_test.py b/tests/predictionio_itemrec_test.py
deleted file mode 100644
index 5f91501..0000000
--- a/tests/predictionio_itemrec_test.py
+++ /dev/null
@@ -1,300 +0,0 @@
-"""
-Test getting itemrec after algo training completes.
-"""
-import predictionio
-import unittest
-import time
-
-APP_KEY = "y2Fk4BACEGYeJnqBF4zL9TmrIBdF9va3gyFaLsnM7PVyUNf0G00zC8vCnyBx5hdA" # replace this with your AppKey
-API_URL = "http://localhost:8000" # PredictoinIO Server
-
-DEBUG = True
-
-MIN_VERSION = '0.6.0'
-if predictionio.__version__ < MIN_VERSION:
-	err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
-	raise Exception(err)
-
-class TestPredictionIO(unittest.TestCase):
-	def setUp(self):
-		pass
-
-	def tearDown(self):
-		pass
-
-	def test_get_itemrec_exception_deprecated(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		try:
-			itemrec = client.get_itemrec("uidwithoutrec", 10, "python-itemrec-engine")
-		except predictionio.ItemRecNotFoundError as e:
-			pass # expected exception
-		except:
-			raise
-
-		client.close()
-
-	def test_get_itemrec_exception(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		client.identify("uidwithoutrec")
-
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10)
-		except predictionio.ItemRecNotFoundError as e:
-			pass # expected exception
-		except:
-			raise
-
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, { "pio_itypes": ("t1",), "pio_latlng": [1.34, 5.67], "pio_within": 5.0, "pio_unit": "km", "pio_attributes": ["custom1", "custom2"]  })
-		except predictionio.ItemRecNotFoundError as e:
-			pass # expected exception
-		except:
-			raise
-
-		client.close()
-
-	def test_get_itemrec_deprecated(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		# request more
-		try:
-			itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine")
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
-
-		try:
-			itemrec = client.get_itemrec("u1", 10, "python-itemrec-engine")
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": ["i2", "i1", "i0", "i3"]}) or 
-						 (itemrec == {"pio_iids": ["i2", "i0", "i1", "i3"]}) )
-
-		try:
-			itemrec = client.get_itemrec("u2", 10, "python-itemrec-engine")
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": ["i3", "i0", "i1", "i2"]}) or
-						 (itemrec == {"pio_iids": ["i3", "i1", "i0", "i2"]}) )
-
-		try:
-			itemrec = client.get_itemrec("u3", 6, "python-itemrec-engine")
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": ["i0", "i1", "i2", "i3"]}) or
-						 (itemrec == {"pio_iids": ["i0", "i2", "i1", "i3"]}) )
-
-		# request less
-		try:
-			itemrec = client.get_itemrec("u0", 1, "python-itemrec-engine")
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2"]})
-
-		try:
-			itemrec = client.get_itemrec("u0", 2, "python-itemrec-engine")
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3"]})
-
-		# request with optional attributes
-
-		# pio_itypes
-		try:
-			itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("t1","t2"))
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
-
-		# subset itypes
-		try:
-			itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("t2",))
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i1"]})
-
-		# nonexisting itypes
-		try:
-			itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("other-itype",))
-		except predictionio.ItemRecNotFoundError as e:
-			pass # expected no recommendation
-		except:
-			raise
-
-		# pio_attributes
-		try:
-			itemrec = client.get_itemrec("u0", 10, "python-itemrec-engine", pio_itypes=("t1",), pio_attributes=["custom1", "custom2"])
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"], "custom1": [None, None, "i1c1", "i0c1"], "custom2": ["i2c2", None, "i1c2", None]})
-
-		client.close()
-
-
-	def test_get_itemrec(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		# request more
-		client.identify("u0")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
-
-		client.identify("u1")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": ["i2", "i1", "i0", "i3"]}) or 
-						 (itemrec == {"pio_iids": ["i2", "i0", "i1", "i3"]}) )
-
-		client.identify("u2")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": ["i3", "i0", "i1", "i2"]}) or
-						 (itemrec == {"pio_iids": ["i3", "i1", "i0", "i2"]}) )
-
-		client.identify("u3")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 6)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": ["i0", "i1", "i2", "i3"]}) or
-						 (itemrec == {"pio_iids": ["i0", "i2", "i1", "i3"]}) )
-
-		# request less
-		client.identify("u0")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 1)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2"]})
-
-		client.identify("u0")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 2)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3"]})
-
-		# request with optional attributes
-
-		# pio_itypes
-		client.identify("u0")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, {"pio_itypes": ("t1","t2")})
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"]})
-
-		# subset itypes
-		client.identify("u0")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, {"pio_itypes": ("t2",)})
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i1"]})
-
-		# nonexisting itypes
-		client.identify("u0")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, {"pio_itypes": ("other-itype",)})
-		except predictionio.ItemRecNotFoundError as e:
-			pass # expected no recommendation
-		except:
-			raise
-
-		# pio_attributes
-		client.identify("u0")
-		try:
-			itemrec = client.get_itemrec_topn("python-itemrec-engine", 10, {"pio_itypes": ("t1",), "pio_attributes": ["custom1", "custom2"]})
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": ["i2", "i3", "i1", "i0"], "custom1": [None, None, "i1c1", "i0c1"], "custom2": ["i2c2", None, "i1c2", None]})
-
-		# TODO pio_latlng
-		# TODO pio_within
-		# TODO pio_unit
-
-		client.close()
-
-if __name__ == "__main__" :
-	unittest.main()
diff --git a/tests/predictionio_itemrec_test_special_char.py b/tests/predictionio_itemrec_test_special_char.py
deleted file mode 100644
index e4ac6c8..0000000
--- a/tests/predictionio_itemrec_test_special_char.py
+++ /dev/null
@@ -1,291 +0,0 @@
-"""
-Test getting itemrec after algo training completes.
-"""
-import predictionio
-import unittest
-import time
-
-APP_KEY = "GToKwk78As0LBp2fAx2YNUBPZFZvtwy6MJkGwRASiD6Q77JjBnTaXBxzBTd52ICE" # replace this with your AppKey
-API_URL = "http://localhost:8000" # PredictoinIO Server
-
-DEBUG = True
-
-MIN_VERSION = '0.6.0'
-if predictionio.__version__ < MIN_VERSION:
-	err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
-	raise Exception(err)
-
-class TestPredictionIO(unittest.TestCase):
-	def setUp(self):
-		pass
-
-	def tearDown(self):
-		pass
-
-	def test_get_itemrec_deprecated(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		uid0 = "u0@u.n"
-		uid1 = "u1@u.n"
-		uid2 = "http://u2.com"
-		uid3 = "u3@u.n"
-
-		iid0 = "http://i0.com"
-		iid1 = "i1@i1"
-		iid2 = "i2.org"
-		iid3 = "i3"
-
-		engine_name = "itemrec"
-
-		# request more
-		try:
-			itemrec = client.get_itemrec(uid0, 10, engine_name)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
-
-		try:
-			itemrec = client.get_itemrec(uid1, 10, engine_name)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": [iid2, iid1, iid0, iid3]}) or 
-						 (itemrec == {"pio_iids": [iid2, iid0, iid1, iid3]}) )
-
-		try:
-			itemrec = client.get_itemrec(uid2, 10, engine_name)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": [iid3, iid0, iid1, iid2]}) or
-						 (itemrec == {"pio_iids": [iid3, iid1, iid0, iid2]}) )
-
-		try:
-			itemrec = client.get_itemrec(uid3, 6, engine_name)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": [iid0, iid1, iid2, iid3]}) or
-						 (itemrec == {"pio_iids": [iid0, iid2, iid1, iid3]}) )
-
-		# request less
-		try:
-			itemrec = client.get_itemrec(uid0, 1, engine_name)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2]})
-
-		try:
-			itemrec = client.get_itemrec(uid0, 2, engine_name)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3]})
-
-		# request with optional attributes
-
-		# pio_itypes
-		try:
-			itemrec = client.get_itemrec(uid0, 10, engine_name, pio_itypes=("t1","t2"))
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
-
-		# subset itypes
-		try:
-			itemrec = client.get_itemrec(uid0, 10, engine_name, pio_itypes=("t2",))
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid1]})
-
-		# nonexisting itypes
-		try:
-			itemrec = client.get_itemrec(uid0, 10, engine_name, pio_itypes=("other-itype",))
-		except predictionio.ItemRecNotFoundError as e:
-			pass # expected no recommendation
-		except:
-			raise
-
-		# pio_attributes
-		try:
-			itemrec = client.get_itemrec(uid0, 10, engine_name, pio_itypes=("t1",), pio_attributes=["custom1", "custom2"])
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0], "custom1": [None, None, "i1c1", "i0c1"], "custom2": ["i2c2", None, "i1c2", None]})
-
-		client.close()
-
-
-	def test_get_itemrec(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		uid0 = "u0@u.n"
-		uid1 = "u1@u.n"
-		uid2 = "http://u2.com"
-		uid3 = "u3@u.n"
-
-		iid0 = "http://i0.com"
-		iid1 = "i1@i1"
-		iid2 = "i2.org"
-		iid3 = "i3"
-
-		engine_name = "itemrec"
-		
-		# request more
-		client.identify(uid0)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 10)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
-
-		client.identify(uid1)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 10)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": [iid2, iid1, iid0, iid3]}) or 
-						 (itemrec == {"pio_iids": [iid2, iid0, iid1, iid3]}) )
-
-		client.identify(uid2)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 10)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": [iid3, iid0, iid1, iid2]}) or
-						 (itemrec == {"pio_iids": [iid3, iid1, iid0, iid2]}) )
-
-		client.identify(uid3)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 6)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertTrue( (itemrec == {"pio_iids": [iid0, iid1, iid2, iid3]}) or
-						 (itemrec == {"pio_iids": [iid0, iid2, iid1, iid3]}) )
-
-		# request less
-		client.identify(uid0)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 1)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2]})
-
-		client.identify(uid0)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 2)
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3]})
-
-		# request with optional attributes
-
-		# pio_itypes
-		client.identify(uid0)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 10, {"pio_itypes": ("t1","t2")})
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0]})
-
-		# subset itypes
-		client.identify(uid0)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 10, {"pio_itypes": ("t2",)})
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid1]})
-
-		# nonexisting itypes
-		client.identify(uid0)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 10, {"pio_itypes": ("other-itype",)})
-		except predictionio.ItemRecNotFoundError as e:
-			pass # expected no recommendation
-		except:
-			raise
-
-		# pio_attributes
-		client.identify(uid0)
-		try:
-			itemrec = client.get_itemrec_topn(engine_name, 10, {"pio_itypes": ("t1",), "pio_attributes": ["custom1", "custom2"]})
-		except predictionio.ItemRecNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemrec
-		self.assertEqual(itemrec, {"pio_iids": [iid2, iid3, iid1, iid0], "custom1": [None, None, "i1c1", "i0c1"], "custom2": ["i2c2", None, "i1c2", None]})
-
-		# TODO pio_latlng
-		# TODO pio_within
-		# TODO pio_unit
-
-		client.close()
-
-if __name__ == "__main__" :
-	unittest.main()
diff --git a/tests/predictionio_itemsim_test.py b/tests/predictionio_itemsim_test.py
deleted file mode 100644
index c540f02..0000000
--- a/tests/predictionio_itemsim_test.py
+++ /dev/null
@@ -1,160 +0,0 @@
-"""
-Test getting itemsim after algo training completes (pdio-itemsimcf with cosine sim).
-"""
-import predictionio
-import unittest
-import time
-
-APP_KEY = "y2Fk4BACEGYeJnqBF4zL9TmrIBdF9va3gyFaLsnM7PVyUNf0G00zC8vCnyBx5hdA" # replace this with your AppKey
-API_URL = "http://localhost:8000" # PredictoinIO Server
-
-DEBUG = True
-
-MIN_VERSION = '0.6.0'
-if predictionio.__version__ < MIN_VERSION:
-	err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
-	raise Exception(err)
-
-class TestPredictionIO(unittest.TestCase):
-	def setUp(self):
-		pass
-
-	def tearDown(self):
-		pass
-
-	def test_get_itemsim_exception(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "iidwithoutsim", 10)
-		except predictionio.ItemSimNotFoundError as e:
-			pass # expected exception
-		except:
-			raise
-
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "iidwithoutsim", 10, { "pio_itypes": ("t1",), "pio_latlng": [1.34, 5.67], "pio_within": 5.0, "pio_unit": "km", "pio_attributes": ["custom1", "custom2"]  })
-		except predictionio.ItemSimNotFoundError as e:
-			pass # expected exception
-		except:
-			raise
-
-		client.close()
-
-	def test_get_itemsim(self):
-		client = predictionio.Client(APP_KEY, 1, API_URL)
-
-		# request more than what is available
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i0", 10)
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertTrue( (itemsim == {"pio_iids": ["i1", "i2", "i3"]}) or
-						 (itemsim == {"pio_iids": ["i1", "i3", "i2"]}) )
-
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10)
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertTrue( (itemsim == {"pio_iids": ["i2", "i3", "i0"]}) )
-
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i2", 10)
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertTrue( (itemsim == {"pio_iids": ["i1", "i3", "i0"]}) )
-
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i3", 10)
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertTrue( (itemsim == {"pio_iids": ["i1", "i2", "i0"]}) )
-
-		# request less
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 1)
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertEqual(itemsim, {"pio_iids": ["i2"]})
-
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 2)
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertEqual(itemsim, {"pio_iids": ["i2", "i3"]})
-
-		# request with optional attributes
-
-		# pio_itypes
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10, {"pio_itypes": ("t1","t2")})
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertEqual(itemsim, {"pio_iids": ["i2", "i3", "i0"]})
-
-		# subset itypes
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10, {"pio_itypes": ("t2",)})
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertEqual(itemsim, {"pio_iids": ["i2"]})
-
-		# nonexisting itypes
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i0", 10, {"pio_itypes": ("other-itype",)})
-		except predictionio.ItemSimNotFoundError as e:
-			pass # expected no recommendation
-		except:
-			raise
-
-		# pio_attributes
-		try:
-			itemsim = client.get_itemsim_topn("python-itemsim-engine", "i1", 10, {"pio_itypes": ("t1",), "pio_attributes": ["custom1", "custom2"]})
-		except predictionio.ItemSimNotFoundError as e:
-			print "ERROR: have you run import_testdata.py and then wait for the algorithm training completion?"
-			raise
-		except:
-			raise
-		if DEBUG: print itemsim
-		self.assertEqual(itemsim, {"pio_iids": ["i2", "i3", "i0"], "custom1": [None, None, "i0c1"], "custom2": ["i2c2", None, None]})
-
-		# TODO pio_latlng
-		# TODO pio_within
-		# TODO pio_unit
-
-		client.close()
-
-if __name__ == "__main__" :
-	unittest.main()
diff --git a/tests/predictionio_test.py b/tests/predictionio_test.py
deleted file mode 100644
index 28b3d06..0000000
--- a/tests/predictionio_test.py
+++ /dev/null
@@ -1,246 +0,0 @@
-"""
-Test Python SDK
-"""
-import predictionio
-import unittest
-import time
-
-APP_KEY = "GToKwk78As0LBp2fAx2YNUBPZFZvtwy6MJkGwRASiD6Q77JjBnTaXBxzBTd52ICE" # replace this with your AppKey
-API_URL = "http://localhost:8000" # PredictoinIO Server
-
-MIN_VERSION = '0.6.0'
-if predictionio.__version__ < MIN_VERSION:
-    err = "Require PredictionIO Python SDK version >= %s" % MIN_VERSION
-    raise Exception(err)
-
-#print predictionio.__version__
-#predictionio.connection.enable_log()
-
-class TestPredictionIO(unittest.TestCase):
-
-    def setUp(self):
-        pass
-
-    def tearDown(self):
-        pass
-
-    def test_status(self):
-        client = predictionio.Client(APP_KEY, 1, API_URL)
-        status = client.get_status()
-        self.assertEqual(status, "PredictionIO Output API is online.")
-        client.close()
-
-    def _test_user(self, uids):
-        client = predictionio.Client(APP_KEY, 1, API_URL)
-
-        uid1 = uids[0]
-        uid2 = uids[1]
-        uid3 = uids[2]
-        uid4 = uids[3]
-        uid5 = uids[4]
-
-        # create users and get them back
-        client.create_user(uid1)
-        # create user with optional attributes
-        client.create_user(uid2, { "pio_latlng": [1.2,33.3] })
-        client.create_user(uid3, { "pio_latlng": [4.5,67.8], "pio_inactive": True } )
-        # create user with custom attributes
-        client.create_user(uid4, { "pio_latlng": [1.2,33.3], "custom1": "value1", "custom2": "value2" })
-        client.create_user(uid5, { "custom1": "u5c1", "custom2": "u5c2" })
-
-        user1 = client.get_user(uid1)
-        user2 = client.get_user(uid2)
-        user3 = client.get_user(uid3)
-        user4 = client.get_user(uid4)
-        user5 = client.get_user(uid5)
-
-        self.assertEqual(user1, {"pio_uid" : uid1})
-        self.assertEqual(user2, {"pio_uid" : uid2, "pio_latlng": [1.2,33.3]})
-        self.assertEqual(user3, {"pio_uid" : uid3, "pio_latlng" : [4.5,67.8], "pio_inactive" : True})
-        self.assertEqual(user4, {"pio_uid" : uid4, "pio_latlng": [1.2,33.3], "custom1": "value1", "custom2": "value2" })
-        self.assertEqual(user5, {"pio_uid" : uid5, "custom1": "u5c1", "custom2": "u5c2"  })
-
-        # delete user and then try to get it
-        client.delete_user(uid1)
-
-        try:
-            user = client.get_user(uid1)
-        except predictionio.UserNotFoundError as e:
-            pass # expected exception
-        except:
-            raise
-
-        # other users still exist
-        user2 = client.get_user(uid2)
-        self.assertEqual(user2, {"pio_uid" : uid2, "pio_latlng": [1.2,33.3]})
-
-        # read, modify, write
-        user3 = client.get_user(uid3)
-        self.assertEqual(user3, {"pio_uid" : uid3, "pio_latlng" : [4.5,67.8], "pio_inactive" : True})
-        del user3["pio_uid"]
-        user3["pio_latlng"] = [5.6,10.11]
-        user3["pio_inactive"] = False
-        user3["custom1"] = "food"
-        client.create_user(uid3, user3)
-        updated_user3 = client.get_user(uid3)
-        self.assertEqual(updated_user3, {"pio_uid" : uid3, "pio_latlng" : [5.6,10.11], "pio_inactive" : False, "custom1" : "food"} )
-
-        user4 = client.get_user(uid4)
-        self.assertEqual(user4, {"pio_uid" : uid4, "pio_latlng": [1.2,33.3], "custom1": "value1", "custom2": "value2" })
-        del user4["pio_uid"]
-        user4["custom1"] = "new value"
-        client.create_user(uid4, user4)
-        updated_user4 = client.get_user(uid4)
-        self.assertEqual(updated_user4, {"pio_uid" : uid4, "pio_latlng": [1.2,33.3], "custom1": "new value", "custom2": "value2" })
-
-        client.close()
-
-    def test_user(self):
-        self._test_user(["u1", "u2", "u3", "u4", "u5"])
-        # test special characters in uid
-        self._test_user(["u1@a.com", "u2@ap/ple", "u3@foo.bar", "u4/a/b", "&^%$()u5"])
-
-    def _test_item(self, iids):
-        client = predictionio.Client(APP_KEY, 1, API_URL)
-
-        iid1 = iids[0]
-        iid2 = iids[1]
-        iid3 = iids[2]
-        iid4 = iids[3]
-        iid5 = iids[4]
-
-        # create items and read back
-        client.create_item(iid1, ("t1","t2","t3"))
-        client.create_item(iid2, ("t1",))
-        client.create_item(iid3, ("t2",), {"pio_price": 4.99, "pio_profit": 2.0, "pio_startT": 12345667, "pio_endT": 4567788, "pio_latlng": [1.345, 9.876], "pio_inactive": True })
-        client.create_item(iid4, ("t2",), {"pio_latlng": [1.2345, 10.11], "custom1": "value1"})
-        client.create_item(iid5, ("t1", "t2"), {"custom1": "i5value1", "custom2": "i5value2"} )
-
-        item1 = client.get_item(iid1)
-        item2 = client.get_item(iid2)
-        item3 = client.get_item(iid3)
-        item4 = client.get_item(iid4)
-        item5 = client.get_item(iid5)
-
-        del item1["pio_startT"] # pio_startT is automatically inserted, don't compare
-        self.assertEqual(item1, {"pio_iid": iid1, "pio_itypes": ("t1", "t2", "t3") } )
-        del item2["pio_startT"]
-        self.assertEqual(item2, {"pio_iid": iid2, "pio_itypes": ("t1",)} )
-        self.assertEqual(item3, {"pio_iid": iid3, "pio_itypes": ("t2",), "pio_price": 4.99, "pio_profit": 2.0, "pio_startT": 12345667, "pio_endT": 4567788, "pio_latlng": [1.345, 9.876], "pio_inactive": True } )
-        del item4["pio_startT"]
-        self.assertEqual(item4, {"pio_iid": iid4, "pio_itypes": ("t2",), "pio_latlng": [1.2345, 10.11], "custom1": "value1"})
-        del item5["pio_startT"]
-        self.assertEqual(item5, {"pio_iid": iid5, "pio_itypes": ("t1","t2"), "custom1": "i5value1", "custom2": "i5value2"})
-
-        # delete and then try to get it
-        client.delete_item(iid2)
-
-        try:
-          item2 = client.get_item(iid2)
-        except predictionio.ItemNotFoundError as e:
-            pass # expected exception
-        except:
-            raise
-
-        # others still exist
-        item3 = client.get_item(iid3)
-        self.assertEqual(item3, {"pio_iid": iid3, "pio_itypes": ("t2",), "pio_price": 4.99, "pio_profit": 2.0, "pio_startT": 12345667, "pio_endT": 4567788, "pio_latlng": [1.345, 9.876], "pio_inactive": True } )
-
-        # read, modify, write
-        del item3["pio_iid"]
-        item3_itypes = item3.pop("pio_itypes")
-        item3["pio_price"] = 6.99
-        item3["custom1"] = "some value"
-        client.create_item(iid3, item3_itypes, item3)
-        updated_item3 = client.get_item(iid3)
-        self.assertEqual(updated_item3, {"pio_iid": iid3, "pio_itypes": ("t2",), "pio_price": 6.99, "pio_profit": 2.0, "pio_startT": 12345667, "pio_endT": 4567788, "pio_latlng": [1.345, 9.876], "pio_inactive": True, "custom1": "some value" } )
-
-        client.close()
-
-    def test_item(self):
-        self._test_item(["i1", "i2", "i3", "i4", "i5"])
-        # test special characters in iid
-        self._test_item(["i1@abc.com", "i2/f/bar//@@foo", "$$i3%%$~~", "http://www.i4.com", "``i5/apple/"])
-
-    def test_u2iAction_deprecated(self):
-        client = predictionio.Client(APP_KEY, 1, API_URL)
-
-        client.user_like_item("u1", "i1")
-        client.user_dislike_item("u2", "i2")
-        client.user_view_item("u3", "i3")
-        client.user_rate_item("u4", "i4", 4)
-        client.user_conversion_item("u5", "i5")
-
-        client.close()
-
-    def test_u2iAction(self):
-        client = predictionio.Client(APP_KEY, 1, API_URL)
-
-        client.identify("u101")
-
-        # required param
-        client.record_action_on_item("like", "i1")
-        client.record_action_on_item("dislike", "i2")
-        client.record_action_on_item("view", "i3")
-        client.record_action_on_item("rate", "i4", { "pio_rate": 1 })
-        client.record_action_on_item("conversion", "i5")
-
-        client.identify("u102")
-
-        # with optional param
-        client.record_action_on_item("like", "i1", { "pio_latlng": [1.23, 4.56] })
-        client.record_action_on_item("dislike", "i2", { "pio_t": 1234567689 })
-        client.record_action_on_item("view", "i3", { "pio_latlng": [4.67, 1.44], "pio_t": 3445566778})
-        client.record_action_on_item("rate", "i4", { "pio_rate": 1, "pio_latlng": [66.78, 9.10] })
-        client.record_action_on_item("conversion", "i5", { "pio_price" : 12.5 })
-
-
-        # uid and iid with special characters
-        client.identify("u1@a.com")
-        client.record_action_on_item("view", "i3@bb.com")
-        client.record_action_on_item("view", "http://www.yahoo.com")
-
-        client.close()
-
-
-    def test_pending_requests(self):
-        client = predictionio.Client(APP_KEY, 1, API_URL)
-
-        client.identify("u111")
-        for i in range(100):
-            client.arecord_action_on_item("like", str(i))
-
-        n = 1
-        while n > 0:
-            n = client.pending_requests()
-            time.sleep(0.1)
-            #print n
-
-        client.close()
-
-    def test_qsize(self):
-        client = predictionio.Client(APP_KEY, 1, API_URL, qsize=10)
-
-        client.identify("u222")
-        for i in range(100):
-            client.arecord_action_on_item("like", str(i))
-
-        n = 1
-        while n > 0:
-            n = client.pending_requests()
-            time.sleep(0.1)
-            #print n
-
-        client.close()
-
-
-
-"""
-to run individual test:
-$ python -m unittest predictionio_test.TestPredictionIO.test_user
-
-to run ALL tests:
-% python predictionio_test.py
-"""
-if __name__ == "__main__" :
-    unittest.main()
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..23d96e4
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,9 @@
+[tox]
+envlist = py27,py34
+
+[testenv]
+deps = flake8
+       pandas
+
+commands = flake8 --ignore=E501 --filename=*.py --exclude=doc,setup.py,*/tests/*
+           python -m unittest discover --pattern=*.py tests