1# Copyright (c) 2012, Willow Garage, Inc. 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in the 11# documentation and/or other materials provided with the distribution. 12# * Neither the name of the Willow Garage, Inc. nor the names of its 13# contributors may be used to endorse or promote products derived from 14# this software without specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26# POSSIBILITY OF SUCH DAMAGE. 27 28import hashlib 29import os 30import tempfile 31 32from .core import CachePermissionError 33 34try: 35 import cPickle as pickle 36except ImportError: 37 import pickle 38 39PICKLE_CACHE_EXT = '.pickle' 40 41 42def compute_filename_hash(key_filenames): 43 sha_hash = hashlib.sha1() 44 if isinstance(key_filenames, list): 45 for key in key_filenames: 46 sha_hash.update(key.encode()) 47 else: 48 sha_hash.update(key_filenames.encode()) 49 return sha_hash.hexdigest() 50 51 52def write_cache_file(source_cache_d, key_filenames, rosdep_data): 53 """ 54 :param source_cache_d: directory to write cache file to 55 :param key_filenames: filename (or list of filenames) to be used in hashing 56 :param rosdep_data: dictionary of data to serialize as YAML 57 :returns: name of file where cache is stored 58 :raises: :exc:`OSError` if cannot write to cache file/directory 59 :raises: :exc:`IOError` if cannot write to cache file/directory 60 """ 61 if not os.path.exists(source_cache_d): 62 os.makedirs(source_cache_d) 63 key_hash = compute_filename_hash(key_filenames) 64 filepath = os.path.join(source_cache_d, key_hash) 65 try: 66 write_atomic(filepath + PICKLE_CACHE_EXT, pickle.dumps(rosdep_data, 2), True) 67 except OSError as e: 68 raise CachePermissionError('Failed to write cache file: ' + str(e)) 69 try: 70 os.unlink(filepath) 71 except OSError: 72 pass 73 return filepath 74 75 76def write_atomic(filepath, data, binary=False): 77 # write data to new file 78 fd, filepath_tmp = tempfile.mkstemp(prefix=os.path.basename(filepath) + '.tmp.', dir=os.path.dirname(filepath)) 79 80 if (binary): 81 fmode = 'wb' 82 else: 83 fmode = 'w' 84 85 with os.fdopen(fd, fmode) as f: 86 f.write(data) 87 f.close() 88 89 try: 90 # switch file atomically (if supported) 91 os.rename(filepath_tmp, filepath) 92 except OSError: 93 # fall back to non-atomic operation 94 try: 95 os.unlink(filepath) 96 except OSError: 97 pass 98 try: 99 os.rename(filepath_tmp, filepath) 100 except OSError: 101 os.unlink(filepath_tmp) 102