prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  HTTP 302 recipe download file
  Downloading from sourceforge autoselecting an mirror results in a HTTP 302 instead of a direct download.
  Trying to build this clean recipe below results in:
  
  ```
  RAN: '/bin/tar xzf ~/.local/share/python-for-android/packages/boost/boost_1_58_0.tar.gz'
    STDOUT:
    STDERR:
  gzip: stdin: not in gzip format
  /bin/tar: Child returned status 1
  /bin/tar: Error is not recoverable: exiting now
  ```
  
  Because this .tar.gz file is in reality the 302 HTML (ASCII) page.
  
  ```
  from pythonforandroid.toolchain import Recipe, shprint, shutil, current_directory
  from os.path import exists, join
  
  class BoostRecipe(Recipe):
      version = '1.58.0'
      url = 'http://downloads.sourceforge.net/project/boost/boost/{version}/boost_1_58_0.tar.gz'
  
  recipe = BoostRecipe()
  ```
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pythonforandroid/util.py`
  Content:
  ```
  1 import contextlib
  2 from os.path import exists
  3 from os import getcwd, chdir, makedirs
  4 import io
  5 import json
  6 import shutil
  7 import sys
  8 from tempfile import mkdtemp
  9 try:
  10     from urllib.request import FancyURLopener
  11 except ImportError:
  12     from urllib import FancyURLopener
  13 
  14 from pythonforandroid.logger import (logger, Err_Fore)
  15 
  16 IS_PY3 = sys.version_info[0] >= 3
  17 
  18 if IS_PY3:
  19     unistr = str
  20 else:
  21     unistr = unicode
  22 
  23 
  24 class ChromeDownloader(FancyURLopener):
  25     version = (
  26         'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '
  27         '(KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36')
  28 
  29 urlretrieve = ChromeDownloader().retrieve
  30 
  31 
  32 @contextlib.contextmanager
  33 def current_directory(new_dir):
  34     cur_dir = getcwd()
  35     logger.info(''.join((Err_Fore.CYAN, '-> directory context ', new_dir,
  36                          Err_Fore.RESET)))
  37     chdir(new_dir)
  38     yield
  39     logger.info(''.join((Err_Fore.CYAN, '<- directory context ', cur_dir,
  40                          Err_Fore.RESET)))
  41     chdir(cur_dir)
  42 
  43 
  44 @contextlib.contextmanager
  45 def temp_directory():
  46     temp_dir = mkdtemp()
  47     try:
  48         logger.debug(''.join((Err_Fore.CYAN, ' + temp directory used ',
  49                               temp_dir, Err_Fore.RESET)))
  50         yield temp_dir
  51     finally:
  52         shutil.rmtree(temp_dir)
  53         logger.debug(''.join((Err_Fore.CYAN, ' - temp directory deleted ',
  54                               temp_dir, Err_Fore.RESET)))
  55 
  56 
  57 def ensure_dir(filename):
  58     if not exists(filename):
  59         makedirs(filename)
  60 
  61 
  62 class JsonStore(object):
  63     """Replacement of shelve using json, needed for support python 2 and 3.
  64     """
  65 
  66     def __init__(self, filename):
  67         super(JsonStore, self).__init__()
  68         self.filename = filename
  69         self.data = {}
  70         if exists(filename):
  71             try:
  72                 with io.open(filename, encoding='utf-8') as fd:
  73                     self.data = json.load(fd)
  74             except ValueError:
  75                 print("Unable to read the state.db, content will be replaced.")
  76 
  77     def __getitem__(self, key):
  78         return self.data[key]
  79 
  80     def __setitem__(self, key, value):
  81         self.data[key] = value
  82         self.sync()
  83 
  84     def __delitem__(self, key):
  85         del self.data[key]
  86         self.sync()
  87 
  88     def __contains__(self, item):
  89         return item in self.data
  90 
  91     def get(self, item, default=None):
  92         return self.data.get(item, default)
  93 
  94     def keys(self):
  95         return self.data.keys()
  96 
  97     def remove_all(self, prefix):
  98         for key in self.data.keys()[:]:
  99             if not key.startswith(prefix):
  100                 continue
  101             del self.data[key]
  102         self.sync()
  103 
  104     def sync(self):
  105         # http://stackoverflow.com/questions/12309269/write-json-data-to-file-in-python/14870531#14870531
  106         if IS_PY3:
  107             with open(self.filename, 'w') as fd:
  108                 json.dump(self.data, fd, ensure_ascii=False)
  109         else:
  110             with io.open(self.filename, 'w', encoding='utf-8') as fd:
  111                 fd.write(unicode(json.dumps(self.data, ensure_ascii=False)))
  112 
  113 
  114 def which(program, path_env):
  115     '''Locate an executable in the system.'''
  116     import os
  117 
  118     def is_exe(fpath):
  119         return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
  120 
  121     fpath, fname = os.path.split(program)
  122     if fpath:
  123         if is_exe(program):
  124             return program
  125     else:
  126         for path in path_env.split(os.pathsep):
  127             path = path.strip('"')
  128             exe_file = os.path.join(path, program)
  129             if is_exe(exe_file):
  130                 return exe_file
  131 
  132     return None
  133 
  134 
  135 def get_directory(filename):
  136     '''If the filename ends with a recognised file extension, return the
  137     filename without this extension.'''
  138     if filename.endswith('.tar.gz'):
  139         return basename(filename[:-7])
  140     elif filename.endswith('.tgz'):
  141         return basename(filename[:-4])
  142     elif filename.endswith('.tar.bz2'):
  143         return basename(filename[:-8])
  144     elif filename.endswith('.tbz2'):
  145         return basename(filename[:-5])
  146     elif filename.endswith('.zip'):
  147         return basename(filename[:-4])
  148     info('Unknown file extension for {}'.format(filename))
  149     exit(1)
  150
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/pythonforandroid/util.py b/pythonforandroid/util.py
  --- a/pythonforandroid/util.py
  +++ b/pythonforandroid/util.py
  @@ -21,12 +21,10 @@
       unistr = unicode
   
   
  -class ChromeDownloader(FancyURLopener):
  -    version = (
  -        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '
  -        '(KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36')
  +class WgetDownloader(FancyURLopener):
  +    version = ('Wget/1.17.1')
   
  -urlretrieve = ChromeDownloader().retrieve
  +urlretrieve = WgetDownloader().retrieve
   
   
   @contextlib.contextmanager
  
verification_info: {"golden_diff": "diff --git a/pythonforandroid/util.py b/pythonforandroid/util.py\n--- a/pythonforandroid/util.py\n+++ b/pythonforandroid/util.py\n@@ -21,12 +21,10 @@\n     unistr = unicode\n \n \n-class ChromeDownloader(FancyURLopener):\n-    version = (\n-        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '\n-        '(KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36')\n+class WgetDownloader(FancyURLopener):\n+    version = ('Wget/1.17.1')\n \n-urlretrieve = ChromeDownloader().retrieve\n+urlretrieve = WgetDownloader().retrieve\n \n \n @contextlib.contextmanager\n", "issue": "HTTP 302 recipe download file\nDownloading from sourceforge autoselecting an mirror results in a HTTP 302 instead of a direct download.\nTrying to build this clean recipe below results in:\n\n```\nRAN: '/bin/tar xzf ~/.local/share/python-for-android/packages/boost/boost_1_58_0.tar.gz'\n  STDOUT:\n  STDERR:\ngzip: stdin: not in gzip format\n/bin/tar: Child returned status 1\n/bin/tar: Error is not recoverable: exiting now\n```\n\nBecause this .tar.gz file is in reality the 302 HTML (ASCII) page.\n\n```\nfrom pythonforandroid.toolchain import Recipe, shprint, shutil, current_directory\nfrom os.path import exists, join\n\nclass BoostRecipe(Recipe):\n    version = '1.58.0'\n    url = 'http://downloads.sourceforge.net/project/boost/boost/{version}/boost_1_58_0.tar.gz'\n\nrecipe = BoostRecipe()\n```\n\n", "before_files": [{"content": "import contextlib\nfrom os.path import exists\nfrom os import getcwd, chdir, makedirs\nimport io\nimport json\nimport shutil\nimport sys\nfrom tempfile import mkdtemp\ntry:\n    from urllib.request import FancyURLopener\nexcept ImportError:\n    from urllib import FancyURLopener\n\nfrom pythonforandroid.logger import (logger, Err_Fore)\n\nIS_PY3 = sys.version_info[0] >= 3\n\nif IS_PY3:\n    unistr = str\nelse:\n    unistr = unicode\n\n\nclass ChromeDownloader(FancyURLopener):\n    version = (\n        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '\n        '(KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36')\n\nurlretrieve = ChromeDownloader().retrieve\n\n\n@contextlib.contextmanager\ndef current_directory(new_dir):\n    cur_dir = getcwd()\n    logger.info(''.join((Err_Fore.CYAN, '-> directory context ', new_dir,\n                         Err_Fore.RESET)))\n    chdir(new_dir)\n    yield\n    logger.info(''.join((Err_Fore.CYAN, '<- directory context ', cur_dir,\n                         Err_Fore.RESET)))\n    chdir(cur_dir)\n\n\n@contextlib.contextmanager\ndef temp_directory():\n    temp_dir = mkdtemp()\n    try:\n        logger.debug(''.join((Err_Fore.CYAN, ' + temp directory used ',\n                              temp_dir, Err_Fore.RESET)))\n        yield temp_dir\n    finally:\n        shutil.rmtree(temp_dir)\n        logger.debug(''.join((Err_Fore.CYAN, ' - temp directory deleted ',\n                              temp_dir, Err_Fore.RESET)))\n\n\ndef ensure_dir(filename):\n    if not exists(filename):\n        makedirs(filename)\n\n\nclass JsonStore(object):\n    \"\"\"Replacement of shelve using json, needed for support python 2 and 3.\n    \"\"\"\n\n    def __init__(self, filename):\n        super(JsonStore, self).__init__()\n        self.filename = filename\n        self.data = {}\n        if exists(filename):\n            try:\n                with io.open(filename, encoding='utf-8') as fd:\n                    self.data = json.load(fd)\n            except ValueError:\n                print(\"Unable to read the state.db, content will be replaced.\")\n\n    def __getitem__(self, key):\n        return self.data[key]\n\n    def __setitem__(self, key, value):\n        self.data[key] = value\n        self.sync()\n\n    def __delitem__(self, key):\n        del self.data[key]\n        self.sync()\n\n    def __contains__(self, item):\n        return item in self.data\n\n    def get(self, item, default=None):\n        return self.data.get(item, default)\n\n    def keys(self):\n        return self.data.keys()\n\n    def remove_all(self, prefix):\n        for key in self.data.keys()[:]:\n            if not key.startswith(prefix):\n                continue\n            del self.data[key]\n        self.sync()\n\n    def sync(self):\n        # http://stackoverflow.com/questions/12309269/write-json-data-to-file-in-python/14870531#14870531\n        if IS_PY3:\n            with open(self.filename, 'w') as fd:\n                json.dump(self.data, fd, ensure_ascii=False)\n        else:\n            with io.open(self.filename, 'w', encoding='utf-8') as fd:\n                fd.write(unicode(json.dumps(self.data, ensure_ascii=False)))\n\n\ndef which(program, path_env):\n    '''Locate an executable in the system.'''\n    import os\n\n    def is_exe(fpath):\n        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)\n\n    fpath, fname = os.path.split(program)\n    if fpath:\n        if is_exe(program):\n            return program\n    else:\n        for path in path_env.split(os.pathsep):\n            path = path.strip('\"')\n            exe_file = os.path.join(path, program)\n            if is_exe(exe_file):\n                return exe_file\n\n    return None\n\n\ndef get_directory(filename):\n    '''If the filename ends with a recognised file extension, return the\n    filename without this extension.'''\n    if filename.endswith('.tar.gz'):\n        return basename(filename[:-7])\n    elif filename.endswith('.tgz'):\n        return basename(filename[:-4])\n    elif filename.endswith('.tar.bz2'):\n        return basename(filename[:-8])\n    elif filename.endswith('.tbz2'):\n        return basename(filename[:-5])\n    elif filename.endswith('.zip'):\n        return basename(filename[:-4])\n    info('Unknown file extension for {}'.format(filename))\n    exit(1)\n", "path": "pythonforandroid/util.py"}], "after_files": [{"content": "import contextlib\nfrom os.path import exists\nfrom os import getcwd, chdir, makedirs\nimport io\nimport json\nimport shutil\nimport sys\nfrom tempfile import mkdtemp\ntry:\n    from urllib.request import FancyURLopener\nexcept ImportError:\n    from urllib import FancyURLopener\n\nfrom pythonforandroid.logger import (logger, Err_Fore)\n\nIS_PY3 = sys.version_info[0] >= 3\n\nif IS_PY3:\n    unistr = str\nelse:\n    unistr = unicode\n\n\nclass WgetDownloader(FancyURLopener):\n    version = ('Wget/1.17.1')\n\nurlretrieve = WgetDownloader().retrieve\n\n\n@contextlib.contextmanager\ndef current_directory(new_dir):\n    cur_dir = getcwd()\n    logger.info(''.join((Err_Fore.CYAN, '-> directory context ', new_dir,\n                         Err_Fore.RESET)))\n    chdir(new_dir)\n    yield\n    logger.info(''.join((Err_Fore.CYAN, '<- directory context ', cur_dir,\n                         Err_Fore.RESET)))\n    chdir(cur_dir)\n\n\n@contextlib.contextmanager\ndef temp_directory():\n    temp_dir = mkdtemp()\n    try:\n        logger.debug(''.join((Err_Fore.CYAN, ' + temp directory used ',\n                              temp_dir, Err_Fore.RESET)))\n        yield temp_dir\n    finally:\n        shutil.rmtree(temp_dir)\n        logger.debug(''.join((Err_Fore.CYAN, ' - temp directory deleted ',\n                              temp_dir, Err_Fore.RESET)))\n\n\ndef ensure_dir(filename):\n    if not exists(filename):\n        makedirs(filename)\n\n\nclass JsonStore(object):\n    \"\"\"Replacement of shelve using json, needed for support python 2 and 3.\n    \"\"\"\n\n    def __init__(self, filename):\n        super(JsonStore, self).__init__()\n        self.filename = filename\n        self.data = {}\n        if exists(filename):\n            try:\n                with io.open(filename, encoding='utf-8') as fd:\n                    self.data = json.load(fd)\n            except ValueError:\n                print(\"Unable to read the state.db, content will be replaced.\")\n\n    def __getitem__(self, key):\n        return self.data[key]\n\n    def __setitem__(self, key, value):\n        self.data[key] = value\n        self.sync()\n\n    def __delitem__(self, key):\n        del self.data[key]\n        self.sync()\n\n    def __contains__(self, item):\n        return item in self.data\n\n    def get(self, item, default=None):\n        return self.data.get(item, default)\n\n    def keys(self):\n        return self.data.keys()\n\n    def remove_all(self, prefix):\n        for key in self.data.keys()[:]:\n            if not key.startswith(prefix):\n                continue\n            del self.data[key]\n        self.sync()\n\n    def sync(self):\n        # http://stackoverflow.com/questions/12309269/write-json-data-to-file-in-python/14870531#14870531\n        if IS_PY3:\n            with open(self.filename, 'w') as fd:\n                json.dump(self.data, fd, ensure_ascii=False)\n        else:\n            with io.open(self.filename, 'w', encoding='utf-8') as fd:\n                fd.write(unicode(json.dumps(self.data, ensure_ascii=False)))\n\n\ndef which(program, path_env):\n    '''Locate an executable in the system.'''\n    import os\n\n    def is_exe(fpath):\n        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)\n\n    fpath, fname = os.path.split(program)\n    if fpath:\n        if is_exe(program):\n            return program\n    else:\n        for path in path_env.split(os.pathsep):\n            path = path.strip('\"')\n            exe_file = os.path.join(path, program)\n            if is_exe(exe_file):\n                return exe_file\n\n    return None\n\n\ndef get_directory(filename):\n    '''If the filename ends with a recognised file extension, return the\n    filename without this extension.'''\n    if filename.endswith('.tar.gz'):\n        return basename(filename[:-7])\n    elif filename.endswith('.tgz'):\n        return basename(filename[:-4])\n    elif filename.endswith('.tar.bz2'):\n        return basename(filename[:-8])\n    elif filename.endswith('.tbz2'):\n        return basename(filename[:-5])\n    elif filename.endswith('.zip'):\n        return basename(filename[:-4])\n    info('Unknown file extension for {}'.format(filename))\n    exit(1)\n", "path": "pythonforandroid/util.py"}]}