diff --git a/docs/API.md b/docs/API.md
index 5e434a75a..41234ffa4 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -614,7 +614,7 @@ __Parameters__
__Example__
```py
-minio.set_bucket_versioning("my-bucketname", VersioningConfig("Enabled"))
+minio.set_bucket_versioning("my-bucketname", VersioningConfig(ENABLED))
```
diff --git a/examples/get_bucket_versioning.py b/examples/get_bucket_versioning.py
new file mode 100644
index 000000000..4e4141287
--- /dev/null
+++ b/examples/get_bucket_versioning.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# MinIO Python Library for Amazon S3 Compatible Cloud Storage.
+# Copyright (C) 2020 MinIO, Inc.
+#
+# 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.
+
+# Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY and my-bucketname are
+# dummy values, please replace them with original values.
+
+from minio import Minio
+
+client = Minio(
+ "play.min.io",
+ access_key="Q3AM3UQ867SPQQA43P2F",
+ secret_key="zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
+)
+
+config = client.get_bucket_versioning("my-bucketname")
+print(config.status)
diff --git a/examples/set_bucket_versioning.py b/examples/set_bucket_versioning.py
new file mode 100644
index 000000000..78af5761f
--- /dev/null
+++ b/examples/set_bucket_versioning.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# MinIO Python Library for Amazon S3 Compatible Cloud Storage.
+# Copyright (C) 2020 MinIO, Inc.
+#
+# 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.
+
+# Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY and my-bucketname are
+# dummy values, please replace them with original values.
+
+from minio import Minio
+from minio.commonconfig import ENABLED
+from minio.versioningconfig import VersioningConfig
+
+client = Minio(
+ "play.min.io",
+ access_key="Q3AM3UQ867SPQQA43P2F",
+ secret_key="zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
+)
+
+client.set_bucket_versioning("my-bucketname", VersioningConfig(ENABLED))
diff --git a/minio/__init__.py b/minio/__init__.py
index 290667516..bf0925cfd 100644
--- a/minio/__init__.py
+++ b/minio/__init__.py
@@ -36,6 +36,6 @@
# pylint: disable=unused-import
from .api import Minio
from .copy_conditions import CopyConditions
-from .definitions import Bucket, Object, VersioningConfig
+from .definitions import Bucket, Object
from .error import InvalidResponseError, S3Error, ServerError
from .post_policy import PostPolicy
diff --git a/minio/api.py b/minio/api.py
index b4d91870d..01d4d02bf 100644
--- a/minio/api.py
+++ b/minio/api.py
@@ -58,17 +58,17 @@
parse_list_parts, parse_location_constraint,
parse_multi_delete_response,
parse_multipart_upload_result,
- parse_new_multipart_upload, parse_versioning_config)
+ parse_new_multipart_upload)
from .replicationconfig import ReplicationConfig
from .select import SelectObjectReader
from .signer import (AMZ_DATE_FORMAT, SIGN_V4_ALGORITHM, get_credential_string,
post_presign_v4, presign_v4, sign_v4_s3)
from .sse import SseCustomerKey
from .thread_pool import ThreadPool
+from .versioningconfig import VersioningConfig
from .xml import marshal, unmarshal
from .xml_marshal import (marshal_bucket_notifications,
marshal_complete_multipart,
- marshal_versioning_config,
xml_marshal_bucket_constraint,
xml_marshal_bucket_encryption,
xml_marshal_delete_objects, xml_marshal_select,
@@ -876,11 +876,13 @@ def set_bucket_versioning(self, bucket_name, config):
Example::
minio.set_bucket_versioning(
- "my-bucketname", VersioningConfig("Enabled"),
+ "my-bucketname", VersioningConfig(ENABLED),
)
"""
check_bucket_name(bucket_name)
- body = marshal_versioning_config(config)
+ if not isinstance(config, VersioningConfig):
+ raise ValueError("config must be VersioningConfig type")
+ body = marshal(config)
self._execute(
"PUT",
bucket_name,
@@ -906,8 +908,7 @@ def get_bucket_versioning(self, bucket_name):
bucket_name,
query_params={"versioning": ""},
)
-
- return parse_versioning_config(response.data)
+ return unmarshal(VersioningConfig, response.data.decode())
def fput_object(self, bucket_name, object_name, file_path,
content_type='application/octet-stream',
diff --git a/minio/definitions.py b/minio/definitions.py
index be135c934..af4e142d4 100644
--- a/minio/definitions.py
+++ b/minio/definitions.py
@@ -472,25 +472,3 @@ def __init__(self, root):
self.parts = [
Part(part_element) for part_element in root.findall("Part")
]
-
-
-class VersioningConfig:
- """Bucket versioning configuration."""
-
- def __init__(self, status, mfa_delete=None):
- if status:
- status = status.title()
- if status not in ["", "Enabled", "Suspended"]:
- raise ValueError("status must be empty, Enabled or Suspended.")
- self._status = status
- self._mfa_delete = mfa_delete
-
- @property
- def status(self):
- """Get status."""
- return self._status or "Off"
-
- @property
- def mfa_delete(self):
- """Get MFA delete."""
- return self._mfa_delete
diff --git a/minio/parsers.py b/minio/parsers.py
index 94d3bee7b..1a1da9aed 100644
--- a/minio/parsers.py
+++ b/minio/parsers.py
@@ -31,8 +31,7 @@
from xml.etree.ElementTree import ParseError
from .definitions import (Bucket, CopyObjectResult, ListMultipartUploadsResult,
- ListPartsResult, MultipartUploadResult, Object,
- VersioningConfig)
+ ListPartsResult, MultipartUploadResult, Object)
# minio specific.
from .error import MultiDeleteError, S3Error
from .helpers import RFC3339, RFC3339NANO
@@ -488,12 +487,3 @@ def parse_list_multipart_uploads(data):
def parse_list_parts(data):
"""Parse ListParts API resppnse XML."""
return ListPartsResult(S3Element.fromstring("ListPartsResult", data))
-
-
-def parse_versioning_config(data):
- """Decode XML data into :class:`VersioningConfig `"""
- root = S3Element.fromstring("VersioningConfiguration", data)
- return VersioningConfig(
- root.get_child_text("Status"),
- root.get_child_text("MFADelete"),
- )
diff --git a/minio/versioningconfig.py b/minio/versioningconfig.py
new file mode 100644
index 000000000..488b4cc09
--- /dev/null
+++ b/minio/versioningconfig.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+# MinIO Python Library for Amazon S3 Compatible Cloud Storage, (C)
+# 2020 MinIO, Inc.
+#
+# 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.
+
+"""Request/response of PutBucketVersioning and GetBucketVersioning APIs."""
+
+from __future__ import absolute_import
+
+from .commonconfig import DISABLED, ENABLED
+from .xml import Element, SubElement, findtext
+
+OFF = "Off"
+SUSPENDED = "Suspended"
+
+
+class VersioningConfig:
+ """Versioning configuration."""
+
+ def __init__(self, status=None, mfa_delete=None):
+ if status is not None and status not in [ENABLED, SUSPENDED]:
+ raise ValueError(
+ "status must be {0} or {1}".format(ENABLED, SUSPENDED),
+ )
+ if mfa_delete is not None and mfa_delete not in [ENABLED, DISABLED]:
+ raise ValueError(
+ "MFA delete must be {0} or {1}".format(ENABLED, DISABLED),
+ )
+ self._status = status
+ self._mfa_delete = mfa_delete
+
+ @property
+ def status(self):
+ """Get status."""
+ return self._status or OFF
+
+ @property
+ def mfa_delete(self):
+ """Get MFA delete."""
+ return self._mfa_delete
+
+ @classmethod
+ def fromxml(cls, element):
+ """Create new object with values from XML element."""
+ status = findtext(element, "Status")
+ mfa_delete = findtext(element, "MFADelete")
+ return cls(status, mfa_delete)
+
+ def toxml(self, element):
+ """Convert to XML."""
+ element = Element("VersioningConfiguration")
+ if self._status:
+ SubElement(element, "Status", self._status)
+ if self._mfa_delete:
+ SubElement(element, "MFADelete", self._mfa_delete)
+ return element
diff --git a/minio/xml_marshal.py b/minio/xml_marshal.py
index 969212b87..da922cec4 100644
--- a/minio/xml_marshal.py
+++ b/minio/xml_marshal.py
@@ -409,14 +409,3 @@ def xml_marshal_delete_objects(keys):
SubElement(element, "VersionId", version_id)
return _get_xml_data(root)
-
-
-def marshal_versioning_config(config):
- """Encode versioning configuration to XML bytes."""
- root = Element("VersioningConfiguration", with_namespace=True)
- SubElement(root, "Status", config.status)
- if config.mfa_delete is not None:
- SubElement(
- root, "MFADelete", "Enabled" if config.mfa_delete else "Disabled",
- )
- return _get_xml_data(root)
diff --git a/tests/functional/tests.py b/tests/functional/tests.py
index 91cf0ae69..4a7b3d492 100644
--- a/tests/functional/tests.py
+++ b/tests/functional/tests.py
@@ -38,13 +38,15 @@
import certifi
import urllib3
-from minio import CopyConditions, Minio, PostPolicy, VersioningConfig
+from minio import CopyConditions, Minio, PostPolicy
+from minio.commonconfig import ENABLED
from minio.error import S3Error
from minio.select.helpers import calculate_crc
from minio.select.options import (CSVInput, CSVOutput, InputSerialization,
OutputSerialization, RequestProgress,
SelectObjectOptions)
from minio.sse import SseCustomerKey
+from minio.versioningconfig import VersioningConfig
if sys.version_info[0] == 2:
from datetime import tzinfo # pylint: disable=ungrouped-imports
@@ -808,7 +810,7 @@ def _test_stat_object(log_entry, sse=None, version_check=False):
try:
if version_check:
_CLIENT.set_bucket_versioning(
- bucket_name, VersioningConfig("Enabled"),
+ bucket_name, VersioningConfig(ENABLED),
)
# Put/Upload a streaming object of 1 MiB
reader = LimitedRandomReader(length)
@@ -878,7 +880,7 @@ def _test_remove_object(log_entry, version_check=False):
try:
if version_check:
_CLIENT.set_bucket_versioning(
- bucket_name, VersioningConfig("Enabled"),
+ bucket_name, VersioningConfig(ENABLED),
)
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(length), length,
@@ -919,7 +921,7 @@ def _test_get_object(log_entry, sse=None, version_check=False):
try:
if version_check:
_CLIENT.set_bucket_versioning(
- bucket_name, VersioningConfig("Enabled"),
+ bucket_name, VersioningConfig(ENABLED),
)
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(length),
@@ -972,7 +974,7 @@ def _test_fget_object(log_entry, sse=None, version_check=False):
try:
if version_check:
_CLIENT.set_bucket_versioning(
- bucket_name, VersioningConfig("Enabled"),
+ bucket_name, VersioningConfig(ENABLED),
)
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(length),
@@ -1099,7 +1101,7 @@ def _test_list_objects(log_entry, use_api_v1=False, version_check=False):
try:
if version_check:
_CLIENT.set_bucket_versioning(
- bucket_name, VersioningConfig("Enabled"),
+ bucket_name, VersioningConfig(ENABLED),
)
size = 1 * KB
_, version_id1 = _CLIENT.put_object(
@@ -1479,7 +1481,7 @@ def test_presigned_get_object_version( # pylint: disable=invalid-name
_CLIENT.make_bucket(bucket_name)
version_id = None
try:
- _CLIENT.set_bucket_versioning(bucket_name, VersioningConfig("Enabled"))
+ _CLIENT.set_bucket_versioning(bucket_name, VersioningConfig(ENABLED))
size = 1 * KB
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(size), size,
@@ -1846,7 +1848,7 @@ def _test_remove_objects(log_entry, version_check=False):
try:
if version_check:
_CLIENT.set_bucket_versioning(
- bucket_name, VersioningConfig("Enabled"),
+ bucket_name, VersioningConfig(ENABLED),
)
size = 1 * KB
# Upload some new objects to prepare for multi-object delete test.
diff --git a/tests/unit/versioningconfig.py b/tests/unit/versioningconfig.py
new file mode 100644
index 000000000..0b4b68cce
--- /dev/null
+++ b/tests/unit/versioningconfig.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+# MinIO Python Library for Amazon S3 Compatible Cloud Storage,
+# (C) 2020 MinIO, Inc.
+#
+# 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.
+
+from unittest import TestCase
+
+from nose.tools import eq_
+
+from minio import xml
+from minio.commonconfig import DISABLED, ENABLED
+from minio.versioningconfig import OFF, SUSPENDED, VersioningConfig
+
+
+class VersioningConfigTest(TestCase):
+ def test_config(self):
+ config = VersioningConfig(ENABLED)
+ xml.marshal(config)
+
+ config = xml.unmarshal(
+ VersioningConfig,
+ """
+""",
+ )
+ xml.marshal(config)
+ eq_(config.status, OFF)
+
+ config = xml.unmarshal(
+ VersioningConfig,
+ """
+ Enabled
+""",
+ )
+ xml.marshal(config)
+ eq_(config.status, ENABLED)
+
+ config = xml.unmarshal(
+ VersioningConfig,
+ """
+ Suspended
+ Disabled
+""",
+ )
+ xml.marshal(config)
+ eq_(config.status, SUSPENDED)
+ eq_(config.mfa_delete, DISABLED)