Skip to content

Commit 1f1b7a0

Browse files
committed
Make BaseException internals to be set
Fixes #1519 Signed-off-by: Bala.FA <[email protected]>
1 parent 6daf366 commit 1f1b7a0

File tree

2 files changed

+327
-254
lines changed

2 files changed

+327
-254
lines changed

minio/error.py

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
from __future__ import absolute_import, annotations
3232

33-
from dataclasses import dataclass
3433
from typing import Optional, Type, TypeVar
3534
from xml.etree import ElementTree as ET
3635

@@ -80,7 +79,6 @@ def status_code(self) -> int:
8079
A = TypeVar("A", bound="S3Error")
8180

8281

83-
@dataclass(frozen=True)
8482
class S3Error(MinioException):
8583
"""
8684
Raised to indicate that error response is received
@@ -92,24 +90,65 @@ class S3Error(MinioException):
9290
resource: Optional[str]
9391
request_id: Optional[str]
9492
host_id: Optional[str]
95-
bucket_name: Optional[str] = None
96-
object_name: Optional[str] = None
93+
bucket_name: Optional[str]
94+
object_name: Optional[str]
95+
96+
_EXC_MUTABLES = {"__traceback__", "__context__", "__cause__"}
97+
98+
def __init__( # pylint: disable=too-many-positional-arguments
99+
self,
100+
response: BaseHTTPResponse,
101+
code: Optional[str],
102+
message: Optional[str],
103+
resource: Optional[str],
104+
request_id: Optional[str],
105+
host_id: Optional[str],
106+
bucket_name: Optional[str] = None,
107+
object_name: Optional[str] = None,
108+
):
109+
object.__setattr__(self, "response", response)
110+
object.__setattr__(self, "code", code)
111+
object.__setattr__(self, "message", message)
112+
object.__setattr__(self, "resource", resource)
113+
object.__setattr__(self, "request_id", request_id)
114+
object.__setattr__(self, "host_id", host_id)
115+
object.__setattr__(self, "bucket_name", bucket_name)
116+
object.__setattr__(self, "object_name", object_name)
117+
118+
bucket_message = f", bucket_name: {bucket_name}" if bucket_name else ""
119+
object_message = f", object_name: {object_name}" if object_name else ""
97120

98-
def __post_init__(self):
99-
bucket_message = (
100-
(", bucket_name: " + self.bucket_name)
101-
if self.bucket_name else ""
102-
)
103-
object_message = (
104-
(", object_name: " + self.object_name)
105-
if self.object_name else ""
106-
)
107121
super().__init__(
108-
f"S3 operation failed; code: {self.code}, message: {self.message}, "
109-
f"resource: {self.resource}, request_id: {self.request_id}, "
110-
f"host_id: {self.host_id}{bucket_message}{object_message}"
122+
f"S3 operation failed; code: {code}, message: {message}, "
123+
f"resource: {resource}, request_id: {request_id}, "
124+
f"host_id: {host_id}{bucket_message}{object_message}"
111125
)
112126

127+
# freeze after init
128+
object.__setattr__(self, "_is_frozen", True)
129+
130+
def __setattr__(self, name, value):
131+
if name in self._EXC_MUTABLES:
132+
object.__setattr__(self, name, value)
133+
return
134+
if getattr(self, "_is_frozen", False):
135+
raise AttributeError(
136+
f"{self.__class__.__name__} is frozen and "
137+
"does not allow attribute assignment"
138+
)
139+
object.__setattr__(self, name, value)
140+
141+
def __delattr__(self, name):
142+
if name in self._EXC_MUTABLES:
143+
object.__delattr__(self, name)
144+
return
145+
if getattr(self, "_is_frozen", False):
146+
raise AttributeError(
147+
f"{self.__class__.__name__} is frozen and "
148+
"does not allow attribute deletion"
149+
)
150+
object.__delattr__(self, name)
151+
113152
@classmethod
114153
def fromxml(cls: Type[A], response: BaseHTTPResponse) -> A:
115154
"""Create new object with values from XML element."""
@@ -126,7 +165,7 @@ def fromxml(cls: Type[A], response: BaseHTTPResponse) -> A:
126165
)
127166

128167
def copy(self, code: str, message: str) -> S3Error:
129-
"""Make a copy with replace code and message."""
168+
"""Make a copy with replaced code and message."""
130169
return S3Error(
131170
response=self.response,
132171
code=code,
@@ -138,6 +177,40 @@ def copy(self, code: str, message: str) -> S3Error:
138177
object_name=self.object_name,
139178
)
140179

180+
def __repr__(self):
181+
return (
182+
f"S3Error(code={self.code!r}, message={self.message!r}, "
183+
f"resource={self.resource!r}, request_id={self.request_id!r}, "
184+
f"host_id={self.host_id!r}, bucket_name={self.bucket_name!r}, "
185+
f"object_name={self.object_name!r})"
186+
)
187+
188+
def __eq__(self, other):
189+
if not isinstance(other, S3Error):
190+
return NotImplemented
191+
return (
192+
self.code == other.code
193+
and self.message == other.message
194+
and self.resource == other.resource
195+
and self.request_id == other.request_id
196+
and self.host_id == other.host_id
197+
and self.bucket_name == other.bucket_name
198+
and self.object_name == other.object_name
199+
)
200+
201+
def __hash__(self):
202+
return hash(
203+
(
204+
self.code,
205+
self.message,
206+
self.resource,
207+
self.request_id,
208+
self.host_id,
209+
self.bucket_name,
210+
self.object_name,
211+
)
212+
)
213+
141214

142215
class MinioAdminException(Exception):
143216
"""Raised to indicate admin API execution error."""

0 commit comments

Comments
 (0)