Repository: minio/minio-py

Issue:
Issue setting bucket (and potentially object) tags
I am using minio-py 7.0.0 and receive a MalformedXML S3Error any time I try to set the tags of my bucket. This issue probably occurs with object tags as well, but I have not tested that yet.

The simplified code below follows your API example code but seems to fail. `bucket_name` and `s3Client` client are defined elsewhere in my code and everything is properly imported within my code.

```
tags = Tags.new_bucket_tags()
tags["Project"] = "Project One"
tags["User"] = "jsmith"

try:
    s3Client.set_bucket_tags(bucket_name, tags)
except (ValueError, S3Error) as exc:
    print(str(exc))
```

Here is the error I am receiving:
```S3 operation failed; code: MalformedXML, message: The XML you provided was not well-formed or did not validate against our published schema, resource: None, request_id: ****, host_id: ****```

Discussion:

Patch:
diff --git a/minio/commonconfig.py b/minio/commonconfig.py
index 735dd42..9d43851 100644
--- a/minio/commonconfig.py
+++ b/minio/commonconfig.py
@@ -179,8 +179,6 @@ class Filter:
         )
         if not valid:
             raise ValueError("only one of and, prefix or tag must be provided")
-        if prefix is not None and not prefix:
-            raise ValueError("prefix must not be empty")
         self._and_operator = and_operator
         self._prefix = prefix
         self._tag = tag
diff --git a/minio/tagging.py b/minio/tagging.py
index ac186ab..d666106 100644
--- a/minio/tagging.py
+++ b/minio/tagging.py
@@ -47,6 +47,5 @@ class Tagging:
         """Convert to XML."""
         element = Element("Tagging")
         if self._tags:
-            element = SubElement(element, "TagSet")
-            self._tags.toxml(element)
+            self._tags.toxml(SubElement(element, "TagSet"))
         return element
diff --git a/minio/xml.py b/minio/xml.py
index 438dbb6..f995d2a 100644
--- a/minio/xml.py
+++ b/minio/xml.py
@@ -77,7 +77,7 @@ def findtext(element, name, strict=False):
         if strict:
             raise ValueError("XML element <{0}> not found".format(name))
         return None
-    return element.text
+    return element.text or ""
 
 
 def unmarshal(cls, xmlstring):


Test patch:
diff --git a/tests/unit/lifecycleconfig_test.py b/tests/unit/lifecycleconfig_test.py
index d9ecde8..577ad01 100644
--- a/tests/unit/lifecycleconfig_test.py
+++ b/tests/unit/lifecycleconfig_test.py
@@ -41,6 +41,18 @@ class LifecycleConfigTest(TestCase):
         )
         xml.marshal(config)
 
+        config = LifecycleConfig(
+            [
+                Rule(
+                    ENABLED,
+                    rule_filter=Filter(prefix=""),
+                    rule_id="rule",
+                    expiration=Expiration(days=365),
+                ),
+            ],
+        )
+        xml.marshal(config)
+
         config = xml.unmarshal(
             LifecycleConfig,
             """<LifeCycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
@@ -68,3 +80,20 @@ class LifecycleConfigTest(TestCase):
 </LifeCycleConfiguration>""",
         )
         xml.marshal(config)
+
+        config = xml.unmarshal(
+            LifecycleConfig,
+            """<LifeCycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
+  <Rule>
+    <ID>DeleteAfterBecomingNonCurrent</ID>
+    <Filter>
+       <Prefix></Prefix>
+    </Filter>
+    <Status>Enabled</Status>
+    <NoncurrentVersionExpiration>
+      <NoncurrentDays>100</NoncurrentDays>
+    </NoncurrentVersionExpiration>
+  </Rule>
+</LifeCycleConfiguration>""",
+        )
+        xml.marshal(config)