We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error message 'NoSuchBucket' not matched via stringification
The following test uses `str(e)` to find out whether the string contains 'NoSuchBucket', however (at least in boto 2.49.0) that message is contained in `e.message` and the default error stringification (inherited form BotoServerError):
https://github.com/boto/boto/blob/91ba037e54ef521c379263b0ac769c66182527d7/boto/exception.py#L154-L156
does not include that instance attribute. 

https://github.com/rucio/rucio/blob/59af37a14e343f938131863969d130f5336e4a28/lib/rucio/rse/protocols/s3boto.py#L247

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/rucio/rse/protocols/s3boto.py`
Content:
```
1 # Copyright European Organization for Nuclear Research (CERN)
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # You may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 # http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Authors:
9 # - Wen Guan, <wen.guan@cern.ch>, 2014-2017
10 # - Vincent Garonne, <vincent.garonne@cern.ch>, 2016-2017
11 # - Nicolo Magini, <nicolo.magini@cern.ch>, 2018
12 # - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019
13 #
14 # PY3K COMPATIBLE
15 
16 import os
17 try:
18     # PY2
19     import urlparse
20 except ImportError:
21     # PY3
22     import urllib.parse as urlparse
23 import logging
24 
25 import boto
26 from boto import connect_s3
27 from boto.s3.connection import OrdinaryCallingFormat
28 from boto.s3.key import Key
29 
30 from rucio.common import exception
31 from rucio.common.config import get_rse_credentials
32 
33 from rucio.rse.protocols import protocol
34 
35 logging.getLogger('boto').setLevel(logging.INFO)
36 
37 
38 class Default(protocol.RSEProtocol):
39     """ Implementing access to RSEs using the S3 protocol."""
40 
41     def __init__(self, protocol_attr, rse_settings, logger=None):
42         super(Default, self).__init__(protocol_attr, rse_settings, logger=logger)
43         if 'determinism_type' in self.attributes:
44             self.attributes['determinism_type'] = 's3'
45         self.__conn = None
46         self.renaming = False
47         self.overwrite = True
48         self.http_proxy = os.environ.get("http_proxy")
49         self.https_proxy = os.environ.get("https_proxy")
50 
51     def _disable_http_proxy(self):
52         """
53            Disable http and https proxy if exists.
54         """
55         if self.http_proxy:
56             del os.environ['http_proxy']
57         if self.https_proxy:
58             del os.environ['https_proxy']
59 
60     def _reset_http_proxy(self):
61         """
62            Reset http and https proxy if exists.
63         """
64         if self.http_proxy:
65             os.environ['http_proxy'] = self.http_proxy
66         if self.https_proxy:
67             os.environ['https_proxy'] = self.https_proxy
68 
69     def get_bucket_key_name(self, pfn):
70         """
71             Gets boto key for a pfn
72 
73             :param pfn: Physical file name
74 
75             :returns: bucket name and key name as string
76         """
77         try:
78             parsed = urlparse.urlparse(pfn)
79             hash_path = parsed.path.strip("/")
80 
81             pos = hash_path.index("/")
82             bucket_name = hash_path[:pos]
83             key_name = hash_path[pos + 1:]
84 
85             return bucket_name, key_name
86         except Exception as e:
87             raise exception.RucioException(str(e))
88 
89     def get_bucket_key(self, pfn, create=False, validate=True):
90         """
91             Gets boto key for a pfn
92 
93             :param pfn: Physical file name
94             :param create: True if needs to create the key, False if not
95 
96             :returns: boto bucket and key object
97         """
98         try:
99             bucket_name, key_name = self.get_bucket_key_name(pfn)
100 
101             if create:
102                 try:
103                     bucket = self.__conn.get_bucket(bucket_name, validate=True)
104                 except boto.exception.S3ResponseError as e:
105                     if e.status == 404:   # bucket not found
106                         bucket = self.__conn.create_bucket(bucket_name)
107                     else:
108                         raise e
109                 key = Key(bucket, key_name)
110             else:
111                 bucket = self.__conn.get_bucket(bucket_name, validate=False)
112                 key = bucket.get_key(key_name, validate=validate)
113             return bucket, key
114         except boto.exception.S3ResponseError as e:
115             if e.status == 404:
116                 raise exception.SourceNotFound(str(e))
117             else:
118                 raise exception.ServiceUnavailable(e)
119 
120     def exists(self, path):
121         """
122             Checks if the requested file is known by the referred RSE.
123 
124             :param path: Physical file name
125 
126             :returns: True if the file exists, False if it doesn't
127 
128             :raises SourceNotFound: if the source file was not found on the referred storage.
129         """
130         try:
131             bucket, key = self.get_bucket_key(path)
132             if key:
133                 return True
134             else:
135                 return False
136         except exception.SourceNotFound:
137             return False
138         except Exception as e:
139             raise exception.ServiceUnavailable(e)
140 
141     def connect(self):
142         """
143             Establishes the actual connection to the referred RSE.
144 
145             :param: credentials needed to establish a connection with the stroage.
146 
147             :raises RSEAccessDenied: if no connection could be established.
148         """
149         try:
150             scheme, prefix = self.attributes.get('scheme'), self.attributes.get('prefix')
151             netloc, port = self.attributes['hostname'], self.attributes.get('port', 80)
152             service_url = '%(scheme)s://%(netloc)s:%(port)s' % locals()
153 
154             access_key, secret_key, is_secure = None, None, None
155             if 'S3_ACCESS_KEY' in os.environ:
156                 access_key = os.environ['S3_ACCESS_KEY']
157             if 'S3_SECRET_KEY' in os.environ:
158                 secret_key = os.environ['S3_SECRET_KEY']
159             if 'S3_IS_SECURE' in os.environ:
160                 if str(os.environ['S3_IS_SECURE']).lower() == 'true':
161                     is_secure = True
162                 elif str(os.environ['S3_IS_SECURE']).lower() == 'false':
163                     is_secure = False
164 
165             if is_secure is None or access_key is None or secret_key is None:
166                 credentials = get_rse_credentials()
167                 self.rse['credentials'] = credentials.get(self.rse['rse'])
168 
169                 if not access_key:
170                     access_key = self.rse['credentials']['access_key']
171                 if not secret_key:
172                     secret_key = self.rse['credentials']['secret_key']
173                 if not is_secure:
174                     is_secure = self.rse['credentials'].get('is_secure', {}).\
175                         get(service_url, False)
176 
177             self._disable_http_proxy()
178             self.__conn = connect_s3(host=self.attributes['hostname'],
179                                      port=int(port),
180                                      aws_access_key_id=access_key,
181                                      aws_secret_access_key=secret_key,
182                                      is_secure=is_secure,
183                                      calling_format=OrdinaryCallingFormat())
184             self._reset_http_proxy()
185         except Exception as e:
186             self._reset_http_proxy()
187             raise exception.RSEAccessDenied(e)
188 
189     def close(self):
190         """ Closes the connection to RSE."""
191         pass
192 
193     def get(self, pfn, dest, transfer_timeout=None):
194         """
195             Provides access to files stored inside connected the RSE.
196 
197             :param path: Physical file name of requested file
198             :param dest: Name and path of the files when stored at the client
199             :param transfer_timeout: Transfer timeout (in seconds) - dummy
200 
201             :raises DestinationNotAccessible: if the destination storage was not accessible.
202             :raises ServiceUnavailable: if some generic error occured in the library.
203             :raises SourceNotFound: if the source file was not found on the referred storage.
204          """
205         try:
206             bucket, key = self.get_bucket_key(pfn, validate=False)
207             if key is None:
208                 raise exception.SourceNotFound('Cannot get the source key from S3')
209             key.get_contents_to_filename(dest)
210         except IOError as e:
211             if e.errno == 2:
212                 raise exception.DestinationNotAccessible(e)
213             else:
214                 raise exception.ServiceUnavailable(e)
215         except boto.exception.S3ResponseError as e:
216             if e.status == 404:
217                 raise exception.SourceNotFound(str(e))
218             else:
219                 raise exception.ServiceUnavailable(e)
220         except exception.SourceNotFound as e:
221             raise exception.SourceNotFound(e)
222         except Exception as e:
223             if os.path.exists(dest):
224                 os.remove(dest)
225             raise exception.ServiceUnavailable(e)
226 
227     def put(self, source, target, source_dir=None, transfer_timeout=None):
228         """
229             Allows to store files inside the referred RSE.
230 
231             :param source: path to the source file on the client file system
232             :param target: path to the destination file on the storage
233             :param source_dir: Path where the to be transferred files are stored in the local file system
234             :param transfer_timeout: Transfer timeout (in seconds) - dummy
235 
236             :raises DestinationNotAccessible: if the destination storage was not accessible.
237             :raises ServiceUnavailable: if some generic error occured in the library.
238             :raises SourceNotFound: if the source file was not found on the referred storage.
239         """
240         full_name = source_dir + '/' + source if source_dir else source
241         try:
242             bucket, key = self.get_bucket_key(target, validate=False)
243             if key is None:
244                 raise exception.DestinationNotAccessible('Cannot get the destionation key from S3')
245             key.set_contents_from_filename(full_name)
246         except boto.exception.S3ResponseError as e:
247             if e.status == 404 and 'NoSuchBucket' in str(e):
248                 try:
249                     bucket, key = self.get_bucket_key(target, create=True)
250                     key.set_contents_from_filename(full_name)
251                 except Exception as e:
252                     raise exception.ServiceUnavailable(e)
253             else:
254                 raise exception.ServiceUnavailable(e)
255         except exception.SourceNotFound as e:
256             raise exception.SourceNotFound(e)
257         except Exception as e:
258             if 'No such file' in str(e):
259                 raise exception.SourceNotFound(e)
260             else:
261                 raise exception.ServiceUnavailable(e)
262 
263     def delete(self, pfn):
264         """
265             Deletes a file from the connected RSE.
266 
267             :param path: path to the to be deleted file
268 
269             :raises ServiceUnavailable: if some generic error occured in the library.
270             :raises SourceNotFound: if the source file was not found on the referred storage.
271         """
272         try:
273             bucket, key = self.get_bucket_key(pfn)
274             if key is None:
275                 raise exception.SourceNotFound('Cannot get the key from S3')
276             key.delete()
277         except exception.SourceNotFound as e:
278             raise exception.SourceNotFound(e)
279         except Exception as e:
280             raise exception.ServiceUnavailable(e)
281 
282     def rename(self, pfn, new_pfn):
283         """ Allows to rename a file stored inside the connected RSE.
284 
285             :param path: path to the current file on the storage
286             :param new_path: path to the new file on the storage
287 
288             :raises DestinationNotAccessible: if the destination storage was not accessible.
289             :raises ServiceUnavailable: if some generic error occured in the library.
290             :raises SourceNotFound: if the source file was not found on the referred storage.
291         """
292         try:
293             bucket, key = self.get_bucket_key(pfn)
294             if key is None:
295                 raise exception.SourceNotFound('Cannot get the source key from S3')
296             bucket_name, key_name = self.get_bucket_key_name(new_pfn)
297             key.copy(bucket_name, key_name)
298             key.delete()
299         except exception.SourceNotFound as e:
300             raise exception.SourceNotFound(e)
301         except boto.exception.S3ResponseError as e:
302             if e.status in [404, 403]:
303                 raise exception.DestinationNotAccessible(e)
304             else:
305                 raise exception.ServiceUnavailable(e)
306         except Exception as e:
307             raise exception.ServiceUnavailable(e)
308 
309     def stat(self, pfn):
310         """ Determines the file size in bytes  of the provided file.
311 
312             :param pfn: The PFN the file.
313 
314             :returns: a dict containing the key filesize.
315         """
316         try:
317             bucket, key = self.get_bucket_key(pfn)
318             if key is None:
319                 raise exception.SourceNotFound('Cannot get the key from S3')
320             return {'filesize': int(key.size)}
321         except exception.SourceNotFound as e:
322             raise exception.SourceNotFound(e)
323         except Exception as e:
324             raise exception.ServiceUnavailable(e)
325 
326     def list(self):
327         try:
328             prefix = self.attributes.get('prefix')
329             prefix = prefix.replace('/', '')
330             bucket = self.__conn.get_bucket(prefix, validate=True)
331         except boto.exception.S3ResponseError as e:
332             raise e
333         return bucket.list()
334
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/rucio/rse/protocols/s3boto.py b/lib/rucio/rse/protocols/s3boto.py
--- a/lib/rucio/rse/protocols/s3boto.py
+++ b/lib/rucio/rse/protocols/s3boto.py
@@ -1,17 +1,26 @@
-# Copyright European Organization for Nuclear Research (CERN)
+# -*- coding: utf-8 -*-
+# Copyright 2014-2021 CERN
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
-# You may not use this file except in compliance with 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
 #
-# Authors:
-# - Wen Guan, <wen.guan@cern.ch>, 2014-2017
-# - Vincent Garonne, <vincent.garonne@cern.ch>, 2016-2017
-# - Nicolo Magini, <nicolo.magini@cern.ch>, 2018
-# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019
+#    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.
 #
-# PY3K COMPATIBLE
+# Authors:
+# - Wen Guan <wen.guan@cern.ch>, 2014-2017
+# - Vincent Garonne <vincent.garonne@cern.ch>, 2016-2017
+# - Martin Barisits <martin.barisits@cern.ch>, 2016-2017
+# - Nicolo Magini <nicolo.magini@cern.ch>, 2018
+# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2019
+# - Tomas Javurek <tomas.javurek@cern.ch>, 2020
+# - Mario Lassnig <mario.lassnig@cern.ch>, 2021
 
 import os
 try:
@@ -244,7 +253,7 @@
                 raise exception.DestinationNotAccessible('Cannot get the destionation key from S3')
             key.set_contents_from_filename(full_name)
         except boto.exception.S3ResponseError as e:
-            if e.status == 404 and 'NoSuchBucket' in str(e):
+            if e.status == 404 and 'NoSuchBucket' in e.error_code:
                 try:
                     bucket, key = self.get_bucket_key(target, create=True)
                     key.set_contents_from_filename(full_name)

{"golden_diff": "diff --git a/lib/rucio/rse/protocols/s3boto.py b/lib/rucio/rse/protocols/s3boto.py\n--- a/lib/rucio/rse/protocols/s3boto.py\n+++ b/lib/rucio/rse/protocols/s3boto.py\n@@ -1,17 +1,26 @@\n-# Copyright European Organization for Nuclear Research (CERN)\n+# -*- coding: utf-8 -*-\n+# Copyright 2014-2021 CERN\n #\n # Licensed under the Apache License, Version 2.0 (the \"License\");\n-# You may not use this file except in compliance with the License.\n+# you may not use this file except in compliance with the License.\n # You may obtain a copy of the License at\n-# http://www.apache.org/licenses/LICENSE-2.0\n #\n-# Authors:\n-# - Wen Guan, <wen.guan@cern.ch>, 2014-2017\n-# - Vincent Garonne, <vincent.garonne@cern.ch>, 2016-2017\n-# - Nicolo Magini, <nicolo.magini@cern.ch>, 2018\n-# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019\n+#    http://www.apache.org/licenses/LICENSE-2.0\n+#\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS,\n+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+# See the License for the specific language governing permissions and\n+# limitations under the License.\n #\n-# PY3K COMPATIBLE\n+# Authors:\n+# - Wen Guan <wen.guan@cern.ch>, 2014-2017\n+# - Vincent Garonne <vincent.garonne@cern.ch>, 2016-2017\n+# - Martin Barisits <martin.barisits@cern.ch>, 2016-2017\n+# - Nicolo Magini <nicolo.magini@cern.ch>, 2018\n+# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2019\n+# - Tomas Javurek <tomas.javurek@cern.ch>, 2020\n+# - Mario Lassnig <mario.lassnig@cern.ch>, 2021\n \n import os\n try:\n@@ -244,7 +253,7 @@\n                 raise exception.DestinationNotAccessible('Cannot get the destionation key from S3')\n             key.set_contents_from_filename(full_name)\n         except boto.exception.S3ResponseError as e:\n-            if e.status == 404 and 'NoSuchBucket' in str(e):\n+            if e.status == 404 and 'NoSuchBucket' in e.error_code:\n                 try:\n                     bucket, key = self.get_bucket_key(target, create=True)\n                     key.set_contents_from_filename(full_name)\n", "issue": "Error message 'NoSuchBucket' not matched via stringification\nThe following test uses `str(e)` to find out whether the string contains 'NoSuchBucket', however (at least in boto 2.49.0) that message is contained in `e.message` and the default error stringification (inherited form BotoServerError):\r\nhttps://github.com/boto/boto/blob/91ba037e54ef521c379263b0ac769c66182527d7/boto/exception.py#L154-L156\r\ndoes not include that instance attribute. \r\n\r\nhttps://github.com/rucio/rucio/blob/59af37a14e343f938131863969d130f5336e4a28/lib/rucio/rse/protocols/s3boto.py#L247\n", "before_files": [{"content": "# Copyright European Organization for Nuclear Research (CERN)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# You may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Authors:\n# - Wen Guan, <wen.guan@cern.ch>, 2014-2017\n# - Vincent Garonne, <vincent.garonne@cern.ch>, 2016-2017\n# - Nicolo Magini, <nicolo.magini@cern.ch>, 2018\n# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019\n#\n# PY3K COMPATIBLE\n\nimport os\ntry:\n    # PY2\n    import urlparse\nexcept ImportError:\n    # PY3\n    import urllib.parse as urlparse\nimport logging\n\nimport boto\nfrom boto import connect_s3\nfrom boto.s3.connection import OrdinaryCallingFormat\nfrom boto.s3.key import Key\n\nfrom rucio.common import exception\nfrom rucio.common.config import get_rse_credentials\n\nfrom rucio.rse.protocols import protocol\n\nlogging.getLogger('boto').setLevel(logging.INFO)\n\n\nclass Default(protocol.RSEProtocol):\n    \"\"\" Implementing access to RSEs using the S3 protocol.\"\"\"\n\n    def __init__(self, protocol_attr, rse_settings, logger=None):\n        super(Default, self).__init__(protocol_attr, rse_settings, logger=logger)\n        if 'determinism_type' in self.attributes:\n            self.attributes['determinism_type'] = 's3'\n        self.__conn = None\n        self.renaming = False\n        self.overwrite = True\n        self.http_proxy = os.environ.get(\"http_proxy\")\n        self.https_proxy = os.environ.get(\"https_proxy\")\n\n    def _disable_http_proxy(self):\n        \"\"\"\n           Disable http and https proxy if exists.\n        \"\"\"\n        if self.http_proxy:\n            del os.environ['http_proxy']\n        if self.https_proxy:\n            del os.environ['https_proxy']\n\n    def _reset_http_proxy(self):\n        \"\"\"\n           Reset http and https proxy if exists.\n        \"\"\"\n        if self.http_proxy:\n            os.environ['http_proxy'] = self.http_proxy\n        if self.https_proxy:\n            os.environ['https_proxy'] = self.https_proxy\n\n    def get_bucket_key_name(self, pfn):\n        \"\"\"\n            Gets boto key for a pfn\n\n            :param pfn: Physical file name\n\n            :returns: bucket name and key name as string\n        \"\"\"\n        try:\n            parsed = urlparse.urlparse(pfn)\n            hash_path = parsed.path.strip(\"/\")\n\n            pos = hash_path.index(\"/\")\n            bucket_name = hash_path[:pos]\n            key_name = hash_path[pos + 1:]\n\n            return bucket_name, key_name\n        except Exception as e:\n            raise exception.RucioException(str(e))\n\n    def get_bucket_key(self, pfn, create=False, validate=True):\n        \"\"\"\n            Gets boto key for a pfn\n\n            :param pfn: Physical file name\n            :param create: True if needs to create the key, False if not\n\n            :returns: boto bucket and key object\n        \"\"\"\n        try:\n            bucket_name, key_name = self.get_bucket_key_name(pfn)\n\n            if create:\n                try:\n                    bucket = self.__conn.get_bucket(bucket_name, validate=True)\n                except boto.exception.S3ResponseError as e:\n                    if e.status == 404:   # bucket not found\n                        bucket = self.__conn.create_bucket(bucket_name)\n                    else:\n                        raise e\n                key = Key(bucket, key_name)\n            else:\n                bucket = self.__conn.get_bucket(bucket_name, validate=False)\n                key = bucket.get_key(key_name, validate=validate)\n            return bucket, key\n        except boto.exception.S3ResponseError as e:\n            if e.status == 404:\n                raise exception.SourceNotFound(str(e))\n            else:\n                raise exception.ServiceUnavailable(e)\n\n    def exists(self, path):\n        \"\"\"\n            Checks if the requested file is known by the referred RSE.\n\n            :param path: Physical file name\n\n            :returns: True if the file exists, False if it doesn't\n\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(path)\n            if key:\n                return True\n            else:\n                return False\n        except exception.SourceNotFound:\n            return False\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def connect(self):\n        \"\"\"\n            Establishes the actual connection to the referred RSE.\n\n            :param: credentials needed to establish a connection with the stroage.\n\n            :raises RSEAccessDenied: if no connection could be established.\n        \"\"\"\n        try:\n            scheme, prefix = self.attributes.get('scheme'), self.attributes.get('prefix')\n            netloc, port = self.attributes['hostname'], self.attributes.get('port', 80)\n            service_url = '%(scheme)s://%(netloc)s:%(port)s' % locals()\n\n            access_key, secret_key, is_secure = None, None, None\n            if 'S3_ACCESS_KEY' in os.environ:\n                access_key = os.environ['S3_ACCESS_KEY']\n            if 'S3_SECRET_KEY' in os.environ:\n                secret_key = os.environ['S3_SECRET_KEY']\n            if 'S3_IS_SECURE' in os.environ:\n                if str(os.environ['S3_IS_SECURE']).lower() == 'true':\n                    is_secure = True\n                elif str(os.environ['S3_IS_SECURE']).lower() == 'false':\n                    is_secure = False\n\n            if is_secure is None or access_key is None or secret_key is None:\n                credentials = get_rse_credentials()\n                self.rse['credentials'] = credentials.get(self.rse['rse'])\n\n                if not access_key:\n                    access_key = self.rse['credentials']['access_key']\n                if not secret_key:\n                    secret_key = self.rse['credentials']['secret_key']\n                if not is_secure:\n                    is_secure = self.rse['credentials'].get('is_secure', {}).\\\n                        get(service_url, False)\n\n            self._disable_http_proxy()\n            self.__conn = connect_s3(host=self.attributes['hostname'],\n                                     port=int(port),\n                                     aws_access_key_id=access_key,\n                                     aws_secret_access_key=secret_key,\n                                     is_secure=is_secure,\n                                     calling_format=OrdinaryCallingFormat())\n            self._reset_http_proxy()\n        except Exception as e:\n            self._reset_http_proxy()\n            raise exception.RSEAccessDenied(e)\n\n    def close(self):\n        \"\"\" Closes the connection to RSE.\"\"\"\n        pass\n\n    def get(self, pfn, dest, transfer_timeout=None):\n        \"\"\"\n            Provides access to files stored inside connected the RSE.\n\n            :param path: Physical file name of requested file\n            :param dest: Name and path of the files when stored at the client\n            :param transfer_timeout: Transfer timeout (in seconds) - dummy\n\n            :raises DestinationNotAccessible: if the destination storage was not accessible.\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n         \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn, validate=False)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the source key from S3')\n            key.get_contents_to_filename(dest)\n        except IOError as e:\n            if e.errno == 2:\n                raise exception.DestinationNotAccessible(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n        except boto.exception.S3ResponseError as e:\n            if e.status == 404:\n                raise exception.SourceNotFound(str(e))\n            else:\n                raise exception.ServiceUnavailable(e)\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            if os.path.exists(dest):\n                os.remove(dest)\n            raise exception.ServiceUnavailable(e)\n\n    def put(self, source, target, source_dir=None, transfer_timeout=None):\n        \"\"\"\n            Allows to store files inside the referred RSE.\n\n            :param source: path to the source file on the client file system\n            :param target: path to the destination file on the storage\n            :param source_dir: Path where the to be transferred files are stored in the local file system\n            :param transfer_timeout: Transfer timeout (in seconds) - dummy\n\n            :raises DestinationNotAccessible: if the destination storage was not accessible.\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        full_name = source_dir + '/' + source if source_dir else source\n        try:\n            bucket, key = self.get_bucket_key(target, validate=False)\n            if key is None:\n                raise exception.DestinationNotAccessible('Cannot get the destionation key from S3')\n            key.set_contents_from_filename(full_name)\n        except boto.exception.S3ResponseError as e:\n            if e.status == 404 and 'NoSuchBucket' in str(e):\n                try:\n                    bucket, key = self.get_bucket_key(target, create=True)\n                    key.set_contents_from_filename(full_name)\n                except Exception as e:\n                    raise exception.ServiceUnavailable(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            if 'No such file' in str(e):\n                raise exception.SourceNotFound(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n\n    def delete(self, pfn):\n        \"\"\"\n            Deletes a file from the connected RSE.\n\n            :param path: path to the to be deleted file\n\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the key from S3')\n            key.delete()\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def rename(self, pfn, new_pfn):\n        \"\"\" Allows to rename a file stored inside the connected RSE.\n\n            :param path: path to the current file on the storage\n            :param new_path: path to the new file on the storage\n\n            :raises DestinationNotAccessible: if the destination storage was not accessible.\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the source key from S3')\n            bucket_name, key_name = self.get_bucket_key_name(new_pfn)\n            key.copy(bucket_name, key_name)\n            key.delete()\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except boto.exception.S3ResponseError as e:\n            if e.status in [404, 403]:\n                raise exception.DestinationNotAccessible(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def stat(self, pfn):\n        \"\"\" Determines the file size in bytes  of the provided file.\n\n            :param pfn: The PFN the file.\n\n            :returns: a dict containing the key filesize.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the key from S3')\n            return {'filesize': int(key.size)}\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def list(self):\n        try:\n            prefix = self.attributes.get('prefix')\n            prefix = prefix.replace('/', '')\n            bucket = self.__conn.get_bucket(prefix, validate=True)\n        except boto.exception.S3ResponseError as e:\n            raise e\n        return bucket.list()\n", "path": "lib/rucio/rse/protocols/s3boto.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright 2014-2021 CERN\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Authors:\n# - Wen Guan <wen.guan@cern.ch>, 2014-2017\n# - Vincent Garonne <vincent.garonne@cern.ch>, 2016-2017\n# - Martin Barisits <martin.barisits@cern.ch>, 2016-2017\n# - Nicolo Magini <nicolo.magini@cern.ch>, 2018\n# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2019\n# - Tomas Javurek <tomas.javurek@cern.ch>, 2020\n# - Mario Lassnig <mario.lassnig@cern.ch>, 2021\n\nimport os\ntry:\n    # PY2\n    import urlparse\nexcept ImportError:\n    # PY3\n    import urllib.parse as urlparse\nimport logging\n\nimport boto\nfrom boto import connect_s3\nfrom boto.s3.connection import OrdinaryCallingFormat\nfrom boto.s3.key import Key\n\nfrom rucio.common import exception\nfrom rucio.common.config import get_rse_credentials\n\nfrom rucio.rse.protocols import protocol\n\nlogging.getLogger('boto').setLevel(logging.INFO)\n\n\nclass Default(protocol.RSEProtocol):\n    \"\"\" Implementing access to RSEs using the S3 protocol.\"\"\"\n\n    def __init__(self, protocol_attr, rse_settings, logger=None):\n        super(Default, self).__init__(protocol_attr, rse_settings, logger=logger)\n        if 'determinism_type' in self.attributes:\n            self.attributes['determinism_type'] = 's3'\n        self.__conn = None\n        self.renaming = False\n        self.overwrite = True\n        self.http_proxy = os.environ.get(\"http_proxy\")\n        self.https_proxy = os.environ.get(\"https_proxy\")\n\n    def _disable_http_proxy(self):\n        \"\"\"\n           Disable http and https proxy if exists.\n        \"\"\"\n        if self.http_proxy:\n            del os.environ['http_proxy']\n        if self.https_proxy:\n            del os.environ['https_proxy']\n\n    def _reset_http_proxy(self):\n        \"\"\"\n           Reset http and https proxy if exists.\n        \"\"\"\n        if self.http_proxy:\n            os.environ['http_proxy'] = self.http_proxy\n        if self.https_proxy:\n            os.environ['https_proxy'] = self.https_proxy\n\n    def get_bucket_key_name(self, pfn):\n        \"\"\"\n            Gets boto key for a pfn\n\n            :param pfn: Physical file name\n\n            :returns: bucket name and key name as string\n        \"\"\"\n        try:\n            parsed = urlparse.urlparse(pfn)\n            hash_path = parsed.path.strip(\"/\")\n\n            pos = hash_path.index(\"/\")\n            bucket_name = hash_path[:pos]\n            key_name = hash_path[pos + 1:]\n\n            return bucket_name, key_name\n        except Exception as e:\n            raise exception.RucioException(str(e))\n\n    def get_bucket_key(self, pfn, create=False, validate=True):\n        \"\"\"\n            Gets boto key for a pfn\n\n            :param pfn: Physical file name\n            :param create: True if needs to create the key, False if not\n\n            :returns: boto bucket and key object\n        \"\"\"\n        try:\n            bucket_name, key_name = self.get_bucket_key_name(pfn)\n\n            if create:\n                try:\n                    bucket = self.__conn.get_bucket(bucket_name, validate=True)\n                except boto.exception.S3ResponseError as e:\n                    if e.status == 404:   # bucket not found\n                        bucket = self.__conn.create_bucket(bucket_name)\n                    else:\n                        raise e\n                key = Key(bucket, key_name)\n            else:\n                bucket = self.__conn.get_bucket(bucket_name, validate=False)\n                key = bucket.get_key(key_name, validate=validate)\n            return bucket, key\n        except boto.exception.S3ResponseError as e:\n            if e.status == 404:\n                raise exception.SourceNotFound(str(e))\n            else:\n                raise exception.ServiceUnavailable(e)\n\n    def exists(self, path):\n        \"\"\"\n            Checks if the requested file is known by the referred RSE.\n\n            :param path: Physical file name\n\n            :returns: True if the file exists, False if it doesn't\n\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(path)\n            if key:\n                return True\n            else:\n                return False\n        except exception.SourceNotFound:\n            return False\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def connect(self):\n        \"\"\"\n            Establishes the actual connection to the referred RSE.\n\n            :param: credentials needed to establish a connection with the stroage.\n\n            :raises RSEAccessDenied: if no connection could be established.\n        \"\"\"\n        try:\n            scheme, prefix = self.attributes.get('scheme'), self.attributes.get('prefix')\n            netloc, port = self.attributes['hostname'], self.attributes.get('port', 80)\n            service_url = '%(scheme)s://%(netloc)s:%(port)s' % locals()\n\n            access_key, secret_key, is_secure = None, None, None\n            if 'S3_ACCESS_KEY' in os.environ:\n                access_key = os.environ['S3_ACCESS_KEY']\n            if 'S3_SECRET_KEY' in os.environ:\n                secret_key = os.environ['S3_SECRET_KEY']\n            if 'S3_IS_SECURE' in os.environ:\n                if str(os.environ['S3_IS_SECURE']).lower() == 'true':\n                    is_secure = True\n                elif str(os.environ['S3_IS_SECURE']).lower() == 'false':\n                    is_secure = False\n\n            if is_secure is None or access_key is None or secret_key is None:\n                credentials = get_rse_credentials()\n                self.rse['credentials'] = credentials.get(self.rse['rse'])\n\n                if not access_key:\n                    access_key = self.rse['credentials']['access_key']\n                if not secret_key:\n                    secret_key = self.rse['credentials']['secret_key']\n                if not is_secure:\n                    is_secure = self.rse['credentials'].get('is_secure', {}).\\\n                        get(service_url, False)\n\n            self._disable_http_proxy()\n            self.__conn = connect_s3(host=self.attributes['hostname'],\n                                     port=int(port),\n                                     aws_access_key_id=access_key,\n                                     aws_secret_access_key=secret_key,\n                                     is_secure=is_secure,\n                                     calling_format=OrdinaryCallingFormat())\n            self._reset_http_proxy()\n        except Exception as e:\n            self._reset_http_proxy()\n            raise exception.RSEAccessDenied(e)\n\n    def close(self):\n        \"\"\" Closes the connection to RSE.\"\"\"\n        pass\n\n    def get(self, pfn, dest, transfer_timeout=None):\n        \"\"\"\n            Provides access to files stored inside connected the RSE.\n\n            :param path: Physical file name of requested file\n            :param dest: Name and path of the files when stored at the client\n            :param transfer_timeout: Transfer timeout (in seconds) - dummy\n\n            :raises DestinationNotAccessible: if the destination storage was not accessible.\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n         \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn, validate=False)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the source key from S3')\n            key.get_contents_to_filename(dest)\n        except IOError as e:\n            if e.errno == 2:\n                raise exception.DestinationNotAccessible(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n        except boto.exception.S3ResponseError as e:\n            if e.status == 404:\n                raise exception.SourceNotFound(str(e))\n            else:\n                raise exception.ServiceUnavailable(e)\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            if os.path.exists(dest):\n                os.remove(dest)\n            raise exception.ServiceUnavailable(e)\n\n    def put(self, source, target, source_dir=None, transfer_timeout=None):\n        \"\"\"\n            Allows to store files inside the referred RSE.\n\n            :param source: path to the source file on the client file system\n            :param target: path to the destination file on the storage\n            :param source_dir: Path where the to be transferred files are stored in the local file system\n            :param transfer_timeout: Transfer timeout (in seconds) - dummy\n\n            :raises DestinationNotAccessible: if the destination storage was not accessible.\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        full_name = source_dir + '/' + source if source_dir else source\n        try:\n            bucket, key = self.get_bucket_key(target, validate=False)\n            if key is None:\n                raise exception.DestinationNotAccessible('Cannot get the destionation key from S3')\n            key.set_contents_from_filename(full_name)\n        except boto.exception.S3ResponseError as e:\n            if e.status == 404 and 'NoSuchBucket' in e.error_code:\n                try:\n                    bucket, key = self.get_bucket_key(target, create=True)\n                    key.set_contents_from_filename(full_name)\n                except Exception as e:\n                    raise exception.ServiceUnavailable(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            if 'No such file' in str(e):\n                raise exception.SourceNotFound(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n\n    def delete(self, pfn):\n        \"\"\"\n            Deletes a file from the connected RSE.\n\n            :param path: path to the to be deleted file\n\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the key from S3')\n            key.delete()\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def rename(self, pfn, new_pfn):\n        \"\"\" Allows to rename a file stored inside the connected RSE.\n\n            :param path: path to the current file on the storage\n            :param new_path: path to the new file on the storage\n\n            :raises DestinationNotAccessible: if the destination storage was not accessible.\n            :raises ServiceUnavailable: if some generic error occured in the library.\n            :raises SourceNotFound: if the source file was not found on the referred storage.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the source key from S3')\n            bucket_name, key_name = self.get_bucket_key_name(new_pfn)\n            key.copy(bucket_name, key_name)\n            key.delete()\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except boto.exception.S3ResponseError as e:\n            if e.status in [404, 403]:\n                raise exception.DestinationNotAccessible(e)\n            else:\n                raise exception.ServiceUnavailable(e)\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def stat(self, pfn):\n        \"\"\" Determines the file size in bytes  of the provided file.\n\n            :param pfn: The PFN the file.\n\n            :returns: a dict containing the key filesize.\n        \"\"\"\n        try:\n            bucket, key = self.get_bucket_key(pfn)\n            if key is None:\n                raise exception.SourceNotFound('Cannot get the key from S3')\n            return {'filesize': int(key.size)}\n        except exception.SourceNotFound as e:\n            raise exception.SourceNotFound(e)\n        except Exception as e:\n            raise exception.ServiceUnavailable(e)\n\n    def list(self):\n        try:\n            prefix = self.attributes.get('prefix')\n            prefix = prefix.replace('/', '')\n            bucket = self.__conn.get_bucket(prefix, validate=True)\n        except boto.exception.S3ResponseError as e:\n            raise e\n        return bucket.list()\n", "path": "lib/rucio/rse/protocols/s3boto.py"}]}