Merge branch 'develop'
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 9855198..4b41630 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -51,7 +51,7 @@
 # The short X.Y version.
 version = '0.6'
 # The full version, including alpha/beta/rc tags.
-release = '0.6.0'
+release = '0.6.1'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/docs/source/predictionio.rst b/docs/source/predictionio.rst
index c2e6747..4890e5c 100644
--- a/docs/source/predictionio.rst
+++ b/docs/source/predictionio.rst
@@ -190,6 +190,10 @@
 
     .. versionadded:: 0.5.0
 
+  .. automethod:: pending_requests
+
+    .. versionadded:: 0.6.1
+    
   |
 
   .. _sync-methods-label:
diff --git a/predictionio/__init__.py b/predictionio/__init__.py
index 6dc2022..9dbb059 100644
--- a/predictionio/__init__.py
+++ b/predictionio/__init__.py
@@ -9,7 +9,7 @@
 __copyright__ = "Copyright 2013, TappingStone, Inc."
 __license__ = "Apache License, Version 2.0"
 
-__version__ = "0.6.0"
+__version__ = "0.6.1"
 
 
 # import packages
@@ -127,6 +127,14 @@
         """
         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
 
@@ -217,7 +225,7 @@
             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)
@@ -428,9 +436,8 @@
         if "pio_attributes" in params:
             params["pio_attributes"] = ",".join(params["pio_attributes"])
 
-        enc_uid = urllib.quote(uid,"") # replace special char with %xx
         path = "%s/engines/itemrec/%s/topn.json" % (self.apiversion, engine)
-        request = AsyncRequest("GET", path, pio_appkey=self.appkey, pio_uid=enc_uid, pio_n=n, **params)
+        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
@@ -508,9 +515,8 @@
         if "pio_attributes" in params:
             params["pio_attributes"] = ",".join(params["pio_attributes"])
 
-        enc_iid = urllib.quote(iid,"") # replace special char with %xx
         path = "%s/engines/itemsim/%s/topn.json" % (self.apiversion, engine)
-        request = AsyncRequest("GET", path, pio_appkey=self.appkey, pio_iid=enc_iid, pio_n=n, **params)
+        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
diff --git a/predictionio/connection.py b/predictionio/connection.py
index 2623c61..f114ed3 100644
--- a/predictionio/connection.py
+++ b/predictionio/connection.py
@@ -296,6 +296,11 @@
         """
         self.q.put(request)
     
+    def pending_requests(self):
+        """number of pending requests in the queue
+        """
+        return self.q.qsize()
+
     def close(self):
         """close this Connection. Call this when main program exits
         """
diff --git a/tests/import_testdata.py b/tests/import_testdata.py
index add526f..57ec48c 100644
--- a/tests/import_testdata.py
+++ b/tests/import_testdata.py
@@ -3,7 +3,7 @@
 """
 import predictionio
 
-APP_KEY = "y2Fk4BACEGYeJnqBF4zL9TmrIBdF9va3gyFaLsnM7PVyUNf0G00zC8vCnyBx5hdA"
+APP_KEY = "tGgZ7bJDpSyxLndJUBWgyAUwfBgVSTjO6KkhjnMpzCi7vSgPoYnXYptyVlg3vjLH"
 API_URL = "http://localhost:8000"
 
 MIN_VERSION = '0.5.0'
diff --git a/tests/import_testdata_id_mismatch.py b/tests/import_testdata_id_mismatch.py
new file mode 100644
index 0000000..17d707a
--- /dev/null
+++ b/tests/import_testdata_id_mismatch.py
@@ -0,0 +1,52 @@
+"""
+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
new file mode 100644
index 0000000..e9445be
--- /dev/null
+++ b/tests/import_testdata_special_char.py
@@ -0,0 +1,52 @@
+"""
+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_special_char.py b/tests/predictionio_itemrec_test_special_char.py
new file mode 100644
index 0000000..aa026ea
--- /dev/null
+++ b/tests/predictionio_itemrec_test_special_char.py
@@ -0,0 +1,295 @@
+"""
+Test getting itemrec after algo training completes.
+"""
+import predictionio
+import unittest
+import threading
+import time
+
+import httplib
+import urllib
+
+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_test.py b/tests/predictionio_test.py
index b4b3c33..22177b4 100644
--- a/tests/predictionio_test.py
+++ b/tests/predictionio_test.py
@@ -9,14 +9,17 @@
 import httplib
 import urllib
 
-APP_KEY = "y2Fk4BACEGYeJnqBF4zL9TmrIBdF9va3gyFaLsnM7PVyUNf0G00zC8vCnyBx5hdA" # replace this with your AppKey
+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):
@@ -31,116 +34,138 @@
         self.assertEqual(status, "PredictionIO Output API is online.")
         client.close()
 
-    def test_user(self):
+    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("u1")
+        client.create_user(uid1)
         # create user with optional attributes
-        client.create_user("u2", { "pio_latlng": [1.2,33.3] })
-        client.create_user("u3", { "pio_latlng": [4.5,67.8], "pio_inactive": True } )
+        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("u4", { "pio_latlng": [1.2,33.3], "custom1": "value1", "custom2": "value2" })
-        client.create_user("u5", { "custom1": "u5c1", "custom2": "u5c2" })
+        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("u1")
-        user2 = client.get_user("u2")
-        user3 = client.get_user("u3")
-        user4 = client.get_user("u4")
-        user5 = client.get_user("u5")
+        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" : "u1"})
-        self.assertEqual(user2, {"pio_uid" : "u2", "pio_latlng": [1.2,33.3]})
-        self.assertEqual(user3, {"pio_uid" : "u3", "pio_latlng" : [4.5,67.8], "pio_inactive" : True})
-        self.assertEqual(user4, {"pio_uid" : "u4", "pio_latlng": [1.2,33.3], "custom1": "value1", "custom2": "value2" })
-        self.assertEqual(user5, {"pio_uid" : "u5", "custom1": "u5c1", "custom2": "u5c2"  })
+        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("u1")
+        client.delete_user(uid1)
 
         try:
-            user = client.get_user("u1")
+            user = client.get_user(uid1)
         except predictionio.UserNotFoundError as e:
             pass # expected exception
         except:
             raise
 
         # other users still exist
-        user2 = client.get_user("u2")
-        self.assertEqual(user2, {"pio_uid" : "u2", "pio_latlng": [1.2,33.3]})
+        user2 = client.get_user(uid2)
+        self.assertEqual(user2, {"pio_uid" : uid2, "pio_latlng": [1.2,33.3]})
 
         # read, modify, write
-        user3 = client.get_user("u3")
-        self.assertEqual(user3, {"pio_uid" : "u3", "pio_latlng" : [4.5,67.8], "pio_inactive" : True})
+        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("u3", user3)
-        updated_user3 = client.get_user("u3")
-        self.assertEqual(updated_user3, {"pio_uid" : "u3", "pio_latlng" : [5.6,10.11], "pio_inactive" : False, "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("u4")
-        self.assertEqual(user4, {"pio_uid" : "u4", "pio_latlng": [1.2,33.3], "custom1": "value1", "custom2": "value2" })
+        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("u4", user4)
-        updated_user4 = client.get_user("u4")
-        self.assertEqual(updated_user4, {"pio_uid" : "u4", "pio_latlng": [1.2,33.3], "custom1": "new value", "custom2": "value2" })
+        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_item(self):
+    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)
 
-        # create items and read back
-        client.create_item("i1", ("t1","t2","t3"))
-        client.create_item("i2", ("t1",))
-        client.create_item("i3", ("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("i4", ("t2",), {"pio_latlng": [1.2345, 10.11], "custom1": "value1"})
-        client.create_item("i5", ("t1", "t2"), {"custom1": "i5value1", "custom2": "i5value2"} )
+        iid1 = iids[0]
+        iid2 = iids[1]
+        iid3 = iids[2]
+        iid4 = iids[3]
+        iid5 = iids[4]
 
-        item1 = client.get_item("i1")
-        item2 = client.get_item("i2")
-        item3 = client.get_item("i3")
-        item4 = client.get_item("i4")
-        item5 = client.get_item("i5")
+        # 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": "i1", "pio_itypes": ("t1", "t2", "t3") } )
+        self.assertEqual(item1, {"pio_iid": iid1, "pio_itypes": ("t1", "t2", "t3") } )
         del item2["pio_startT"]
-        self.assertEqual(item2, {"pio_iid": "i2", "pio_itypes": ("t1",)} )
-        self.assertEqual(item3, {"pio_iid": "i3", "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 } )
+        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": "i4", "pio_itypes": ("t2",), "pio_latlng": [1.2345, 10.11], "custom1": "value1"})
+        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": "i5", "pio_itypes": ("t1","t2"), "custom1": "i5value1", "custom2": "i5value2"})
+        self.assertEqual(item5, {"pio_iid": iid5, "pio_itypes": ("t1","t2"), "custom1": "i5value1", "custom2": "i5value2"})
 
         # delete and then try to get it
-        client.delete_item("i2")
+        client.delete_item(iid2)
 
         try:
-          item2 = client.get_item("i2")
+          item2 = client.get_item(iid2)
         except predictionio.ItemNotFoundError as e:
             pass # expected exception
         except:
             raise
 
         # others still exist
-        item3 = client.get_item("i3")
-        self.assertEqual(item3, {"pio_iid": "i3", "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 } )
+        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("i3", item3_itypes, item3)
-        updated_item3 = client.get_item("i3")
-        self.assertEqual(updated_item3, {"pio_iid": "i3", "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.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)
 
@@ -173,6 +198,28 @@
         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()