We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[bug] authentication error is handled incorrectly in client.rest.uploader_downloader.FileDownloader
When installing a Conan package I've got this error message:
```
tools.get(**self.conan_data["sources"][self.version])
AttributeError: 'NoneType' object has no attribute 'token'
```
Obviously, the message isn't very helpful as it implies that there's a problem with the package recipe (there wasn't).

After some investigation I found out that the error ultimately happens in `client.rest.uploader_downloader.FileDownloader` when the download fails with a 403.
The method does the following check: `auth.token is None`. The issue is, `auth` is an optional parameter. Also it is effectively allowed to be a tuple according to documentation for `tools.get`. So, I assume the check should have been `not auth or not hasattr(auth, "token") or auth.token is None`.

Conan version: 1.21.0


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conans/client/rest/uploader_downloader.py`
Content:
```
1 import os
2 import traceback
3 import time
4 from copy import copy
5 
6 import six
7 
8 from conans.util import progress_bar
9 from conans.client.rest import response_to_str
10 from conans.errors import AuthenticationException, ConanConnectionError, ConanException, \
11     NotFoundException, ForbiddenException, RequestErrorException
12 from conans.util.files import mkdir, sha1sum
13 from conans.util.log import logger
14 from conans.util.tracer import log_download
15 
16 
17 class FileUploader(object):
18 
19     def __init__(self, requester, output, verify, chunk_size=1000):
20         self.chunk_size = chunk_size
21         self.output = output
22         self.requester = requester
23         self.verify = verify
24 
25     def upload(self, url, abs_path, auth=None, dedup=False, retry=None, retry_wait=None,
26                headers=None):
27         retry = retry if retry is not None else self.requester.retry
28         retry = retry if retry is not None else 1
29         retry_wait = retry_wait if retry_wait is not None else self.requester.retry_wait
30         retry_wait = retry_wait if retry_wait is not None else 5
31 
32         # Send always the header with the Sha1
33         headers = copy(headers) or {}
34         headers["X-Checksum-Sha1"] = sha1sum(abs_path)
35         if dedup:
36             dedup_headers = {"X-Checksum-Deploy": "true"}
37             if headers:
38                 dedup_headers.update(headers)
39             response = self.requester.put(url, data="", verify=self.verify, headers=dedup_headers,
40                                           auth=auth)
41             if response.status_code == 400:
42                 raise RequestErrorException(response_to_str(response))
43 
44             if response.status_code == 401:
45                 raise AuthenticationException(response_to_str(response))
46 
47             if response.status_code == 403:
48                 if auth.token is None:
49                     raise AuthenticationException(response_to_str(response))
50                 raise ForbiddenException(response_to_str(response))
51             if response.status_code == 201:  # Artifactory returns 201 if the file is there
52                 return response
53 
54         ret = call_with_retry(self.output, retry, retry_wait, self._upload_file, url,
55                               abs_path=abs_path, headers=headers, auth=auth)
56         return ret
57 
58     def _upload_file(self, url, abs_path,  headers, auth):
59 
60         file_size = os.stat(abs_path).st_size
61         file_name = os.path.basename(abs_path)
62         description = "Uploading {}".format(file_name)
63 
64         def load_in_chunks(_file, size):
65             """Lazy function (generator) to read a file piece by piece.
66             Default chunk size: 1k."""
67             while True:
68                 chunk = _file.read(size)
69                 if not chunk:
70                     break
71                 yield chunk
72 
73         with open(abs_path, mode='rb') as file_handler:
74             progress = progress_bar.Progress(file_size, self.output, description, print_dot=True)
75             chunk_size = 1024
76             data = progress.update(load_in_chunks(file_handler, chunk_size), chunk_size)
77             iterable_to_file = IterableToFileAdapter(data, file_size)
78             try:
79                 response = self.requester.put(url, data=iterable_to_file, verify=self.verify,
80                                               headers=headers, auth=auth)
81 
82                 if response.status_code == 400:
83                     raise RequestErrorException(response_to_str(response))
84 
85                 if response.status_code == 401:
86                     raise AuthenticationException(response_to_str(response))
87 
88                 if response.status_code == 403:
89                     if auth.token is None:
90                         raise AuthenticationException(response_to_str(response))
91                     raise ForbiddenException(response_to_str(response))
92 
93                 response.raise_for_status()  # Raise HTTPError for bad http response status
94 
95             except ConanException:
96                 raise
97             except Exception as exc:
98                 raise ConanException(exc)
99 
100         return response
101 
102 
103 class IterableToFileAdapter(object):
104     def __init__(self, iterable, total_size):
105         self.iterator = iter(iterable)
106         self.total_size = total_size
107 
108     def read(self, size=-1):  # @UnusedVariable
109         return next(self.iterator, b'')
110 
111     def __len__(self):
112         return self.total_size
113 
114     def __iter__(self):
115         return self.iterator.__iter__()
116 
117 
118 class FileDownloader(object):
119 
120     def __init__(self, requester, output, verify, chunk_size=1000):
121         self.chunk_size = chunk_size
122         self.output = output
123         self.requester = requester
124         self.verify = verify
125 
126     def download(self, url, file_path=None, auth=None, retry=None, retry_wait=None, overwrite=False,
127                  headers=None):
128         retry = retry if retry is not None else self.requester.retry
129         retry = retry if retry is not None else 2
130         retry_wait = retry_wait if retry_wait is not None else self.requester.retry_wait
131         retry_wait = retry_wait if retry_wait is not None else 0
132 
133         if file_path and not os.path.isabs(file_path):
134             file_path = os.path.abspath(file_path)
135 
136         if file_path and os.path.exists(file_path):
137             if overwrite:
138                 if self.output:
139                     self.output.warn("file '%s' already exists, overwriting" % file_path)
140             else:
141                 # Should not happen, better to raise, probably we had to remove
142                 # the dest folder before
143                 raise ConanException("Error, the file to download already exists: '%s'" % file_path)
144 
145         return call_with_retry(self.output, retry, retry_wait, self._download_file, url, auth,
146                                headers, file_path)
147 
148     def _download_file(self, url, auth, headers, file_path):
149         t1 = time.time()
150         try:
151             response = self.requester.get(url, stream=True, verify=self.verify, auth=auth,
152                                           headers=headers)
153         except Exception as exc:
154             raise ConanException("Error downloading file %s: '%s'" % (url, exc))
155 
156         if not response.ok:
157             if response.status_code == 404:
158                 raise NotFoundException("Not found: %s" % url)
159             elif response.status_code == 403:
160                 if auth.token is None:
161                     raise AuthenticationException(response_to_str(response))
162                 raise ForbiddenException(response_to_str(response))
163             elif response.status_code == 401:
164                 raise AuthenticationException()
165             raise ConanException("Error %d downloading file %s" % (response.status_code, url))
166 
167         def read_response(size):
168             for chunk in response.iter_content(size):
169                 yield chunk
170 
171         def write_chunks(chunks, path):
172             ret = None
173             downloaded_size = 0
174             if path:
175                 mkdir(os.path.dirname(path))
176                 with open(path, 'wb') as file_handler:
177                     for chunk in chunks:
178                         assert ((six.PY3 and isinstance(chunk, bytes)) or
179                                 (six.PY2 and isinstance(chunk, str)))
180                         file_handler.write(chunk)
181                         downloaded_size += len(chunk)
182             else:
183                 ret_data = bytearray()
184                 for chunk in chunks:
185                     ret_data.extend(chunk)
186                     downloaded_size += len(chunk)
187                 ret = bytes(ret_data)
188             return ret, downloaded_size
189 
190         try:
191             logger.debug("DOWNLOAD: %s" % url)
192             total_length = response.headers.get('content-length') or len(response.content)
193             total_length = int(total_length)
194             description = "Downloading {}".format(os.path.basename(file_path)) if file_path else None
195             progress = progress_bar.Progress(total_length, self.output, description, print_dot=False)
196 
197             chunk_size = 1024 if not file_path else 1024 * 100
198             encoding = response.headers.get('content-encoding')
199             gzip = (encoding == "gzip")
200 
201             written_chunks, total_downloaded_size = write_chunks(
202                 progress.update(read_response(chunk_size), chunk_size),
203                 file_path
204             )
205 
206             response.close()
207             if total_downloaded_size != total_length and not gzip:
208                 raise ConanException("Transfer interrupted before "
209                                      "complete: %s < %s" % (total_downloaded_size, total_length))
210 
211             duration = time.time() - t1
212             log_download(url, duration)
213             return written_chunks
214 
215         except Exception as e:
216             logger.debug(e.__class__)
217             logger.debug(traceback.format_exc())
218             # If this part failed, it means problems with the connection to server
219             raise ConanConnectionError("Download failed, check server, possibly try again\n%s"
220                                        % str(e))
221 
222 
223 def print_progress(output, units, progress=""):
224     if output.is_terminal:
225         output.rewrite_line("[%s%s] %s" % ('=' * units, ' ' * (50 - units), progress))
226 
227 
228 def call_with_retry(out, retry, retry_wait, method, *args, **kwargs):
229     for counter in range(retry + 1):
230         try:
231             return method(*args, **kwargs)
232         except (NotFoundException, ForbiddenException, AuthenticationException,
233                 RequestErrorException):
234             raise
235         except ConanException as exc:
236             if counter == retry:
237                 raise
238             else:
239                 if out:
240                     out.error(exc)
241                     out.info("Waiting %d seconds to retry..." % retry_wait)
242                 time.sleep(retry_wait)
243
```
--- 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/conans/client/rest/uploader_downloader.py b/conans/client/rest/uploader_downloader.py
--- a/conans/client/rest/uploader_downloader.py
+++ b/conans/client/rest/uploader_downloader.py
@@ -45,7 +45,7 @@
                 raise AuthenticationException(response_to_str(response))
 
             if response.status_code == 403:
-                if auth.token is None:
+                if auth is None or auth.token is None:
                     raise AuthenticationException(response_to_str(response))
                 raise ForbiddenException(response_to_str(response))
             if response.status_code == 201:  # Artifactory returns 201 if the file is there
@@ -86,7 +86,7 @@
                     raise AuthenticationException(response_to_str(response))
 
                 if response.status_code == 403:
-                    if auth.token is None:
+                    if auth is None or auth.token is None:
                         raise AuthenticationException(response_to_str(response))
                     raise ForbiddenException(response_to_str(response))
 
@@ -157,7 +157,8 @@
             if response.status_code == 404:
                 raise NotFoundException("Not found: %s" % url)
             elif response.status_code == 403:
-                if auth.token is None:
+                if auth is None or (hasattr(auth, "token") and auth.token is None):
+                    # TODO: This is a bit weird, why this conversion? Need to investigate
                     raise AuthenticationException(response_to_str(response))
                 raise ForbiddenException(response_to_str(response))
             elif response.status_code == 401:

{"golden_diff": "diff --git a/conans/client/rest/uploader_downloader.py b/conans/client/rest/uploader_downloader.py\n--- a/conans/client/rest/uploader_downloader.py\n+++ b/conans/client/rest/uploader_downloader.py\n@@ -45,7 +45,7 @@\n                 raise AuthenticationException(response_to_str(response))\n \n             if response.status_code == 403:\n-                if auth.token is None:\n+                if auth is None or auth.token is None:\n                     raise AuthenticationException(response_to_str(response))\n                 raise ForbiddenException(response_to_str(response))\n             if response.status_code == 201:  # Artifactory returns 201 if the file is there\n@@ -86,7 +86,7 @@\n                     raise AuthenticationException(response_to_str(response))\n \n                 if response.status_code == 403:\n-                    if auth.token is None:\n+                    if auth is None or auth.token is None:\n                         raise AuthenticationException(response_to_str(response))\n                     raise ForbiddenException(response_to_str(response))\n \n@@ -157,7 +157,8 @@\n             if response.status_code == 404:\n                 raise NotFoundException(\"Not found: %s\" % url)\n             elif response.status_code == 403:\n-                if auth.token is None:\n+                if auth is None or (hasattr(auth, \"token\") and auth.token is None):\n+                    # TODO: This is a bit weird, why this conversion? Need to investigate\n                     raise AuthenticationException(response_to_str(response))\n                 raise ForbiddenException(response_to_str(response))\n             elif response.status_code == 401:\n", "issue": "[bug] authentication error is handled incorrectly in client.rest.uploader_downloader.FileDownloader\nWhen installing a Conan package I've got this error message:\r\n```\r\ntools.get(**self.conan_data[\"sources\"][self.version])\r\nAttributeError: 'NoneType' object has no attribute 'token'\r\n```\r\nObviously, the message isn't very helpful as it implies that there's a problem with the package recipe (there wasn't).\r\n\r\nAfter some investigation I found out that the error ultimately happens in `client.rest.uploader_downloader.FileDownloader` when the download fails with a 403.\r\nThe method does the following check: `auth.token is None`. The issue is, `auth` is an optional parameter. Also it is effectively allowed to be a tuple according to documentation for `tools.get`. So, I assume the check should have been `not auth or not hasattr(auth, \"token\") or auth.token is None`.\r\n\r\nConan version: 1.21.0\r\n\n", "before_files": [{"content": "import os\nimport traceback\nimport time\nfrom copy import copy\n\nimport six\n\nfrom conans.util import progress_bar\nfrom conans.client.rest import response_to_str\nfrom conans.errors import AuthenticationException, ConanConnectionError, ConanException, \\\n    NotFoundException, ForbiddenException, RequestErrorException\nfrom conans.util.files import mkdir, sha1sum\nfrom conans.util.log import logger\nfrom conans.util.tracer import log_download\n\n\nclass FileUploader(object):\n\n    def __init__(self, requester, output, verify, chunk_size=1000):\n        self.chunk_size = chunk_size\n        self.output = output\n        self.requester = requester\n        self.verify = verify\n\n    def upload(self, url, abs_path, auth=None, dedup=False, retry=None, retry_wait=None,\n               headers=None):\n        retry = retry if retry is not None else self.requester.retry\n        retry = retry if retry is not None else 1\n        retry_wait = retry_wait if retry_wait is not None else self.requester.retry_wait\n        retry_wait = retry_wait if retry_wait is not None else 5\n\n        # Send always the header with the Sha1\n        headers = copy(headers) or {}\n        headers[\"X-Checksum-Sha1\"] = sha1sum(abs_path)\n        if dedup:\n            dedup_headers = {\"X-Checksum-Deploy\": \"true\"}\n            if headers:\n                dedup_headers.update(headers)\n            response = self.requester.put(url, data=\"\", verify=self.verify, headers=dedup_headers,\n                                          auth=auth)\n            if response.status_code == 400:\n                raise RequestErrorException(response_to_str(response))\n\n            if response.status_code == 401:\n                raise AuthenticationException(response_to_str(response))\n\n            if response.status_code == 403:\n                if auth.token is None:\n                    raise AuthenticationException(response_to_str(response))\n                raise ForbiddenException(response_to_str(response))\n            if response.status_code == 201:  # Artifactory returns 201 if the file is there\n                return response\n\n        ret = call_with_retry(self.output, retry, retry_wait, self._upload_file, url,\n                              abs_path=abs_path, headers=headers, auth=auth)\n        return ret\n\n    def _upload_file(self, url, abs_path,  headers, auth):\n\n        file_size = os.stat(abs_path).st_size\n        file_name = os.path.basename(abs_path)\n        description = \"Uploading {}\".format(file_name)\n\n        def load_in_chunks(_file, size):\n            \"\"\"Lazy function (generator) to read a file piece by piece.\n            Default chunk size: 1k.\"\"\"\n            while True:\n                chunk = _file.read(size)\n                if not chunk:\n                    break\n                yield chunk\n\n        with open(abs_path, mode='rb') as file_handler:\n            progress = progress_bar.Progress(file_size, self.output, description, print_dot=True)\n            chunk_size = 1024\n            data = progress.update(load_in_chunks(file_handler, chunk_size), chunk_size)\n            iterable_to_file = IterableToFileAdapter(data, file_size)\n            try:\n                response = self.requester.put(url, data=iterable_to_file, verify=self.verify,\n                                              headers=headers, auth=auth)\n\n                if response.status_code == 400:\n                    raise RequestErrorException(response_to_str(response))\n\n                if response.status_code == 401:\n                    raise AuthenticationException(response_to_str(response))\n\n                if response.status_code == 403:\n                    if auth.token is None:\n                        raise AuthenticationException(response_to_str(response))\n                    raise ForbiddenException(response_to_str(response))\n\n                response.raise_for_status()  # Raise HTTPError for bad http response status\n\n            except ConanException:\n                raise\n            except Exception as exc:\n                raise ConanException(exc)\n\n        return response\n\n\nclass IterableToFileAdapter(object):\n    def __init__(self, iterable, total_size):\n        self.iterator = iter(iterable)\n        self.total_size = total_size\n\n    def read(self, size=-1):  # @UnusedVariable\n        return next(self.iterator, b'')\n\n    def __len__(self):\n        return self.total_size\n\n    def __iter__(self):\n        return self.iterator.__iter__()\n\n\nclass FileDownloader(object):\n\n    def __init__(self, requester, output, verify, chunk_size=1000):\n        self.chunk_size = chunk_size\n        self.output = output\n        self.requester = requester\n        self.verify = verify\n\n    def download(self, url, file_path=None, auth=None, retry=None, retry_wait=None, overwrite=False,\n                 headers=None):\n        retry = retry if retry is not None else self.requester.retry\n        retry = retry if retry is not None else 2\n        retry_wait = retry_wait if retry_wait is not None else self.requester.retry_wait\n        retry_wait = retry_wait if retry_wait is not None else 0\n\n        if file_path and not os.path.isabs(file_path):\n            file_path = os.path.abspath(file_path)\n\n        if file_path and os.path.exists(file_path):\n            if overwrite:\n                if self.output:\n                    self.output.warn(\"file '%s' already exists, overwriting\" % file_path)\n            else:\n                # Should not happen, better to raise, probably we had to remove\n                # the dest folder before\n                raise ConanException(\"Error, the file to download already exists: '%s'\" % file_path)\n\n        return call_with_retry(self.output, retry, retry_wait, self._download_file, url, auth,\n                               headers, file_path)\n\n    def _download_file(self, url, auth, headers, file_path):\n        t1 = time.time()\n        try:\n            response = self.requester.get(url, stream=True, verify=self.verify, auth=auth,\n                                          headers=headers)\n        except Exception as exc:\n            raise ConanException(\"Error downloading file %s: '%s'\" % (url, exc))\n\n        if not response.ok:\n            if response.status_code == 404:\n                raise NotFoundException(\"Not found: %s\" % url)\n            elif response.status_code == 403:\n                if auth.token is None:\n                    raise AuthenticationException(response_to_str(response))\n                raise ForbiddenException(response_to_str(response))\n            elif response.status_code == 401:\n                raise AuthenticationException()\n            raise ConanException(\"Error %d downloading file %s\" % (response.status_code, url))\n\n        def read_response(size):\n            for chunk in response.iter_content(size):\n                yield chunk\n\n        def write_chunks(chunks, path):\n            ret = None\n            downloaded_size = 0\n            if path:\n                mkdir(os.path.dirname(path))\n                with open(path, 'wb') as file_handler:\n                    for chunk in chunks:\n                        assert ((six.PY3 and isinstance(chunk, bytes)) or\n                                (six.PY2 and isinstance(chunk, str)))\n                        file_handler.write(chunk)\n                        downloaded_size += len(chunk)\n            else:\n                ret_data = bytearray()\n                for chunk in chunks:\n                    ret_data.extend(chunk)\n                    downloaded_size += len(chunk)\n                ret = bytes(ret_data)\n            return ret, downloaded_size\n\n        try:\n            logger.debug(\"DOWNLOAD: %s\" % url)\n            total_length = response.headers.get('content-length') or len(response.content)\n            total_length = int(total_length)\n            description = \"Downloading {}\".format(os.path.basename(file_path)) if file_path else None\n            progress = progress_bar.Progress(total_length, self.output, description, print_dot=False)\n\n            chunk_size = 1024 if not file_path else 1024 * 100\n            encoding = response.headers.get('content-encoding')\n            gzip = (encoding == \"gzip\")\n\n            written_chunks, total_downloaded_size = write_chunks(\n                progress.update(read_response(chunk_size), chunk_size),\n                file_path\n            )\n\n            response.close()\n            if total_downloaded_size != total_length and not gzip:\n                raise ConanException(\"Transfer interrupted before \"\n                                     \"complete: %s < %s\" % (total_downloaded_size, total_length))\n\n            duration = time.time() - t1\n            log_download(url, duration)\n            return written_chunks\n\n        except Exception as e:\n            logger.debug(e.__class__)\n            logger.debug(traceback.format_exc())\n            # If this part failed, it means problems with the connection to server\n            raise ConanConnectionError(\"Download failed, check server, possibly try again\\n%s\"\n                                       % str(e))\n\n\ndef print_progress(output, units, progress=\"\"):\n    if output.is_terminal:\n        output.rewrite_line(\"[%s%s] %s\" % ('=' * units, ' ' * (50 - units), progress))\n\n\ndef call_with_retry(out, retry, retry_wait, method, *args, **kwargs):\n    for counter in range(retry + 1):\n        try:\n            return method(*args, **kwargs)\n        except (NotFoundException, ForbiddenException, AuthenticationException,\n                RequestErrorException):\n            raise\n        except ConanException as exc:\n            if counter == retry:\n                raise\n            else:\n                if out:\n                    out.error(exc)\n                    out.info(\"Waiting %d seconds to retry...\" % retry_wait)\n                time.sleep(retry_wait)\n", "path": "conans/client/rest/uploader_downloader.py"}], "after_files": [{"content": "import os\nimport traceback\nimport time\nfrom copy import copy\n\nimport six\n\nfrom conans.util import progress_bar\nfrom conans.client.rest import response_to_str\nfrom conans.errors import AuthenticationException, ConanConnectionError, ConanException, \\\n    NotFoundException, ForbiddenException, RequestErrorException\nfrom conans.util.files import mkdir, sha1sum\nfrom conans.util.log import logger\nfrom conans.util.tracer import log_download\n\n\nclass FileUploader(object):\n\n    def __init__(self, requester, output, verify, chunk_size=1000):\n        self.chunk_size = chunk_size\n        self.output = output\n        self.requester = requester\n        self.verify = verify\n\n    def upload(self, url, abs_path, auth=None, dedup=False, retry=None, retry_wait=None,\n               headers=None):\n        retry = retry if retry is not None else self.requester.retry\n        retry = retry if retry is not None else 1\n        retry_wait = retry_wait if retry_wait is not None else self.requester.retry_wait\n        retry_wait = retry_wait if retry_wait is not None else 5\n\n        # Send always the header with the Sha1\n        headers = copy(headers) or {}\n        headers[\"X-Checksum-Sha1\"] = sha1sum(abs_path)\n        if dedup:\n            dedup_headers = {\"X-Checksum-Deploy\": \"true\"}\n            if headers:\n                dedup_headers.update(headers)\n            response = self.requester.put(url, data=\"\", verify=self.verify, headers=dedup_headers,\n                                          auth=auth)\n            if response.status_code == 400:\n                raise RequestErrorException(response_to_str(response))\n\n            if response.status_code == 401:\n                raise AuthenticationException(response_to_str(response))\n\n            if response.status_code == 403:\n                if auth is None or auth.token is None:\n                    raise AuthenticationException(response_to_str(response))\n                raise ForbiddenException(response_to_str(response))\n            if response.status_code == 201:  # Artifactory returns 201 if the file is there\n                return response\n\n        ret = call_with_retry(self.output, retry, retry_wait, self._upload_file, url,\n                              abs_path=abs_path, headers=headers, auth=auth)\n        return ret\n\n    def _upload_file(self, url, abs_path,  headers, auth):\n\n        file_size = os.stat(abs_path).st_size\n        file_name = os.path.basename(abs_path)\n        description = \"Uploading {}\".format(file_name)\n\n        def load_in_chunks(_file, size):\n            \"\"\"Lazy function (generator) to read a file piece by piece.\n            Default chunk size: 1k.\"\"\"\n            while True:\n                chunk = _file.read(size)\n                if not chunk:\n                    break\n                yield chunk\n\n        with open(abs_path, mode='rb') as file_handler:\n            progress = progress_bar.Progress(file_size, self.output, description, print_dot=True)\n            chunk_size = 1024\n            data = progress.update(load_in_chunks(file_handler, chunk_size), chunk_size)\n            iterable_to_file = IterableToFileAdapter(data, file_size)\n            try:\n                response = self.requester.put(url, data=iterable_to_file, verify=self.verify,\n                                              headers=headers, auth=auth)\n\n                if response.status_code == 400:\n                    raise RequestErrorException(response_to_str(response))\n\n                if response.status_code == 401:\n                    raise AuthenticationException(response_to_str(response))\n\n                if response.status_code == 403:\n                    if auth is None or auth.token is None:\n                        raise AuthenticationException(response_to_str(response))\n                    raise ForbiddenException(response_to_str(response))\n\n                response.raise_for_status()  # Raise HTTPError for bad http response status\n\n            except ConanException:\n                raise\n            except Exception as exc:\n                raise ConanException(exc)\n\n        return response\n\n\nclass IterableToFileAdapter(object):\n    def __init__(self, iterable, total_size):\n        self.iterator = iter(iterable)\n        self.total_size = total_size\n\n    def read(self, size=-1):  # @UnusedVariable\n        return next(self.iterator, b'')\n\n    def __len__(self):\n        return self.total_size\n\n    def __iter__(self):\n        return self.iterator.__iter__()\n\n\nclass FileDownloader(object):\n\n    def __init__(self, requester, output, verify, chunk_size=1000):\n        self.chunk_size = chunk_size\n        self.output = output\n        self.requester = requester\n        self.verify = verify\n\n    def download(self, url, file_path=None, auth=None, retry=None, retry_wait=None, overwrite=False,\n                 headers=None):\n        retry = retry if retry is not None else self.requester.retry\n        retry = retry if retry is not None else 2\n        retry_wait = retry_wait if retry_wait is not None else self.requester.retry_wait\n        retry_wait = retry_wait if retry_wait is not None else 0\n\n        if file_path and not os.path.isabs(file_path):\n            file_path = os.path.abspath(file_path)\n\n        if file_path and os.path.exists(file_path):\n            if overwrite:\n                if self.output:\n                    self.output.warn(\"file '%s' already exists, overwriting\" % file_path)\n            else:\n                # Should not happen, better to raise, probably we had to remove\n                # the dest folder before\n                raise ConanException(\"Error, the file to download already exists: '%s'\" % file_path)\n\n        return call_with_retry(self.output, retry, retry_wait, self._download_file, url, auth,\n                               headers, file_path)\n\n    def _download_file(self, url, auth, headers, file_path):\n        t1 = time.time()\n        try:\n            response = self.requester.get(url, stream=True, verify=self.verify, auth=auth,\n                                          headers=headers)\n        except Exception as exc:\n            raise ConanException(\"Error downloading file %s: '%s'\" % (url, exc))\n\n        if not response.ok:\n            if response.status_code == 404:\n                raise NotFoundException(\"Not found: %s\" % url)\n            elif response.status_code == 403:\n                if auth is None or (hasattr(auth, \"token\") and auth.token is None):\n                    # TODO: This is a bit weird, why this conversion? Need to investigate\n                    raise AuthenticationException(response_to_str(response))\n                raise ForbiddenException(response_to_str(response))\n            elif response.status_code == 401:\n                raise AuthenticationException()\n            raise ConanException(\"Error %d downloading file %s\" % (response.status_code, url))\n\n        def read_response(size):\n            for chunk in response.iter_content(size):\n                yield chunk\n\n        def write_chunks(chunks, path):\n            ret = None\n            downloaded_size = 0\n            if path:\n                mkdir(os.path.dirname(path))\n                with open(path, 'wb') as file_handler:\n                    for chunk in chunks:\n                        assert ((six.PY3 and isinstance(chunk, bytes)) or\n                                (six.PY2 and isinstance(chunk, str)))\n                        file_handler.write(chunk)\n                        downloaded_size += len(chunk)\n            else:\n                ret_data = bytearray()\n                for chunk in chunks:\n                    ret_data.extend(chunk)\n                    downloaded_size += len(chunk)\n                ret = bytes(ret_data)\n            return ret, downloaded_size\n\n        try:\n            logger.debug(\"DOWNLOAD: %s\" % url)\n            total_length = response.headers.get('content-length') or len(response.content)\n            total_length = int(total_length)\n            description = \"Downloading {}\".format(os.path.basename(file_path)) if file_path else None\n            progress = progress_bar.Progress(total_length, self.output, description, print_dot=False)\n\n            chunk_size = 1024 if not file_path else 1024 * 100\n            encoding = response.headers.get('content-encoding')\n            gzip = (encoding == \"gzip\")\n\n            written_chunks, total_downloaded_size = write_chunks(\n                progress.update(read_response(chunk_size), chunk_size),\n                file_path\n            )\n\n            response.close()\n            if total_downloaded_size != total_length and not gzip:\n                raise ConanException(\"Transfer interrupted before \"\n                                     \"complete: %s < %s\" % (total_downloaded_size, total_length))\n\n            duration = time.time() - t1\n            log_download(url, duration)\n            return written_chunks\n\n        except Exception as e:\n            logger.debug(e.__class__)\n            logger.debug(traceback.format_exc())\n            # If this part failed, it means problems with the connection to server\n            raise ConanConnectionError(\"Download failed, check server, possibly try again\\n%s\"\n                                       % str(e))\n\n\ndef print_progress(output, units, progress=\"\"):\n    if output.is_terminal:\n        output.rewrite_line(\"[%s%s] %s\" % ('=' * units, ' ' * (50 - units), progress))\n\n\ndef call_with_retry(out, retry, retry_wait, method, *args, **kwargs):\n    for counter in range(retry + 1):\n        try:\n            return method(*args, **kwargs)\n        except (NotFoundException, ForbiddenException, AuthenticationException,\n                RequestErrorException):\n            raise\n        except ConanException as exc:\n            if counter == retry:\n                raise\n            else:\n                if out:\n                    out.error(exc)\n                    out.info(\"Waiting %d seconds to retry...\" % retry_wait)\n                time.sleep(retry_wait)\n", "path": "conans/client/rest/uploader_downloader.py"}]}