We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
sage-download-file: Limit number of mirrors contacted
<div id="comment:0"></div>

In particular when `--enable-download-from-upstream-url` is in use.

CC:  @jhpalmieri @dimpase @vbraun @williamstein

Component: **build**

_Issue created by migration from https://trac.sagemath.org/ticket/34411_



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `build/sage_bootstrap/download/mirror_list.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """
3 Access the List of Sage Download Mirrors
4 """
5 
6 #*****************************************************************************
7 #       Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 2 of the License, or
12 # (at your option) any later version.
13 #                  http://www.gnu.org/licenses/
14 #*****************************************************************************
15 
16 import os
17 import contextlib
18 import logging
19 log = logging.getLogger()
20 
21 from sage_bootstrap.compat import urllib, urlparse
22 from sage_bootstrap.env import SAGE_DISTFILES, SAGE_ROOT
23 
24 from fcntl import flock, LOCK_SH, LOCK_EX
25 from errno import ENOLCK
26 
27 
28 def try_lock(fd, operation):
29     """
30     Try flock() but ignore ``ENOLCK`` errors, which could happen if the
31     file system does not support locking.
32     """
33     try:
34         flock(fd, operation)
35     except IOError as e:
36         if e.errno != ENOLCK:
37             raise
38 
39         
40 class MirrorListException(RuntimeError):
41     pass
42         
43 
44 class MirrorList(object):
45 
46     def __init__(self):
47         self.sources = []
48         upstream_d = os.path.join(SAGE_ROOT, '.upstream.d')
49         for fname in sorted(os.listdir(upstream_d)):
50             if '~' in fname or '#' in fname:
51                 # Ignore auto-save and backup files
52                 continue
53             try:
54                 with open(os.path.join(upstream_d, fname), 'r') as f:
55                     for line in f:
56                         line = line.strip()
57                         if line.startswith('#'):
58                             continue
59                         if not line:
60                             continue
61                         line = line.replace('${SAGE_ROOT}', SAGE_ROOT)
62                         line = line.replace('${SAGE_DISTFILES}', SAGE_DISTFILES)
63                         if '${SAGE_SERVER}' in line:
64                             SAGE_SERVER = os.environ.get("SAGE_SERVER", "")
65                             if not SAGE_SERVER:
66                                 continue
67                             line = line.replace('${SAGE_SERVER}', SAGE_SERVER)
68                         if line.endswith('mirror_list'):
69                             cache_filename = os.path.join(SAGE_DISTFILES, line.rpartition('/')[2])
70                             self.sources.append(MirrorList_from_url(line, cache_filename))
71                         else:
72                             self.sources.append([line])
73             except IOError:
74                 # Silently ignore files that do not exist
75                 pass
76 
77     def __iter__(self):
78         """
79         Iterate through the list of mirrors.
80 
81         This is the main entry point into the mirror list. Every
82         script should just use this function to try mirrors in order
83         of preference. This will not just yield the official mirrors,
84         but also urls for packages that are currently being tested.
85         """
86         for source in self.sources:
87             for mirror in source:
88                 yield mirror
89 
90 
91 class MirrorList_from_url(object):
92     
93     MAXAGE = 24*60*60   # seconds
94 
95     def __init__(self, url, filename):
96         self.url = url
97         self.filename = filename
98         self._mirrors = None
99 
100     @property
101     def mirrors(self):
102         if self._mirrors is not None:
103             return self._mirrors
104 
105         try:
106             self.mirrorfile = open(self.filename, 'r+t')
107         except IOError:
108             self.mirrorfile = open(self.filename, 'w+t')
109 
110         with self.mirrorfile:
111             self.mirrorfd = self.mirrorfile.fileno()
112             try_lock(self.mirrorfd, LOCK_SH)  # shared (read) lock
113             if self._must_refresh():
114                 try_lock(self.mirrorfd, LOCK_EX)  # exclusive (write) lock
115                 # Maybe the mirror list file was updated by a different
116                 # process while we waited for the lock?  Check again.
117                 if self._must_refresh():
118                     self._refresh()
119             if self._mirrors is None:
120                 self._mirrors = self._load()
121 
122         return self._mirrors
123 
124     def _load(self, mirror_list=None):
125         """
126         Load and return `mirror_list` (defaults to the one on disk) as
127         a list of strings
128         """
129         if mirror_list is None:
130             try:
131                 self.mirrorfile.seek(0)
132                 mirror_list = self.mirrorfile.read()
133             except IOError:
134                 log.critical('Failed to load the cached mirror list')
135                 return []
136         if mirror_list == '':
137             return []
138         import ast
139         try:
140             return ast.literal_eval(mirror_list)
141         except SyntaxError:
142             log.critical('Downloaded mirror list has syntax error: {0}'.format(mirror_list))
143             return []
144 
145     def _save(self):
146         """
147         Save the mirror list for (short-term) future  use.
148         """
149         self.mirrorfile.seek(0)
150         self.mirrorfile.write(repr(self.mirrors))
151         self.mirrorfile.truncate()
152         self.mirrorfile.flush()
153 
154     def _port_of_mirror(self, mirror):
155         if mirror.startswith('http://'):
156             return 80
157         if mirror.startswith('https://'):
158             return 443
159         if mirror.startswith('ftp://'):
160             return 21
161         # Sensible default (invalid mirror?)
162         return 80
163 
164     def _rank_mirrors(self):
165         """
166         Sort the mirrors by speed, fastest being first
167 
168         This method is used by the YUM fastestmirror plugin
169         """
170         timed_mirrors = []
171         import time, socket
172         log.info('Searching fastest mirror')
173         timeout = socket.getdefaulttimeout()
174         if timeout is None:
175             timeout = 1
176         for mirror in self.mirrors:
177             if not mirror.startswith('http'):
178                 log.debug('we currently can only handle http, got %s', mirror)
179                 continue
180             port = self._port_of_mirror(mirror)
181             mirror_hostname = urlparse.urlsplit(mirror).netloc
182             time_before = time.time()
183             try:
184                 sock = socket.create_connection((mirror_hostname, port), timeout)
185                 sock.close()
186             except (IOError, socket.error, socket.timeout) as err:
187                 log.warning(str(err).strip() + ': ' + mirror)
188                 continue
189             result = time.time() - time_before
190             result_ms = int(1000 * result)
191             log.info(str(result_ms).rjust(5) + 'ms: ' + mirror)
192             timed_mirrors.append((result, mirror))
193         if len(timed_mirrors) == 0:
194             # We cannot reach any mirror directly, most likely firewall issue
195             if 'http_proxy' not in os.environ:
196                 log.error('Could not reach any mirror directly and no proxy set')
197                 raise MirrorListException('Failed to connect to any mirror, probably no internet connection')
198             log.info('Cannot time mirrors via proxy, using default order')
199         else:
200             timed_mirrors.sort()
201             self._mirrors = [m[1] for m in timed_mirrors]
202         log.info('Fastest mirror: ' + self.fastest)
203 
204     def _age(self):
205         """
206         Return the age of the cached mirror list in seconds
207         """
208         import time
209         mtime = os.fstat(self.mirrorfd).st_mtime
210         now = time.mktime(time.localtime())
211         return now - mtime
212 
213     def _must_refresh(self):
214         """
215         Return whether we must download the mirror list.
216 
217         If and only if this method returns ``False`` is it admissible
218         to use the cached mirror list.
219         """
220         if os.fstat(self.mirrorfd).st_size == 0:
221             return True
222         return self._age() > self.MAXAGE
223 
224     def _refresh(self):
225         """
226         Download and rank the mirror list.
227         """
228         log.info('Downloading the Sage mirror list')
229         try:
230             with contextlib.closing(urllib.urlopen(self.url)) as f:
231                 mirror_list = f.read().decode("ascii")
232         except IOError:
233             log.critical('Downloading the mirror list failed, using cached version')
234         else:
235             self._mirrors = self._load(mirror_list)
236             self._rank_mirrors()
237             self._save()
238 
239     def __iter__(self):
240         """
241         Iterate through the list of mirrors.
242 
243         This is the main entry point into the mirror list. Every
244         script should just use this function to try mirrors in order
245         of preference. This will not just yield the official mirrors,
246         but also urls for packages that are currently being tested.
247         """
248         try:
249             yield os.environ['SAGE_SERVER']
250         except KeyError:
251             pass
252         for mirror in self.mirrors:
253             if not mirror.endswith('/'):
254                 mirror += '/'
255             yield mirror + '/'.join(['spkg', 'upstream', '${SPKG}'])
256 
257     @property
258     def fastest(self):
259         return next(iter(self))
260
```
--- 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/build/sage_bootstrap/download/mirror_list.py b/build/sage_bootstrap/download/mirror_list.py
--- a/build/sage_bootstrap/download/mirror_list.py
+++ b/build/sage_bootstrap/download/mirror_list.py
@@ -170,9 +170,7 @@
         timed_mirrors = []
         import time, socket
         log.info('Searching fastest mirror')
-        timeout = socket.getdefaulttimeout()
-        if timeout is None:
-            timeout = 1
+        timeout = 1
         for mirror in self.mirrors:
             if not mirror.startswith('http'):
                 log.debug('we currently can only handle http, got %s', mirror)
@@ -190,6 +188,11 @@
             result_ms = int(1000 * result)
             log.info(str(result_ms).rjust(5) + 'ms: ' + mirror)
             timed_mirrors.append((result, mirror))
+            timed_mirrors.sort()
+            if len(timed_mirrors) >= 5 and timed_mirrors[4][0] < 0.3:
+                # We don't need more than 5 decent mirrors
+                break
+
         if len(timed_mirrors) == 0:
             # We cannot reach any mirror directly, most likely firewall issue
             if 'http_proxy' not in os.environ:
@@ -197,7 +200,6 @@
                 raise MirrorListException('Failed to connect to any mirror, probably no internet connection')
             log.info('Cannot time mirrors via proxy, using default order')
         else:
-            timed_mirrors.sort()
             self._mirrors = [m[1] for m in timed_mirrors]
         log.info('Fastest mirror: ' + self.fastest)

{"golden_diff": "diff --git a/build/sage_bootstrap/download/mirror_list.py b/build/sage_bootstrap/download/mirror_list.py\n--- a/build/sage_bootstrap/download/mirror_list.py\n+++ b/build/sage_bootstrap/download/mirror_list.py\n@@ -170,9 +170,7 @@\n         timed_mirrors = []\n         import time, socket\n         log.info('Searching fastest mirror')\n-        timeout = socket.getdefaulttimeout()\n-        if timeout is None:\n-            timeout = 1\n+        timeout = 1\n         for mirror in self.mirrors:\n             if not mirror.startswith('http'):\n                 log.debug('we currently can only handle http, got %s', mirror)\n@@ -190,6 +188,11 @@\n             result_ms = int(1000 * result)\n             log.info(str(result_ms).rjust(5) + 'ms: ' + mirror)\n             timed_mirrors.append((result, mirror))\n+            timed_mirrors.sort()\n+            if len(timed_mirrors) >= 5 and timed_mirrors[4][0] < 0.3:\n+                # We don't need more than 5 decent mirrors\n+                break\n+\n         if len(timed_mirrors) == 0:\n             # We cannot reach any mirror directly, most likely firewall issue\n             if 'http_proxy' not in os.environ:\n@@ -197,7 +200,6 @@\n                 raise MirrorListException('Failed to connect to any mirror, probably no internet connection')\n             log.info('Cannot time mirrors via proxy, using default order')\n         else:\n-            timed_mirrors.sort()\n             self._mirrors = [m[1] for m in timed_mirrors]\n         log.info('Fastest mirror: ' + self.fastest)\n", "issue": "sage-download-file: Limit number of mirrors contacted\n<div id=\"comment:0\"></div>\n\nIn particular when `--enable-download-from-upstream-url` is in use.\n\nCC:  @jhpalmieri @dimpase @vbraun @williamstein\n\nComponent: **build**\n\n_Issue created by migration from https://trac.sagemath.org/ticket/34411_\n\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"\nAccess the List of Sage Download Mirrors\n\"\"\"\n\n#*****************************************************************************\n#       Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#                  http://www.gnu.org/licenses/\n#*****************************************************************************\n\nimport os\nimport contextlib\nimport logging\nlog = logging.getLogger()\n\nfrom sage_bootstrap.compat import urllib, urlparse\nfrom sage_bootstrap.env import SAGE_DISTFILES, SAGE_ROOT\n\nfrom fcntl import flock, LOCK_SH, LOCK_EX\nfrom errno import ENOLCK\n\n\ndef try_lock(fd, operation):\n    \"\"\"\n    Try flock() but ignore ``ENOLCK`` errors, which could happen if the\n    file system does not support locking.\n    \"\"\"\n    try:\n        flock(fd, operation)\n    except IOError as e:\n        if e.errno != ENOLCK:\n            raise\n\n        \nclass MirrorListException(RuntimeError):\n    pass\n        \n\nclass MirrorList(object):\n\n    def __init__(self):\n        self.sources = []\n        upstream_d = os.path.join(SAGE_ROOT, '.upstream.d')\n        for fname in sorted(os.listdir(upstream_d)):\n            if '~' in fname or '#' in fname:\n                # Ignore auto-save and backup files\n                continue\n            try:\n                with open(os.path.join(upstream_d, fname), 'r') as f:\n                    for line in f:\n                        line = line.strip()\n                        if line.startswith('#'):\n                            continue\n                        if not line:\n                            continue\n                        line = line.replace('${SAGE_ROOT}', SAGE_ROOT)\n                        line = line.replace('${SAGE_DISTFILES}', SAGE_DISTFILES)\n                        if '${SAGE_SERVER}' in line:\n                            SAGE_SERVER = os.environ.get(\"SAGE_SERVER\", \"\")\n                            if not SAGE_SERVER:\n                                continue\n                            line = line.replace('${SAGE_SERVER}', SAGE_SERVER)\n                        if line.endswith('mirror_list'):\n                            cache_filename = os.path.join(SAGE_DISTFILES, line.rpartition('/')[2])\n                            self.sources.append(MirrorList_from_url(line, cache_filename))\n                        else:\n                            self.sources.append([line])\n            except IOError:\n                # Silently ignore files that do not exist\n                pass\n\n    def __iter__(self):\n        \"\"\"\n        Iterate through the list of mirrors.\n\n        This is the main entry point into the mirror list. Every\n        script should just use this function to try mirrors in order\n        of preference. This will not just yield the official mirrors,\n        but also urls for packages that are currently being tested.\n        \"\"\"\n        for source in self.sources:\n            for mirror in source:\n                yield mirror\n\n\nclass MirrorList_from_url(object):\n    \n    MAXAGE = 24*60*60   # seconds\n\n    def __init__(self, url, filename):\n        self.url = url\n        self.filename = filename\n        self._mirrors = None\n\n    @property\n    def mirrors(self):\n        if self._mirrors is not None:\n            return self._mirrors\n\n        try:\n            self.mirrorfile = open(self.filename, 'r+t')\n        except IOError:\n            self.mirrorfile = open(self.filename, 'w+t')\n\n        with self.mirrorfile:\n            self.mirrorfd = self.mirrorfile.fileno()\n            try_lock(self.mirrorfd, LOCK_SH)  # shared (read) lock\n            if self._must_refresh():\n                try_lock(self.mirrorfd, LOCK_EX)  # exclusive (write) lock\n                # Maybe the mirror list file was updated by a different\n                # process while we waited for the lock?  Check again.\n                if self._must_refresh():\n                    self._refresh()\n            if self._mirrors is None:\n                self._mirrors = self._load()\n\n        return self._mirrors\n\n    def _load(self, mirror_list=None):\n        \"\"\"\n        Load and return `mirror_list` (defaults to the one on disk) as\n        a list of strings\n        \"\"\"\n        if mirror_list is None:\n            try:\n                self.mirrorfile.seek(0)\n                mirror_list = self.mirrorfile.read()\n            except IOError:\n                log.critical('Failed to load the cached mirror list')\n                return []\n        if mirror_list == '':\n            return []\n        import ast\n        try:\n            return ast.literal_eval(mirror_list)\n        except SyntaxError:\n            log.critical('Downloaded mirror list has syntax error: {0}'.format(mirror_list))\n            return []\n\n    def _save(self):\n        \"\"\"\n        Save the mirror list for (short-term) future  use.\n        \"\"\"\n        self.mirrorfile.seek(0)\n        self.mirrorfile.write(repr(self.mirrors))\n        self.mirrorfile.truncate()\n        self.mirrorfile.flush()\n\n    def _port_of_mirror(self, mirror):\n        if mirror.startswith('http://'):\n            return 80\n        if mirror.startswith('https://'):\n            return 443\n        if mirror.startswith('ftp://'):\n            return 21\n        # Sensible default (invalid mirror?)\n        return 80\n\n    def _rank_mirrors(self):\n        \"\"\"\n        Sort the mirrors by speed, fastest being first\n\n        This method is used by the YUM fastestmirror plugin\n        \"\"\"\n        timed_mirrors = []\n        import time, socket\n        log.info('Searching fastest mirror')\n        timeout = socket.getdefaulttimeout()\n        if timeout is None:\n            timeout = 1\n        for mirror in self.mirrors:\n            if not mirror.startswith('http'):\n                log.debug('we currently can only handle http, got %s', mirror)\n                continue\n            port = self._port_of_mirror(mirror)\n            mirror_hostname = urlparse.urlsplit(mirror).netloc\n            time_before = time.time()\n            try:\n                sock = socket.create_connection((mirror_hostname, port), timeout)\n                sock.close()\n            except (IOError, socket.error, socket.timeout) as err:\n                log.warning(str(err).strip() + ': ' + mirror)\n                continue\n            result = time.time() - time_before\n            result_ms = int(1000 * result)\n            log.info(str(result_ms).rjust(5) + 'ms: ' + mirror)\n            timed_mirrors.append((result, mirror))\n        if len(timed_mirrors) == 0:\n            # We cannot reach any mirror directly, most likely firewall issue\n            if 'http_proxy' not in os.environ:\n                log.error('Could not reach any mirror directly and no proxy set')\n                raise MirrorListException('Failed to connect to any mirror, probably no internet connection')\n            log.info('Cannot time mirrors via proxy, using default order')\n        else:\n            timed_mirrors.sort()\n            self._mirrors = [m[1] for m in timed_mirrors]\n        log.info('Fastest mirror: ' + self.fastest)\n\n    def _age(self):\n        \"\"\"\n        Return the age of the cached mirror list in seconds\n        \"\"\"\n        import time\n        mtime = os.fstat(self.mirrorfd).st_mtime\n        now = time.mktime(time.localtime())\n        return now - mtime\n\n    def _must_refresh(self):\n        \"\"\"\n        Return whether we must download the mirror list.\n\n        If and only if this method returns ``False`` is it admissible\n        to use the cached mirror list.\n        \"\"\"\n        if os.fstat(self.mirrorfd).st_size == 0:\n            return True\n        return self._age() > self.MAXAGE\n\n    def _refresh(self):\n        \"\"\"\n        Download and rank the mirror list.\n        \"\"\"\n        log.info('Downloading the Sage mirror list')\n        try:\n            with contextlib.closing(urllib.urlopen(self.url)) as f:\n                mirror_list = f.read().decode(\"ascii\")\n        except IOError:\n            log.critical('Downloading the mirror list failed, using cached version')\n        else:\n            self._mirrors = self._load(mirror_list)\n            self._rank_mirrors()\n            self._save()\n\n    def __iter__(self):\n        \"\"\"\n        Iterate through the list of mirrors.\n\n        This is the main entry point into the mirror list. Every\n        script should just use this function to try mirrors in order\n        of preference. This will not just yield the official mirrors,\n        but also urls for packages that are currently being tested.\n        \"\"\"\n        try:\n            yield os.environ['SAGE_SERVER']\n        except KeyError:\n            pass\n        for mirror in self.mirrors:\n            if not mirror.endswith('/'):\n                mirror += '/'\n            yield mirror + '/'.join(['spkg', 'upstream', '${SPKG}'])\n\n    @property\n    def fastest(self):\n        return next(iter(self))\n", "path": "build/sage_bootstrap/download/mirror_list.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"\nAccess the List of Sage Download Mirrors\n\"\"\"\n\n#*****************************************************************************\n#       Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#                  http://www.gnu.org/licenses/\n#*****************************************************************************\n\nimport os\nimport contextlib\nimport logging\nlog = logging.getLogger()\n\nfrom sage_bootstrap.compat import urllib, urlparse\nfrom sage_bootstrap.env import SAGE_DISTFILES, SAGE_ROOT\n\nfrom fcntl import flock, LOCK_SH, LOCK_EX\nfrom errno import ENOLCK\n\n\ndef try_lock(fd, operation):\n    \"\"\"\n    Try flock() but ignore ``ENOLCK`` errors, which could happen if the\n    file system does not support locking.\n    \"\"\"\n    try:\n        flock(fd, operation)\n    except IOError as e:\n        if e.errno != ENOLCK:\n            raise\n\n        \nclass MirrorListException(RuntimeError):\n    pass\n        \n\nclass MirrorList(object):\n\n    def __init__(self):\n        self.sources = []\n        upstream_d = os.path.join(SAGE_ROOT, '.upstream.d')\n        for fname in sorted(os.listdir(upstream_d)):\n            if '~' in fname or '#' in fname:\n                # Ignore auto-save and backup files\n                continue\n            try:\n                with open(os.path.join(upstream_d, fname), 'r') as f:\n                    for line in f:\n                        line = line.strip()\n                        if line.startswith('#'):\n                            continue\n                        if not line:\n                            continue\n                        line = line.replace('${SAGE_ROOT}', SAGE_ROOT)\n                        line = line.replace('${SAGE_DISTFILES}', SAGE_DISTFILES)\n                        if '${SAGE_SERVER}' in line:\n                            SAGE_SERVER = os.environ.get(\"SAGE_SERVER\", \"\")\n                            if not SAGE_SERVER:\n                                continue\n                            line = line.replace('${SAGE_SERVER}', SAGE_SERVER)\n                        if line.endswith('mirror_list'):\n                            cache_filename = os.path.join(SAGE_DISTFILES, line.rpartition('/')[2])\n                            self.sources.append(MirrorList_from_url(line, cache_filename))\n                        else:\n                            self.sources.append([line])\n            except IOError:\n                # Silently ignore files that do not exist\n                pass\n\n    def __iter__(self):\n        \"\"\"\n        Iterate through the list of mirrors.\n\n        This is the main entry point into the mirror list. Every\n        script should just use this function to try mirrors in order\n        of preference. This will not just yield the official mirrors,\n        but also urls for packages that are currently being tested.\n        \"\"\"\n        for source in self.sources:\n            for mirror in source:\n                yield mirror\n\n\nclass MirrorList_from_url(object):\n    \n    MAXAGE = 24*60*60   # seconds\n\n    def __init__(self, url, filename):\n        self.url = url\n        self.filename = filename\n        self._mirrors = None\n\n    @property\n    def mirrors(self):\n        if self._mirrors is not None:\n            return self._mirrors\n\n        try:\n            self.mirrorfile = open(self.filename, 'r+t')\n        except IOError:\n            self.mirrorfile = open(self.filename, 'w+t')\n\n        with self.mirrorfile:\n            self.mirrorfd = self.mirrorfile.fileno()\n            try_lock(self.mirrorfd, LOCK_SH)  # shared (read) lock\n            if self._must_refresh():\n                try_lock(self.mirrorfd, LOCK_EX)  # exclusive (write) lock\n                # Maybe the mirror list file was updated by a different\n                # process while we waited for the lock?  Check again.\n                if self._must_refresh():\n                    self._refresh()\n            if self._mirrors is None:\n                self._mirrors = self._load()\n\n        return self._mirrors\n\n    def _load(self, mirror_list=None):\n        \"\"\"\n        Load and return `mirror_list` (defaults to the one on disk) as\n        a list of strings\n        \"\"\"\n        if mirror_list is None:\n            try:\n                self.mirrorfile.seek(0)\n                mirror_list = self.mirrorfile.read()\n            except IOError:\n                log.critical('Failed to load the cached mirror list')\n                return []\n        if mirror_list == '':\n            return []\n        import ast\n        try:\n            return ast.literal_eval(mirror_list)\n        except SyntaxError:\n            log.critical('Downloaded mirror list has syntax error: {0}'.format(mirror_list))\n            return []\n\n    def _save(self):\n        \"\"\"\n        Save the mirror list for (short-term) future  use.\n        \"\"\"\n        self.mirrorfile.seek(0)\n        self.mirrorfile.write(repr(self.mirrors))\n        self.mirrorfile.truncate()\n        self.mirrorfile.flush()\n\n    def _port_of_mirror(self, mirror):\n        if mirror.startswith('http://'):\n            return 80\n        if mirror.startswith('https://'):\n            return 443\n        if mirror.startswith('ftp://'):\n            return 21\n        # Sensible default (invalid mirror?)\n        return 80\n\n    def _rank_mirrors(self):\n        \"\"\"\n        Sort the mirrors by speed, fastest being first\n\n        This method is used by the YUM fastestmirror plugin\n        \"\"\"\n        timed_mirrors = []\n        import time, socket\n        log.info('Searching fastest mirror')\n        timeout = 1\n        for mirror in self.mirrors:\n            if not mirror.startswith('http'):\n                log.debug('we currently can only handle http, got %s', mirror)\n                continue\n            port = self._port_of_mirror(mirror)\n            mirror_hostname = urlparse.urlsplit(mirror).netloc\n            time_before = time.time()\n            try:\n                sock = socket.create_connection((mirror_hostname, port), timeout)\n                sock.close()\n            except (IOError, socket.error, socket.timeout) as err:\n                log.warning(str(err).strip() + ': ' + mirror)\n                continue\n            result = time.time() - time_before\n            result_ms = int(1000 * result)\n            log.info(str(result_ms).rjust(5) + 'ms: ' + mirror)\n            timed_mirrors.append((result, mirror))\n            timed_mirrors.sort()\n            if len(timed_mirrors) >= 5 and timed_mirrors[4][0] < 0.3:\n                # We don't need more than 5 decent mirrors\n                break\n\n        if len(timed_mirrors) == 0:\n            # We cannot reach any mirror directly, most likely firewall issue\n            if 'http_proxy' not in os.environ:\n                log.error('Could not reach any mirror directly and no proxy set')\n                raise MirrorListException('Failed to connect to any mirror, probably no internet connection')\n            log.info('Cannot time mirrors via proxy, using default order')\n        else:\n            self._mirrors = [m[1] for m in timed_mirrors]\n        log.info('Fastest mirror: ' + self.fastest)\n\n    def _age(self):\n        \"\"\"\n        Return the age of the cached mirror list in seconds\n        \"\"\"\n        import time\n        mtime = os.fstat(self.mirrorfd).st_mtime\n        now = time.mktime(time.localtime())\n        return now - mtime\n\n    def _must_refresh(self):\n        \"\"\"\n        Return whether we must download the mirror list.\n\n        If and only if this method returns ``False`` is it admissible\n        to use the cached mirror list.\n        \"\"\"\n        if os.fstat(self.mirrorfd).st_size == 0:\n            return True\n        return self._age() > self.MAXAGE\n\n    def _refresh(self):\n        \"\"\"\n        Download and rank the mirror list.\n        \"\"\"\n        log.info('Downloading the Sage mirror list')\n        try:\n            with contextlib.closing(urllib.urlopen(self.url)) as f:\n                mirror_list = f.read().decode(\"ascii\")\n        except IOError:\n            log.critical('Downloading the mirror list failed, using cached version')\n        else:\n            self._mirrors = self._load(mirror_list)\n            self._rank_mirrors()\n            self._save()\n\n    def __iter__(self):\n        \"\"\"\n        Iterate through the list of mirrors.\n\n        This is the main entry point into the mirror list. Every\n        script should just use this function to try mirrors in order\n        of preference. This will not just yield the official mirrors,\n        but also urls for packages that are currently being tested.\n        \"\"\"\n        try:\n            yield os.environ['SAGE_SERVER']\n        except KeyError:\n            pass\n        for mirror in self.mirrors:\n            if not mirror.endswith('/'):\n                mirror += '/'\n            yield mirror + '/'.join(['spkg', 'upstream', '${SPKG}'])\n\n    @property\n    def fastest(self):\n        return next(iter(self))\n", "path": "build/sage_bootstrap/download/mirror_list.py"}]}