diff --git a/Makefile b/Makefile
index 75f512240..fb004cb79 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
check:
@pip install --user --upgrade pylint
- @if python --version | grep -qi 'python 3'; then pylint --reports=no --score=no --disable=R0401 minio/*py; fi
+ @if python --version | grep -qi 'python 3'; then pylint --reports=no --score=no --disable=R0401,R0801 minio/*py; fi
@if python --version | grep -qi 'python 3'; then pylint --reports=no --score=no minio/credentials minio/select tests/functional; fi
@isort --diff --recursive .
diff --git a/docs/API.md b/docs/API.md
index e0a427fd2..08a51345a 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -30,24 +30,24 @@ s3Client = Minio(
)
```
-| Bucket operations | Object operations | Presigned operations | Bucket policy/notification/encryption operations |
-|:----------------------------------------------------------|:--------------------------------------------------|:--------------------------------------------------|:--------------------------------------------------------------------|
-| [`make_bucket`](#make_bucket) | [`get_object`](#get_object) | [`presigned_get_object`](#presigned_get_object) | [`get_bucket_policy`](#get_bucket_policy) |
-| [`list_buckets`](#list_buckets) | [`put_object`](#put_object) | [`presigned_put_object`](#presigned_put_object) | [`set_bucket_policy`](#set_bucket_policy) |
-| [`bucket_exists`](#bucket_exists) | [`copy_object`](#copy_object) | [`presigned_post_policy`](#presigned_post_policy) | [`delete_bucket_policy`](#delete_bucket_policy) |
-| [`remove_bucket`](#remove_bucket) | [`stat_object`](#stat_object) | | [`get_bucket_notification`](#get_bucket_notification) |
-| [`list_objects`](#list_objects) | [`remove_object`](#remove_object) | | [`set_bucket_notification`](#set_bucket_notification) |
-| [`get_bucket_versioning`](#get_bucket_versioning) | [`remove_objects`](#remove_objects) | | [`remove_all_bucket_notification`](#remove_all_bucket_notification) |
-| [`set_bucket_versioning`](#set_bucket_versioning) | [`fput_object`](#fput_object) | | [`listen_bucket_notification`](#listen_bucket_notification) |
-| [`delete_bucket_replication`](#delete_bucket_replication) | [`fget_object`](#fget_object) | | [`get_bucket_encryption`](#get_bucket_encryption) |
-| [`get_bucket_replication`](#get_bucket_replication) | [`select_object_content`](#select_object_content) | | [`remove_all_bucket_notification`](#remove_all_bucket_notification) |
-| [`set_bucket_replication`](#set_bucket_replication) | [`delete_object_tags`](#delete_object_tags) | | [`put_bucket_encryption`](#put_bucket_encryption) |
-| [`delete_bucket_lifecycle`](#delete_bucket_lifecycle) | [`get_object_tags`](#get_object_tags) | | [`delete_bucket_encryption`](#delete_bucket_encryption) |
-| [`get_bucket_lifecycle`](#get_bucket_lifecycle) | [`set_object_tags`](#set_object_tags) | | |
-| [`set_bucket_lifecycle`](#set_bucket_lifecycle) | | | |
-| [`delete_bucket_tags`](#delete_bucket_tags) | | | |
-| [`get_bucket_tags`](#get_bucket_tags) | | | |
-| [`set_bucket_tags`](#set_bucket_tags) | | | |
+| Bucket operations | Object operations | Presigned operations | Bucket policy/notification/encryption operations |
+|:----------------------------------------------------------|:----------------------------------------------------------------|:--------------------------------------------------|:--------------------------------------------------------------------|
+| [`make_bucket`](#make_bucket) | [`get_object`](#get_object) | [`presigned_get_object`](#presigned_get_object) | [`get_bucket_policy`](#get_bucket_policy) |
+| [`list_buckets`](#list_buckets) | [`put_object`](#put_object) | [`presigned_put_object`](#presigned_put_object) | [`set_bucket_policy`](#set_bucket_policy) |
+| [`bucket_exists`](#bucket_exists) | [`copy_object`](#copy_object) | [`presigned_post_policy`](#presigned_post_policy) | [`delete_bucket_policy`](#delete_bucket_policy) |
+| [`remove_bucket`](#remove_bucket) | [`stat_object`](#stat_object) | | [`get_bucket_notification`](#get_bucket_notification) |
+| [`list_objects`](#list_objects) | [`remove_object`](#remove_object) | | [`set_bucket_notification`](#set_bucket_notification) |
+| [`get_bucket_versioning`](#get_bucket_versioning) | [`remove_objects`](#remove_objects) | | [`remove_all_bucket_notification`](#remove_all_bucket_notification) |
+| [`set_bucket_versioning`](#set_bucket_versioning) | [`fput_object`](#fput_object) | | [`listen_bucket_notification`](#listen_bucket_notification) |
+| [`delete_bucket_replication`](#delete_bucket_replication) | [`fget_object`](#fget_object) | | [`get_bucket_encryption`](#get_bucket_encryption) |
+| [`get_bucket_replication`](#get_bucket_replication) | [`select_object_content`](#select_object_content) | | [`remove_all_bucket_notification`](#remove_all_bucket_notification) |
+| [`set_bucket_replication`](#set_bucket_replication) | [`delete_object_tags`](#delete_object_tags) | | [`put_bucket_encryption`](#put_bucket_encryption) |
+| [`delete_bucket_lifecycle`](#delete_bucket_lifecycle) | [`get_object_tags`](#get_object_tags) | | [`delete_bucket_encryption`](#delete_bucket_encryption) |
+| [`get_bucket_lifecycle`](#get_bucket_lifecycle) | [`set_object_tags`](#set_object_tags) | | |
+| [`set_bucket_lifecycle`](#set_bucket_lifecycle) | [`enable_object_legal_hold`](#enable_object_legal_hold) | | |
+| [`delete_bucket_tags`](#delete_bucket_tags) | [`disable_object_legal_hold`](#disable_object_legal_hold) | | |
+| [`get_bucket_tags`](#get_bucket_tags) | [`is_object_legal_hold_enabled`](#is_object_legal_hold_enabled) | | |
+| [`set_bucket_tags`](#set_bucket_tags) | | | |
## 1. Constructor
@@ -1245,6 +1245,66 @@ tags["User"] = "jsmith"
client.set_object_tags("my-bucketname", "my-objectname", tags)
```
+
+
+### enable_object_legal_hold(bucket_name, object_name, version_id=None)
+
+Enable legal hold on an object.
+
+__Parameters__
+
+| Param | Type | Description |
+|:--------------|:------|:---------------------------|
+| `bucket_name` | _str_ | Name of the bucket. |
+| `object_name` | _str_ | Object name in the bucket. |
+| `version_id` | _str_ | Version ID of the object. |
+
+__Example__
+
+```py
+minio.enable_object_legal_hold("my-bucketname", "my-objectname")
+```
+
+
+
+### disable_object_legal_hold(bucket_name, object_name, version_id=None)
+
+Disable legal hold on an object.
+
+__Parameters__
+
+| Param | Type | Description |
+|:--------------|:------|:---------------------------|
+| `bucket_name` | _str_ | Name of the bucket. |
+| `object_name` | _str_ | Object name in the bucket. |
+| `version_id` | _str_ | Version ID of the object. |
+
+__Example__
+
+```py
+minio.disable_object_legal_hold("my-bucketname", "my-objectname")
+```
+
+
+
+### is_object_legal_hold_enabled(bucket_name, object_name, version_id=None)
+
+Returns true if legal hold is enabled on an object.
+
+__Parameters__
+
+| Param | Type | Description |
+|:--------------|:------|:---------------------------|
+| `bucket_name` | _str_ | Name of the bucket. |
+| `object_name` | _str_ | Object name in the bucket. |
+| `version_id` | _str_ | Version ID of the object. |
+
+__Example__
+
+```py
+minio.is_object_legal_hold_enabled("my-bucketname", "my-objectname")
+```
+
## 4. Presigned operations
diff --git a/examples/disable_object_legal_hold.py b/examples/disable_object_legal_hold.py
new file mode 100644
index 000000000..70db87abf
--- /dev/null
+++ b/examples/disable_object_legal_hold.py
@@ -0,0 +1,28 @@
+# -*- 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",
+)
+
+client.disable_object_legal_hold("my-bucketname", "my-objectname")
diff --git a/examples/enable_object_legal_hold.py b/examples/enable_object_legal_hold.py
new file mode 100644
index 000000000..fd77afc70
--- /dev/null
+++ b/examples/enable_object_legal_hold.py
@@ -0,0 +1,28 @@
+# -*- 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",
+)
+
+client.enable_object_legal_hold("my-bucketname", "my-objectname")
diff --git a/examples/is_object_legal_hold_enabled.py b/examples/is_object_legal_hold_enabled.py
new file mode 100644
index 000000000..057f477c2
--- /dev/null
+++ b/examples/is_object_legal_hold_enabled.py
@@ -0,0 +1,28 @@
+# -*- 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",
+)
+
+status = client.is_object_legal_hold_enabled("my-bucketname", "my-objectname")
diff --git a/minio/api.py b/minio/api.py
index 6f5d4995c..274b967a3 100644
--- a/minio/api.py
+++ b/minio/api.py
@@ -53,6 +53,7 @@
is_supported_header, is_valid_notification_config,
is_valid_policy_type, makedirs, md5sum_hash, quote,
read_part_data, sha256_hash, strptime_rfc3339)
+from .legalhold import LegalHold
from .lifecycleconfig import LifecycleConfig
from .parsers import (parse_error_response, parse_get_bucket_notification,
parse_list_buckets, parse_list_multipart_uploads,
@@ -2072,6 +2073,93 @@ def set_object_tags(self, bucket_name, object_name, tags, version_id=None):
query_params=query_params,
)
+ def enable_object_legal_hold(
+ self, bucket_name, object_name, version_id=None,
+ ):
+ """
+ Enable legal hold on an object.
+
+ :param bucket_name: Name of the bucket.
+ :param object_name: Object name in the bucket.
+ :param version_id: Version ID of the object.
+
+ Example::
+ minio.enable_object_legal_hold("my-bucketname", "my-objectname")
+ """
+ check_bucket_name(bucket_name)
+ check_non_empty_string(object_name)
+ body = marshal(LegalHold(True))
+ query_params = {"versionId", version_id} if version_id else {}
+ query_params["legal-hold"] = ""
+ self._execute(
+ "PUT",
+ bucket_name,
+ object_name=object_name,
+ body=body,
+ headers={"Content-MD5": md5sum_hash(body)},
+ query_params=query_params,
+ )
+
+ def disable_object_legal_hold(
+ self, bucket_name, object_name, version_id=None,
+ ):
+ """
+ Disable legal hold on an object.
+
+ :param bucket_name: Name of the bucket.
+ :param object_name: Object name in the bucket.
+ :param version_id: Version ID of the object.
+
+ Example::
+ minio.disable_object_legal_hold("my-bucketname", "my-objectname")
+ """
+ check_bucket_name(bucket_name)
+ check_non_empty_string(object_name)
+ body = marshal(LegalHold(False))
+ query_params = {"versionId", version_id} if version_id else {}
+ query_params["legal-hold"] = ""
+ self._execute(
+ "PUT",
+ bucket_name,
+ object_name=object_name,
+ body=body,
+ headers={"Content-MD5": md5sum_hash(body)},
+ query_params=query_params,
+ )
+
+ def is_object_legal_hold_enabled(
+ self, bucket_name, object_name, version_id=None,
+ ):
+ """
+ Returns true if legal hold is enabled on an object.
+
+ :param bucket_name: Name of the bucket.
+ :param object_name: Object name in the bucket.
+ :param version_id: Version ID of the object.
+
+ Example::
+ status = minio.is_object_legal_hold_enabled(
+ "my-bucketname", "my-objectname",
+ )
+ """
+ check_bucket_name(bucket_name)
+ check_non_empty_string(object_name)
+ query_params = {"versionId", version_id} if version_id else {}
+ query_params["legal-hold"] = ""
+ try:
+ response = self._execute(
+ "GET",
+ bucket_name,
+ object_name=object_name,
+ query_params=query_params,
+ )
+ legal_hold = unmarshal(LegalHold, response.data.decode())
+ return legal_hold.status
+ except S3Error as exc:
+ if exc.code != "NoSuchObjectLockConfiguration":
+ raise
+ return False
+
def _list_objects( # pylint: disable=too-many-arguments,too-many-branches
self,
bucket_name,
diff --git a/minio/legalhold.py b/minio/legalhold.py
new file mode 100644
index 000000000..c5b23315a
--- /dev/null
+++ b/minio/legalhold.py
@@ -0,0 +1,45 @@
+# -*- 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 PutObjectLegalHold and GetObjectLegalHold S3 APIs."""
+
+from __future__ import absolute_import
+
+from .xml import Element, SubElement, findtext
+
+
+class LegalHold:
+ """Legal hold configuration."""
+
+ def __init__(self, status=False):
+ self._status = status
+
+ @property
+ def status(self):
+ """Get status."""
+ return self._status
+
+ @classmethod
+ def fromxml(cls, element):
+ """Create new object with values from XML element."""
+ status = findtext(element, "Status")
+ return cls(status == "ON")
+
+ def toxml(self, element):
+ """Convert to XML."""
+ element = Element("LegalHold")
+ SubElement(element, "Status", "ON" if self._status is True else "OFF")
+ return element
diff --git a/tests/unit/legelhold.py b/tests/unit/legelhold.py
new file mode 100644
index 000000000..9382a4976
--- /dev/null
+++ b/tests/unit/legelhold.py
@@ -0,0 +1,37 @@
+# -*- 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.legalhold import LegalHold
+
+
+class LegalHoldTest(TestCase):
+ def test_status(self):
+ config = LegalHold(True)
+ xml.marshal(config)
+
+ config = xml.unmarshal(
+ LegalHold,
+ """
+ OFF
+""",
+ )
+ xml.marshal(config)
+ eq_(config.status, False)