def open_file_for_write(filepath, mode=None): """Writes both to given filepath, and tmpdir location. This is to get around the problem with some NFS's where immediately reading a file that has just been written is problematic. Instead, any files that we write, we also write to /tmp, and reads of these files are redirected there. Args: filepath (str): File to write. mode (int): Same mode arg as you would pass to `os.chmod`. Yields: File-like object. """ stream = StringIO() yield stream content = stream.getvalue() filepath = os.path.realpath(filepath) tmpdir = tmpdir_manager.mkdtemp() cache_filepath = os.path.join(tmpdir, os.path.basename(filepath)) debug_print("Writing to %s (local cache of %s)", cache_filepath, filepath) with atomic_write(filepath, overwrite=True) as f: f.write(content) if mode is not None: os.chmod(filepath, mode) with open(cache_filepath, 'w') as f: f.write(content) file_cache[filepath] = cache_filepath
def open_file_for_write(filepath, mode=None): """Writes both to given filepath, and tmpdir location. This is to get around the problem with some NFS's where immediately reading a file that has just been written is problematic. Instead, any files that we write, we also write to /tmp, and reads of these files are redirected there. Args: filepath (str): File to write. mode (int): Same mode arg as you would pass to `os.chmod`. Yields: File-like object. """ stream = StringIO() yield stream content = stream.getvalue() filepath = os.path.realpath(filepath) tmpdir = tmpdir_manager.mkdtemp() cache_filepath = os.path.join(tmpdir, os.path.basename(filepath)) encoding = {"encoding": "utf-8"} if PY3 else {} debug_print("Writing to %s (local cache of %s)", cache_filepath, filepath) # Attempt to make file writable if it isn't already. Just fallthrough # if this fails, we'll get the error we expect on write anyway # try: if os.path.exists(filepath): orig_mode = os.stat(filepath).st_mode os.chmod(filepath, orig_mode | stat.S_IWUSR) except: pass # try atomic write, but that can sometimes fail. # https://github.com/nerdvegas/rez/issues/858 # written = False try: with atomic_write(filepath, overwrite=True, **encoding) as f: f.write(content) written = True except: pass # fallback to standard write if not written: with open(filepath, 'w', **encoding) as f: f.write(content) if mode is not None: os.chmod(filepath, mode) # write the local fs cache copy with open(cache_filepath, 'w', **encoding) as f: f.write(content) file_cache[filepath] = cache_filepath
def open_file_for_write(filepath, mode=None): """Writes both to given filepath, and tmpdir location. This is to get around the problem with some NFS's where immediately reading a file that has just been written is problematic. Instead, any files that we write, we also write to /tmp, and reads of these files are redirected there. Args: filepath (str): File to write. mode (int): Same mode arg as you would pass to `os.chmod`. Yields: File-like object. """ stream = StringIO() yield stream content = stream.getvalue() filepath = os.path.realpath(filepath) tmpdir = tmpdir_manager.mkdtemp() cache_filepath = os.path.join(tmpdir, os.path.basename(filepath)) encoding = {"encoding": "utf-8"} if PY3 else {} debug_print("Writing to %s (local cache of %s)", cache_filepath, filepath) for attempt in range(2): try: with atomic_write(filepath, overwrite=True, **encoding) as f: f.write(content) except OSError as e: # If we are writing to the network, we can't ensure atomicity if e.errno == 22: with open(filepath, "w", **encoding) as f: f.write(content) elif attempt == 0: # `overwrite=True` of atomic_write doesn't restore # writability to the file being written to. os.chmod(filepath, stat.S_IWRITE | stat.S_IREAD) elif sys.platform == "win32": # Under Windows, atomic_write doesn't tell you about # which file actually failed. raise WindowsError("%s: '%s'" % (e, filepath)) if mode is not None: os.chmod(filepath, mode) with open(cache_filepath, 'w', **encoding) as f: f.write(content) file_cache[filepath] = cache_filepath
def open_file_for_write(filepath, mode=None): """Writes both to given filepath, and tmpdir location. This is to get around the problem with some NFS's where immediately reading a file that has just been written is problematic. Instead, any files that we write, we also write to /tmp, and reads of these files are redirected there. Args: filepath (str): File to write. mode (int): Same mode arg as you would pass to `os.chmod`. Yields: File-like object. """ stream = StringIO() yield stream content = stream.getvalue() filepath = os.path.realpath(filepath) tmpdir = tmpdir_manager.mkdtemp() cache_filepath = os.path.join(tmpdir, os.path.basename(filepath)) debug_print("Writing to %s (local cache of %s)", cache_filepath, filepath) for attempt in range(2): try: with atomic_write(filepath, overwrite=True) as f: f.write(content) except WindowsError as e: if attempt == 0: # `overwrite=True` of atomic_write doesn't restore # writability to the file being written to. os.chmod(filepath, stat.S_IWRITE | stat.S_IREAD) else: # Under Windows, atomic_write doesn't tell you about # which file actually failed. raise WindowsError("%s: '%s'" % (e, filepath)) if mode is not None: os.chmod(filepath, mode) with open(cache_filepath, 'w') as f: f.write(content) file_cache[filepath] = cache_filepath
def open_file_for_write(filepath): """Writes both to given filepath, and tmpdir location. This is to get around the problem with some NFS's where immediately reading a file that has just been written is problematic. Instead, any files that we write, we also write to /tmp, and reads of these files are redirected there. """ stream = StringIO() yield stream content = stream.getvalue() filepath = os.path.realpath(filepath) tmpdir = tmpdir_manager.mkdtemp() cache_filepath = os.path.join(tmpdir, os.path.basename(filepath)) debug_print("Writing to %s (local cache of %s)", cache_filepath, filepath) with atomic_write(filepath, overwrite=True) as f: f.write(content) with open(cache_filepath, 'w') as f: f.write(content) file_cache[filepath] = cache_filepath