prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Race condition for root environment detection
  Periodically, when two conda processes are running at the same time, it is possible to see a race condition on determining whether the root environment is writable. Notice how the following produces two different configs from the same setup:
  
  ```
  $ conda info & conda info 
  
  Current conda install:
  
               platform : osx-64
          conda version : 3.18.3
    conda-build version : 1.18.0
         python version : 2.7.10.final.0
       requests version : 2.8.1
       root environment : /Users/pelson/miniconda  (read only)
    default environment : /Users/pelson/miniconda
       envs directories : /Users/pelson/.conda/envs
                          /Users/pelson/envs
                          /Users/pelson/miniconda/envs
          package cache : /Users/pelson/.conda/envs/.pkgs
                          /Users/pelson/envs/.pkgs
                          /Users/pelson/miniconda/pkgs
  ...
  
  Current conda install:
  
               platform : osx-64
          conda version : 3.18.3
    conda-build version : 1.18.0
         python version : 2.7.10.final.0
       requests version : 2.8.1
       root environment : /Users/pelson/miniconda  (writable)
    default environment : /Users/pelson/miniconda
       envs directories : /Users/pelson/miniconda/envs
          package cache : /Users/pelson/miniconda/pkgs
  ...
  ```
  
  The offending line is in https://github.com/conda/conda/blob/master/conda/config.py#L135-L143 and https://github.com/conda/conda/blob/master/conda/utils.py#L43-L54. My assumption is that the `.conda-try-write` is being removed by the other process, and the exception is being raised in the `finally` block.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `conda/utils.py`
  Content:
  ```
  1 from __future__ import print_function, division, absolute_import
  2 
  3 import logging
  4 import sys
  5 import hashlib
  6 import collections
  7 from functools import partial
  8 from os.path import abspath, isdir, join
  9 import os
  10 
  11 log = logging.getLogger(__name__)
  12 stderrlog = logging.getLogger('stderrlog')
  13 
  14 def can_open(file):
  15     """
  16     Return True if the given ``file`` can be opened for writing
  17     """
  18     try:
  19         fp = open(file, "ab")
  20         fp.close()
  21         return True
  22     except IOError:
  23         stderrlog.info("Unable to open %s\n" % file)
  24         return False
  25 
  26 
  27 def can_open_all(files):
  28     """
  29     Return True if all of the provided ``files`` can be opened
  30     """
  31     for f in files:
  32         if not can_open(f):
  33             return False
  34     return True
  35 
  36 
  37 def can_open_all_files_in_prefix(prefix, files):
  38     """
  39     Returns True if all ``files`` at a given ``prefix`` can be opened
  40     """
  41     return can_open_all((os.path.join(prefix, f) for f in files))
  42 
  43 def try_write(dir_path):
  44     assert isdir(dir_path)
  45     try:
  46         try:
  47             with open(join(dir_path, '.conda-try-write'), mode='wb') as fo:
  48                 fo.write(b'This is a test file.\n')
  49             return True
  50         finally:
  51             # XXX: If this raises an exception it will also return False
  52             os.unlink(join(dir_path, '.conda-try-write'))
  53     except (IOError, OSError):
  54         return False
  55 
  56 
  57 def hashsum_file(path, mode='md5'):
  58     h = hashlib.new(mode)
  59     with open(path, 'rb') as fi:
  60         while True:
  61             chunk = fi.read(262144)  # process chunks of 256KB
  62             if not chunk:
  63                 break
  64             h.update(chunk)
  65     return h.hexdigest()
  66 
  67 
  68 def md5_file(path):
  69     return hashsum_file(path, 'md5')
  70 
  71 
  72 def url_path(path):
  73     path = abspath(path)
  74     if sys.platform == 'win32':
  75         path = '/' + path.replace(':', '|').replace('\\', '/')
  76     return 'file://%s' % path
  77 
  78 
  79 def human_bytes(n):
  80     """
  81     Return the number of bytes n in more human readable form.
  82     """
  83     if n < 1024:
  84         return '%d B' % n
  85     k = n/1024
  86     if k < 1024:
  87         return '%d KB' % round(k)
  88     m = k/1024
  89     if m < 1024:
  90         return '%.1f MB' % m
  91     g = m/1024
  92     return '%.2f GB' % g
  93 
  94 
  95 class memoized(object):
  96     """Decorator. Caches a function's return value each time it is called.
  97     If called later with the same arguments, the cached value is returned
  98     (not reevaluated).
  99     """
  100     def __init__(self, func):
  101         self.func = func
  102         self.cache = {}
  103     def __call__(self, *args, **kw):
  104         newargs = []
  105         for arg in args:
  106             if isinstance(arg, list):
  107                 newargs.append(tuple(arg))
  108             elif not isinstance(arg, collections.Hashable):
  109                 # uncacheable. a list, for instance.
  110                 # better to not cache than blow up.
  111                 return self.func(*args, **kw)
  112             else:
  113                 newargs.append(arg)
  114         newargs = tuple(newargs)
  115         key = (newargs, frozenset(kw.items()))
  116         if key in self.cache:
  117             return self.cache[key]
  118         else:
  119             value = self.func(*args, **kw)
  120             self.cache[key] = value
  121             return value
  122 
  123 
  124 # For instance methods only
  125 class memoize(object): # 577452
  126     def __init__(self, func):
  127         self.func = func
  128     def __get__(self, obj, objtype=None):
  129         if obj is None:
  130             return self.func
  131         return partial(self, obj)
  132     def __call__(self, *args, **kw):
  133         obj = args[0]
  134         try:
  135             cache = obj.__cache
  136         except AttributeError:
  137             cache = obj.__cache = {}
  138         key = (self.func, args[1:], frozenset(kw.items()))
  139         try:
  140             res = cache[key]
  141         except KeyError:
  142             res = cache[key] = self.func(*args, **kw)
  143         return res
  144
  ```
  --- 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/conda/utils.py b/conda/utils.py
  --- a/conda/utils.py
  +++ b/conda/utils.py
  @@ -5,8 +5,10 @@
   import hashlib
   import collections
   from functools import partial
  -from os.path import abspath, isdir, join
  +from os.path import abspath, isdir
   import os
  +import tempfile
  +
   
   log = logging.getLogger(__name__)
   stderrlog = logging.getLogger('stderrlog')
  @@ -43,13 +45,10 @@
   def try_write(dir_path):
       assert isdir(dir_path)
       try:
  -        try:
  -            with open(join(dir_path, '.conda-try-write'), mode='wb') as fo:
  -                fo.write(b'This is a test file.\n')
  -            return True
  -        finally:
  -            # XXX: If this raises an exception it will also return False
  -            os.unlink(join(dir_path, '.conda-try-write'))
  +        with tempfile.TemporaryFile(prefix='.conda-try-write',
  +                                    dir=dir_path) as fo:
  +            fo.write(b'This is a test file.\n')
  +        return True
       except (IOError, OSError):
           return False
  
verification_info: {"golden_diff": "diff --git a/conda/utils.py b/conda/utils.py\n--- a/conda/utils.py\n+++ b/conda/utils.py\n@@ -5,8 +5,10 @@\n import hashlib\n import collections\n from functools import partial\n-from os.path import abspath, isdir, join\n+from os.path import abspath, isdir\n import os\n+import tempfile\n+\n \n log = logging.getLogger(__name__)\n stderrlog = logging.getLogger('stderrlog')\n@@ -43,13 +45,10 @@\n def try_write(dir_path):\n     assert isdir(dir_path)\n     try:\n-        try:\n-            with open(join(dir_path, '.conda-try-write'), mode='wb') as fo:\n-                fo.write(b'This is a test file.\\n')\n-            return True\n-        finally:\n-            # XXX: If this raises an exception it will also return False\n-            os.unlink(join(dir_path, '.conda-try-write'))\n+        with tempfile.TemporaryFile(prefix='.conda-try-write',\n+                                    dir=dir_path) as fo:\n+            fo.write(b'This is a test file.\\n')\n+        return True\n     except (IOError, OSError):\n         return False\n", "issue": "Race condition for root environment detection\nPeriodically, when two conda processes are running at the same time, it is possible to see a race condition on determining whether the root environment is writable. Notice how the following produces two different configs from the same setup:\n\n```\n$ conda info & conda info \n\nCurrent conda install:\n\n             platform : osx-64\n        conda version : 3.18.3\n  conda-build version : 1.18.0\n       python version : 2.7.10.final.0\n     requests version : 2.8.1\n     root environment : /Users/pelson/miniconda  (read only)\n  default environment : /Users/pelson/miniconda\n     envs directories : /Users/pelson/.conda/envs\n                        /Users/pelson/envs\n                        /Users/pelson/miniconda/envs\n        package cache : /Users/pelson/.conda/envs/.pkgs\n                        /Users/pelson/envs/.pkgs\n                        /Users/pelson/miniconda/pkgs\n...\n\nCurrent conda install:\n\n             platform : osx-64\n        conda version : 3.18.3\n  conda-build version : 1.18.0\n       python version : 2.7.10.final.0\n     requests version : 2.8.1\n     root environment : /Users/pelson/miniconda  (writable)\n  default environment : /Users/pelson/miniconda\n     envs directories : /Users/pelson/miniconda/envs\n        package cache : /Users/pelson/miniconda/pkgs\n...\n```\n\nThe offending line is in https://github.com/conda/conda/blob/master/conda/config.py#L135-L143 and https://github.com/conda/conda/blob/master/conda/utils.py#L43-L54. My assumption is that the `.conda-try-write` is being removed by the other process, and the exception is being raised in the `finally` block.\n\n", "before_files": [{"content": "from __future__ import print_function, division, absolute_import\n\nimport logging\nimport sys\nimport hashlib\nimport collections\nfrom functools import partial\nfrom os.path import abspath, isdir, join\nimport os\n\nlog = logging.getLogger(__name__)\nstderrlog = logging.getLogger('stderrlog')\n\ndef can_open(file):\n    \"\"\"\n    Return True if the given ``file`` can be opened for writing\n    \"\"\"\n    try:\n        fp = open(file, \"ab\")\n        fp.close()\n        return True\n    except IOError:\n        stderrlog.info(\"Unable to open %s\\n\" % file)\n        return False\n\n\ndef can_open_all(files):\n    \"\"\"\n    Return True if all of the provided ``files`` can be opened\n    \"\"\"\n    for f in files:\n        if not can_open(f):\n            return False\n    return True\n\n\ndef can_open_all_files_in_prefix(prefix, files):\n    \"\"\"\n    Returns True if all ``files`` at a given ``prefix`` can be opened\n    \"\"\"\n    return can_open_all((os.path.join(prefix, f) for f in files))\n\ndef try_write(dir_path):\n    assert isdir(dir_path)\n    try:\n        try:\n            with open(join(dir_path, '.conda-try-write'), mode='wb') as fo:\n                fo.write(b'This is a test file.\\n')\n            return True\n        finally:\n            # XXX: If this raises an exception it will also return False\n            os.unlink(join(dir_path, '.conda-try-write'))\n    except (IOError, OSError):\n        return False\n\n\ndef hashsum_file(path, mode='md5'):\n    h = hashlib.new(mode)\n    with open(path, 'rb') as fi:\n        while True:\n            chunk = fi.read(262144)  # process chunks of 256KB\n            if not chunk:\n                break\n            h.update(chunk)\n    return h.hexdigest()\n\n\ndef md5_file(path):\n    return hashsum_file(path, 'md5')\n\n\ndef url_path(path):\n    path = abspath(path)\n    if sys.platform == 'win32':\n        path = '/' + path.replace(':', '|').replace('\\\\', '/')\n    return 'file://%s' % path\n\n\ndef human_bytes(n):\n    \"\"\"\n    Return the number of bytes n in more human readable form.\n    \"\"\"\n    if n < 1024:\n        return '%d B' % n\n    k = n/1024\n    if k < 1024:\n        return '%d KB' % round(k)\n    m = k/1024\n    if m < 1024:\n        return '%.1f MB' % m\n    g = m/1024\n    return '%.2f GB' % g\n\n\nclass memoized(object):\n    \"\"\"Decorator. Caches a function's return value each time it is called.\n    If called later with the same arguments, the cached value is returned\n    (not reevaluated).\n    \"\"\"\n    def __init__(self, func):\n        self.func = func\n        self.cache = {}\n    def __call__(self, *args, **kw):\n        newargs = []\n        for arg in args:\n            if isinstance(arg, list):\n                newargs.append(tuple(arg))\n            elif not isinstance(arg, collections.Hashable):\n                # uncacheable. a list, for instance.\n                # better to not cache than blow up.\n                return self.func(*args, **kw)\n            else:\n                newargs.append(arg)\n        newargs = tuple(newargs)\n        key = (newargs, frozenset(kw.items()))\n        if key in self.cache:\n            return self.cache[key]\n        else:\n            value = self.func(*args, **kw)\n            self.cache[key] = value\n            return value\n\n\n# For instance methods only\nclass memoize(object): # 577452\n    def __init__(self, func):\n        self.func = func\n    def __get__(self, obj, objtype=None):\n        if obj is None:\n            return self.func\n        return partial(self, obj)\n    def __call__(self, *args, **kw):\n        obj = args[0]\n        try:\n            cache = obj.__cache\n        except AttributeError:\n            cache = obj.__cache = {}\n        key = (self.func, args[1:], frozenset(kw.items()))\n        try:\n            res = cache[key]\n        except KeyError:\n            res = cache[key] = self.func(*args, **kw)\n        return res\n", "path": "conda/utils.py"}], "after_files": [{"content": "from __future__ import print_function, division, absolute_import\n\nimport logging\nimport sys\nimport hashlib\nimport collections\nfrom functools import partial\nfrom os.path import abspath, isdir\nimport os\nimport tempfile\n\n\nlog = logging.getLogger(__name__)\nstderrlog = logging.getLogger('stderrlog')\n\ndef can_open(file):\n    \"\"\"\n    Return True if the given ``file`` can be opened for writing\n    \"\"\"\n    try:\n        fp = open(file, \"ab\")\n        fp.close()\n        return True\n    except IOError:\n        stderrlog.info(\"Unable to open %s\\n\" % file)\n        return False\n\n\ndef can_open_all(files):\n    \"\"\"\n    Return True if all of the provided ``files`` can be opened\n    \"\"\"\n    for f in files:\n        if not can_open(f):\n            return False\n    return True\n\n\ndef can_open_all_files_in_prefix(prefix, files):\n    \"\"\"\n    Returns True if all ``files`` at a given ``prefix`` can be opened\n    \"\"\"\n    return can_open_all((os.path.join(prefix, f) for f in files))\n\ndef try_write(dir_path):\n    assert isdir(dir_path)\n    try:\n        with tempfile.TemporaryFile(prefix='.conda-try-write',\n                                    dir=dir_path) as fo:\n            fo.write(b'This is a test file.\\n')\n        return True\n    except (IOError, OSError):\n        return False\n\n\ndef hashsum_file(path, mode='md5'):\n    h = hashlib.new(mode)\n    with open(path, 'rb') as fi:\n        while True:\n            chunk = fi.read(262144)  # process chunks of 256KB\n            if not chunk:\n                break\n            h.update(chunk)\n    return h.hexdigest()\n\n\ndef md5_file(path):\n    return hashsum_file(path, 'md5')\n\n\ndef url_path(path):\n    path = abspath(path)\n    if sys.platform == 'win32':\n        path = '/' + path.replace(':', '|').replace('\\\\', '/')\n    return 'file://%s' % path\n\n\ndef human_bytes(n):\n    \"\"\"\n    Return the number of bytes n in more human readable form.\n    \"\"\"\n    if n < 1024:\n        return '%d B' % n\n    k = n/1024\n    if k < 1024:\n        return '%d KB' % round(k)\n    m = k/1024\n    if m < 1024:\n        return '%.1f MB' % m\n    g = m/1024\n    return '%.2f GB' % g\n\n\nclass memoized(object):\n    \"\"\"Decorator. Caches a function's return value each time it is called.\n    If called later with the same arguments, the cached value is returned\n    (not reevaluated).\n    \"\"\"\n    def __init__(self, func):\n        self.func = func\n        self.cache = {}\n    def __call__(self, *args, **kw):\n        newargs = []\n        for arg in args:\n            if isinstance(arg, list):\n                newargs.append(tuple(arg))\n            elif not isinstance(arg, collections.Hashable):\n                # uncacheable. a list, for instance.\n                # better to not cache than blow up.\n                return self.func(*args, **kw)\n            else:\n                newargs.append(arg)\n        newargs = tuple(newargs)\n        key = (newargs, frozenset(kw.items()))\n        if key in self.cache:\n            return self.cache[key]\n        else:\n            value = self.func(*args, **kw)\n            self.cache[key] = value\n            return value\n\n\n# For instance methods only\nclass memoize(object): # 577452\n    def __init__(self, func):\n        self.func = func\n    def __get__(self, obj, objtype=None):\n        if obj is None:\n            return self.func\n        return partial(self, obj)\n    def __call__(self, *args, **kw):\n        obj = args[0]\n        try:\n            cache = obj.__cache\n        except AttributeError:\n            cache = obj.__cache = {}\n        key = (self.func, args[1:], frozenset(kw.items()))\n        try:\n            res = cache[key]\n        except KeyError:\n            res = cache[key] = self.func(*args, **kw)\n        return res\n", "path": "conda/utils.py"}]}