Repository: elastic/elasticsearch-py

Issue:
'bytes' object has no attribute 'encode'
    File "/root/.local/share/virtualenvs/celery-jR7fJ1bi/lib/python3.5/site-packages/elasticsearch/client/utils.py", line 32, in _escape
        return value.encode('utf-8')
    AttributeError: 'bytes' object has no attribute 'encode'

It appears that when _escape receives value, which in my case is b'celery-task-meta-c4f1201f-eb7b-41d5-9318-a75a8cfbdaa0', it gets checked against (<class 'str'>, <class 'bytes'>), and therefore passes the test but <class 'bytes'> does not have the **encode** method.

try does not catch as error is AttributeError: 'bytes' object has no attribute 'encode'

    def _escape(value):
        """
        Escape a single value of a URL string or a query parameter. If it is a list
        or tuple, turn it into a comma-separated string first.
        """

        # make sequences into comma-separated stings
        if isinstance(value, (list, tuple)):
            value = ','.join(value)

        # dates and datetimes into isoformat
        elif isinstance(value, (date, datetime)):
            value = value.isoformat()

        # make bools into true/false strings
        elif isinstance(value, bool):
            value = str(value).lower()

        # encode strings to utf-8
        if isinstance(value, string_types):
            try:
                return value.encode('utf-8')
            except UnicodeDecodeError:
                # Python 2 and str, no need to re-encode
                pass

    return str(value)


Comments:

Patch:
diff --git a/elasticsearch/client/utils.py b/elasticsearch/client/utils.py
index 78b005cd..120b4abe 100644
--- a/elasticsearch/client/utils.py
+++ b/elasticsearch/client/utils.py
@@ -26,13 +26,13 @@ def _escape(value):
     elif isinstance(value, bool):
         value = str(value).lower()
 
+    # don't decode bytestrings
+    elif isinstance(value, bytes):
+        return value
+
     # encode strings to utf-8
     if isinstance(value, string_types):
-        try:
-            return value.encode('utf-8')
-        except UnicodeDecodeError:
-            # Python 2 and str, no need to re-encode
-            pass
+        return value.encode('utf-8')
 
     return str(value)
 


Test patch:
diff --git a/test_elasticsearch/test_client/test_utils.py b/test_elasticsearch/test_client/test_utils.py
index 9fd6df4f..66a83293 100644
--- a/test_elasticsearch/test_client/test_utils.py
+++ b/test_elasticsearch/test_client/test_utils.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from elasticsearch.client.utils import _make_path
+from elasticsearch.client.utils import _make_path, _escape
 from elasticsearch.compat import PY2
 
 from ..test_cases import TestCase, SkipTest
@@ -17,3 +17,24 @@ class TestMakePath(TestCase):
         id = "中文".encode('utf-8')
         self.assertEquals('/some-index/type/%E4%B8%AD%E6%96%87', _make_path('some-index', 'type', id))
 
+
+class TestEscape(TestCase):
+    def test_handles_ascii(self):
+        string = "abc123"
+        self.assertEquals(
+            b'abc123',
+            _escape(string)
+        )
+    def test_handles_unicode(self):
+        string = "中文"
+        self.assertEquals(
+            b'\xe4\xb8\xad\xe6\x96\x87',
+            _escape(string)
+        )
+
+    def test_handles_bytestring(self):
+        string = b'celery-task-meta-c4f1201f-eb7b-41d5-9318-a75a8cfbdaa0'
+        self.assertEquals(
+            string,
+            _escape(string)
+        )