Repository: piskvorky/smart_open
Pull request #68 (2016-03-29 14:04:02)

Support profile_name as a keyword argument.
Allow passing `profile_name` to optionally specify a boto credentials profile.

`aws_access_key_id` and `aws_secret_access_key` are still supported and in fact will take precedence if provided. See http://boto.cloudhackers.com/en/latest/boto_config_tut.html#credentials for auth source precedence.

```python
with smart_open.smart_open('s3://bucket/prefix/', profile_name='name') as infile:
    pass
```


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index add920d..9d54e2f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,7 +1,13 @@
+* 1.4.0, 29th March 2016
+
+  - Allow passing boto `profile_name` to smart_open (PR #68, @robcowie)
+
+
 * 1.3.2, 3rd January 2016
 
   - Bug fix release to enable 'wb+' file mode (PR #50)
 
+
 * 1.3.1, 18th December 2015
 
   - Disable multiprocessing if unavailable. Allows to run on Google Compute Engine. (PR #41, @nikicc)
diff --git a/smart_open/smart_open_lib.py b/smart_open/smart_open_lib.py
index 35c71cd..07dfafa 100644
--- a/smart_open/smart_open_lib.py
+++ b/smart_open/smart_open_lib.py
@@ -118,7 +118,13 @@ def smart_open(uri, mode="rb", **kw):
             # compression, if any, is determined by the filename extension (.gz, .bz2)
             return file_smart_open(parsed_uri.uri_path, mode)
         elif parsed_uri.scheme in ("s3", "s3n"):
-            s3_connection = boto.connect_s3(aws_access_key_id=parsed_uri.access_id, aws_secret_access_key=parsed_uri.access_secret)
+            # For credential order of precedence see
+            # http://boto.cloudhackers.com/en/latest/boto_config_tut.html#credentials
+            s3_connection = boto.connect_s3(
+                aws_access_key_id=parsed_uri.access_id,
+                aws_secret_access_key=parsed_uri.access_secret,
+                profile_name=kw.pop('profile_name', None))
+
             bucket = s3_connection.get_bucket(parsed_uri.bucket_id)
             if mode in ('r', 'rb'):
                 key = bucket.get_key(parsed_uri.key_id)


Test patch:
diff --git a/smart_open/tests/test_smart_open.py b/smart_open/tests/test_smart_open.py
index d1f18e9..911b79c 100644
--- a/smart_open/tests/test_smart_open.py
+++ b/smart_open/tests/test_smart_open.py
@@ -138,12 +138,17 @@ class SmartOpenReadTest(unittest.TestCase):
         # no credentials
         smart_open_object = smart_open.smart_open("s3://mybucket/mykey")
         smart_open_object.__iter__()
-        mock_boto.connect_s3.assert_called_with(aws_access_key_id=None, aws_secret_access_key=None)
+        mock_boto.connect_s3.assert_called_with(aws_access_key_id=None, aws_secret_access_key=None, profile_name=None)
 
         # with credential
         smart_open_object = smart_open.smart_open("s3://access_id:access_secret@mybucket/mykey")
         smart_open_object.__iter__()
-        mock_boto.connect_s3.assert_called_with(aws_access_key_id="access_id", aws_secret_access_key="access_secret")
+        mock_boto.connect_s3.assert_called_with(aws_access_key_id="access_id", aws_secret_access_key="access_secret", profile_name=None)
+
+        # with credential profile
+        smart_open_object = smart_open.smart_open("s3://mybucket/mykey", profile_name="my_credentials")
+        smart_open_object.__iter__()
+        mock_boto.connect_s3.assert_called_with(aws_access_key_id=None, aws_secret_access_key=None, profile_name="my_credentials")
 
         # lookup bucket, key; call s3_iter_lines
         smart_open_object = smart_open.smart_open("s3://access_id:access_secret@mybucket/mykey")
@@ -317,7 +322,7 @@ class SmartOpenTest(unittest.TestCase):
         """Are s3:// open modes passed correctly?"""
         # correct write mode, correct s3 URI
         smart_open.smart_open("s3://mybucket/mykey", "w")
-        mock_boto.connect_s3.assert_called_with(aws_access_key_id=None, aws_secret_access_key=None)
+        mock_boto.connect_s3.assert_called_with(aws_access_key_id=None, aws_secret_access_key=None, profile_name=None)
         mock_boto.connect_s3().lookup.return_value = True
         mock_boto.connect_s3().get_bucket.assert_called_with("mybucket")
         self.assertTrue(mock_write.called)