Repository: incubator-airflow
Updated Branches:
  refs/heads/v1-9-test 8dea4a64e -> d592f891e


[AIRFLOW-1797] S3Hook.load_string didn't work on Python3

With the switch to Boto3 we now need the content
to be bytes, not a
string. On Python2 there is no difference, but for
Python3 this matters.

And since there were no real tests covering the
S3Hook I've added some
basic ones.

Closes #2771 from ashb/AIRFLOW-1797

(cherry picked from commit 28411b1e7eddb3338a329db3e52ee09de3676784)
Signed-off-by: Bolke de Bruin <bo...@xs4all.nl>


Project: http://git-wip-us.apache.org/repos/asf/incubator-airflow/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-airflow/commit/d592f891
Tree: http://git-wip-us.apache.org/repos/asf/incubator-airflow/tree/d592f891
Diff: http://git-wip-us.apache.org/repos/asf/incubator-airflow/diff/d592f891

Branch: refs/heads/v1-9-test
Commit: d592f891e58650472c8fba89bace3cce54a7972b
Parents: 8dea4a6
Author: Ash Berlin-Taylor <ash_git...@firemirror.com>
Authored: Thu Nov 9 20:47:08 2017 +0100
Committer: Bolke de Bruin <bo...@xs4all.nl>
Committed: Thu Nov 9 20:47:34 2017 +0100

----------------------------------------------------------------------
 airflow/hooks/S3_hook.py    |  7 ++--
 tests/core.py               | 20 -----------
 tests/hooks/test_s3_hook.py | 74 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/d592f891/airflow/hooks/S3_hook.py
----------------------------------------------------------------------
diff --git a/airflow/hooks/S3_hook.py b/airflow/hooks/S3_hook.py
index f8052ca..b16566f 100644
--- a/airflow/hooks/S3_hook.py
+++ b/airflow/hooks/S3_hook.py
@@ -15,7 +15,7 @@
 from airflow.exceptions import AirflowException
 from airflow.contrib.hooks.aws_hook import AwsHook
 
-from six import StringIO
+from six import BytesIO
 from urllib.parse import urlparse
 import re
 import fnmatch
@@ -217,7 +217,8 @@ class S3Hook(AwsHook):
                     key, 
                     bucket_name=None,
                     replace=False,
-                    encrypt=False):
+                    encrypt=False,
+                    encoding='utf-8'):
         """
         Loads a string to S3
 
@@ -247,7 +248,7 @@ class S3Hook(AwsHook):
         if encrypt:
             extra_args['ServerSideEncryption'] = "AES256"
         
-        filelike_buffer = StringIO(string_data)
+        filelike_buffer = BytesIO(string_data.encode(encoding))
         
         client = self.get_conn()
         client.upload_fileobj(filelike_buffer, bucket_name, key, 
ExtraArgs=extra_args)

http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/d592f891/tests/core.py
----------------------------------------------------------------------
diff --git a/tests/core.py b/tests/core.py
index c08d63d..0bd0c87 100644
--- a/tests/core.py
+++ b/tests/core.py
@@ -2386,26 +2386,6 @@ class HttpHookTest(unittest.TestCase):
         self.assertEqual(hook.base_url, 'https://localhost')
 
 
-try:
-    from airflow.hooks.S3_hook import S3Hook
-except ImportError:
-    S3Hook = None
-
-
-@unittest.skipIf(S3Hook is None,
-                 "Skipping test because S3Hook is not installed")
-class S3HookTest(unittest.TestCase):
-    def setUp(self):
-        configuration.load_test_config()
-        self.s3_test_url = "s3://test/this/is/not/a-real-key.txt"
-
-    def test_parse_s3_url(self):
-        parsed = S3Hook.parse_s3_url(self.s3_test_url)
-        self.assertEqual(parsed,
-                         ("test", "this/is/not/a-real-key.txt"),
-                         "Incorrect parsing of the s3 url")
-
-
 send_email_test = mock.Mock()
 
 

http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/d592f891/tests/hooks/test_s3_hook.py
----------------------------------------------------------------------
diff --git a/tests/hooks/test_s3_hook.py b/tests/hooks/test_s3_hook.py
new file mode 100644
index 0000000..48c9fde
--- /dev/null
+++ b/tests/hooks/test_s3_hook.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import unittest
+
+from airflow import configuration
+
+try:
+    from airflow.hooks.S3_hook import S3Hook
+except ImportError:
+    S3Hook = None
+
+
+try:
+    import boto3
+    from moto import mock_s3
+except ImportError:
+    mock_s3 = None
+
+
+@unittest.skipIf(S3Hook is None,
+                 "Skipping test because S3Hook is not available")
+@unittest.skipIf(mock_s3 is None,
+                 "Skipping test because moto.mock_s3 is not available")
+class TestS3Hook(unittest.TestCase):
+    def setUp(self):
+        configuration.load_test_config()
+        self.s3_test_url = "s3://test/this/is/not/a-real-key.txt"
+
+    def test_parse_s3_url(self):
+        parsed = S3Hook.parse_s3_url(self.s3_test_url)
+        self.assertEqual(parsed,
+                         ("test", "this/is/not/a-real-key.txt"),
+                         "Incorrect parsing of the s3 url")
+
+    @mock_s3
+    def test_load_string(self):
+        hook = S3Hook(aws_conn_id=None)
+        conn = hook.get_conn()
+        # We need to create the bucket since this is all in Moto's 'virtual'
+        # AWS account
+        conn.create_bucket(Bucket="mybucket")
+
+        hook.load_string(u"Contént", "my_key", "mybucket")
+        body = boto3.resource('s3').Object('mybucket', 
'my_key').get()['Body'].read()
+
+        self.assertEqual(body, b'Cont\xC3\xA9nt')
+
+    @mock_s3
+    def test_read_key(self):
+        hook = S3Hook(aws_conn_id=None)
+        conn = hook.get_conn()
+        # We need to create the bucket since this is all in Moto's 'virtual'
+        # AWS account
+        conn.create_bucket(Bucket='mybucket')
+        conn.put_object(Bucket='mybucket', Key='my_key', 
Body=b'Cont\xC3\xA9nt')
+
+        self.assertEqual(hook.read_key('my_key', 'mybucket'), u'Contént')
+
+
+if __name__ == '__main__':
+    unittest.main()

Reply via email to