Repository: Backblaze/b2-sdk-python

Issue:
should bucket.update() let me set my bucket_info to {}?

Hi --

I'd like to set the bucket info on my bucket to not have any entries.  To do that, I tried calling bucket.set_info() and got an assert.  Here's a simple failing case:

> b2_api = B2Api(InMemoryAccountInfo())
> b2_api.authorize_account(url, app_key_id, app_key)
> bucket = b2_api.get_bucket_by_name("myBucket"))
> 
> bucket.set_info({})

This triggers an assert on line 558, in update_bucket:
> assert bucket_info or bucket_type

I'm running on macOS 10.15.7.
I'm using Python 3.8.6 installed with mac ports.
i have the following b2 things installed with pip3:

> $ pip3 list | grep b2
> b2        2.0.2
> b2sdk  1.1.4

Here's the stack trace that's generated: [stack.txt](https://github.com/Backblaze/b2-sdk-python/files/5337715/stack.txt)

A similar thing happens when I use bucket.update(bucket_info={}), which ends up on the same place.  I'm currently working around it by passing the bucket's type back in:
>    bucket.update(bucket_type=bucket.as_dict()['bucketType'], bucket_info={}, lifecycle_rules=[])                                                                            

Comments:

Patch:
diff --git a/.readthedocs.yml b/.readthedocs.yml
index b4278123..7a1f282a 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -14,7 +14,7 @@ formats: all
 
 # Optionally set the version of Python and requirements required to build your docs
 python:
-  version: 3.9
+  version: 3.8
   install:
     - requirements: requirements.txt
     - method: pip
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d0fd4e5..7a2a1214 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 * Drop Python 2 and Python 3.4 support :tada:
 * Remove `--prefix` from `ls` (it didn't really work, use `folderName` argument)
 
+### Fixed
+* Allow to set an empty bucket info during the update
+
 ## [1.1.4] - 2020-07-15
 
 ### Added
diff --git a/b2sdk/raw_api.py b/b2sdk/raw_api.py
index 6aadbdb7..7c71450a 100644
--- a/b2sdk/raw_api.py
+++ b/b2sdk/raw_api.py
@@ -552,7 +552,7 @@ class B2RawApi(AbstractRawApi):
         lifecycle_rules=None,
         if_revision_is=None
     ):
-        assert bucket_info or bucket_type
+        assert bucket_info is not None or bucket_type is not None
 
         kwargs = {}
         if if_revision_is is not None:
diff --git a/noxfile.py b/noxfile.py
index a24483eb..a9f815f7 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -25,7 +25,7 @@ PY_PATHS = ['b2sdk', 'test', 'noxfile.py', 'setup.py']
 
 REQUIREMENTS_FORMAT = ['yapf==0.27']
 REQUIREMENTS_LINT = ['yapf==0.27', 'pyflakes==2.2.0', 'pytest==6.1.1', 'liccheck==0.4.7']
-REQUIREMENTS_TEST = ['pytest==6.1.1', 'pytest-cov==2.10.1']
+REQUIREMENTS_TEST = ['pytest==6.1.1', 'pytest-cov==2.10.1', 'pytest-mock==3.3.1']
 REQUIREMENTS_BUILD = ['setuptools>=20.2']
 
 nox.options.reuse_existing_virtualenvs = True


Test patch:
diff --git a/test/unit/v0/test_raw_api.py b/test/unit/v0/test_raw_api.py
index 222fae4d..b1a8e03b 100644
--- a/test/unit/v0/test_raw_api.py
+++ b/test/unit/v0/test_raw_api.py
@@ -7,6 +7,7 @@
 # License https://www.backblaze.com/using_b2_code.html
 #
 ######################################################################
+import pytest
 
 from .test_base import TestBase
 
@@ -92,3 +93,27 @@ class TestRawAPIFilenames(TestBase):
         self._should_raise(u'foo/' + 251 * u'x', "segment too long")
         # So a segment of 125 two-byte chars plus one should also fail.
         self._should_raise(u'foo/' + 125 * TWO_BYTE_UNICHR + u'x', "segment too long")
+
+
+class TestUpdateBucket:
+    """Test updating bucket."""
+
+    @pytest.fixture(autouse=True)
+    def init(self, mocker):
+        b2_http = mocker.MagicMock()
+        self.raw_api = B2RawApi(b2_http)
+
+    def test_assertion_raises(self):
+        with pytest.raises(AssertionError):
+            self.raw_api.update_bucket('test', 'account_auth_token', 'account_id', 'bucket_id')
+
+    @pytest.mark.parametrize('bucket_type,bucket_info', ((None, {}), ('allPublic', None)))
+    def test_assertion_not_raises(self, bucket_type, bucket_info):
+        self.raw_api.update_bucket(
+            'test',
+            'account_auth_token',
+            'account_id',
+            'bucket_id',
+            bucket_type=bucket_type,
+            bucket_info=bucket_info
+        )
diff --git a/test/unit/v1/test_raw_api.py b/test/unit/v1/test_raw_api.py
index a0139740..1bf01dee 100644
--- a/test/unit/v1/test_raw_api.py
+++ b/test/unit/v1/test_raw_api.py
@@ -7,6 +7,7 @@
 # License https://www.backblaze.com/using_b2_code.html
 #
 ######################################################################
+import pytest
 
 from .test_base import TestBase
 
@@ -92,3 +93,27 @@ class TestRawAPIFilenames(TestBase):
         self._should_raise(u'foo/' + 251 * u'x', "segment too long")
         # So a segment of 125 two-byte chars plus one should also fail.
         self._should_raise(u'foo/' + 125 * TWO_BYTE_UNICHR + u'x', "segment too long")
+
+
+class TestUpdateBucket:
+    """Test updating bucket."""
+
+    @pytest.fixture(autouse=True)
+    def init(self, mocker):
+        b2_http = mocker.MagicMock()
+        self.raw_api = B2RawApi(b2_http)
+
+    def test_assertion_raises(self):
+        with pytest.raises(AssertionError):
+            self.raw_api.update_bucket('test', 'account_auth_token', 'account_id', 'bucket_id')
+
+    @pytest.mark.parametrize('bucket_type,bucket_info', ((None, {}), ('allPublic', None)))
+    def test_assertion_not_raises(self, bucket_type, bucket_info):
+        self.raw_api.update_bucket(
+            'test',
+            'account_auth_token',
+            'account_id',
+            'bucket_id',
+            bucket_type=bucket_type,
+            bucket_info=bucket_info
+        )