Repository: piskvorky/smart_open (python)

Issue:
Get rid of file mode restrictions
Currently `smart_open` is limiting file modes to `('r', 'rb', 'w', 'wb')` [here](https://github.com/piskvorky/smart_open/blob/master/smart_open/smart_open_lib.py#L110).

I don't think there is any reason for this -- remove the restriction and allow whatever mode is supported by the underlying storage ([such as `wb+`](http://stackoverflow.com/questions/34396300/gensim-installation-on-yosemite-using-anaconda)).

Pull request description:
Fix #49 Mode restriction applied only to the relevant types.
Mode restriction only applied to S3, HDFS, WebHDFS modes

@piskvorky @gojomo Would appreciate your opinion before bug fix release in 8 hours.

Patch:
diff --git a/smart_open/smart_open_lib.py b/smart_open/smart_open_lib.py
index efb3427..35c71cd 100644
--- a/smart_open/smart_open_lib.py
+++ b/smart_open/smart_open_lib.py
@@ -107,8 +107,6 @@ def smart_open(uri, mode="rb", **kw):
     # validate mode parameter
     if not isinstance(mode, six.string_types):
         raise TypeError('mode should be a string')
-    if not mode in ('r', 'rb', 'w', 'wb'):
-        raise NotImplementedError('unknown file mode %s' % mode)
 
     if isinstance(uri, six.string_types):
         # this method just routes the request to classes handling the specific storage
@@ -127,21 +125,26 @@ def smart_open(uri, mode="rb", **kw):
                 if key is None:
                     raise KeyError(parsed_uri.key_id)
                 return S3OpenRead(key, **kw)
-            else:
+            elif mode in ('w', 'wb'):
                 key = bucket.get_key(parsed_uri.key_id, validate=False)
                 if key is None:
                     raise KeyError(parsed_uri.key_id)
                 return S3OpenWrite(key, **kw)
+            else:
+                raise NotImplementedError("file mode %s not supported for %r scheme", mode, parsed_uri.scheme)
+
         elif parsed_uri.scheme in ("hdfs", ):
             if mode in ('r', 'rb'):
                 return HdfsOpenRead(parsed_uri, **kw)
             else:
-                raise NotImplementedError("write mode not supported for %r scheme", parsed_uri.scheme)
+                raise NotImplementedError("file mode %s not supported for %r scheme", mode, parsed_uri.scheme)
         elif parsed_uri.scheme in ("webhdfs", ):
             if mode in ('r', 'rb'):
                 return WebHdfsOpenRead(parsed_uri, **kw)
-            else:
+            elif mode in ('w', 'wb'):
                 return WebHdfsOpenWrite(parsed_uri, **kw)
+            else:
+                raise NotImplementedError("file mode %s not supported for %r scheme", mode, parsed_uri.scheme)
         else:
             raise NotImplementedError("scheme %r is not supported", parsed_uri.scheme)
     elif isinstance(uri, boto.s3.key.Key):


Test patch:
diff --git a/smart_open/tests/test_smart_open.py b/smart_open/tests/test_smart_open.py
index b0338d0..d1f18e9 100644
--- a/smart_open/tests/test_smart_open.py
+++ b/smart_open/tests/test_smart_open.py
@@ -293,7 +293,9 @@ class SmartOpenTest(unittest.TestCase):
 
         # correct write modes, incorrect scheme
         self.assertRaises(NotImplementedError, smart_open.smart_open, "hdfs:///blah.txt", "wb")
+        self.assertRaises(NotImplementedError, smart_open.smart_open, "hdfs:///blah.txt", "wb+")
         self.assertRaises(NotImplementedError, smart_open.smart_open, "http:///blah.txt", "w")
+        self.assertRaises(NotImplementedError, smart_open.smart_open, "s3://bucket/key", "wb+")
 
         # correct write mode, correct file:// URI
         smart_open.smart_open("blah", "w")
@@ -302,6 +304,13 @@ class SmartOpenTest(unittest.TestCase):
         smart_open.smart_open("file:///some/file.txt", "wb")
         mock_file.assert_called_with("/some/file.txt", "wb")
 
+        smart_open.smart_open("file:///some/file.txt", "wb+")
+        mock_file.assert_called_with("/some/file.txt", "wb+")
+
+        smart_open.smart_open("file:///some/file.txt", "w+")
+        mock_file.assert_called_with("/some/file.txt", "w+")
+
+
     @mock.patch('smart_open.smart_open_lib.boto')
     @mock.patch('smart_open.smart_open_lib.S3OpenWrite')
     def test_s3_mode_mock(self, mock_write, mock_boto):