Module Resource
[hide private]
[frames] | no frames]

Source Code for Module Resource

  1  ##################################################################### 
  2  # -*- coding: iso-8859-1 -*-                                        # 
  3  #                                                                   # 
  4  # Frets on Fire                                                     # 
  5  # Copyright (C) 2006 Sami Kyöstilä                                  # 
  6  #                                                                   # 
  7  # This program is free software; you can redistribute it and/or     # 
  8  # modify it under the terms of the GNU General Public License       # 
  9  # as published by the Free Software Foundation; either version 2    # 
 10  # of the License, or (at your option) any later version.            # 
 11  #                                                                   # 
 12  # This program is distributed in the hope that it will be useful,   # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of    # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     # 
 15  # GNU General Public License for more details.                      # 
 16  #                                                                   # 
 17  # You should have received a copy of the GNU General Public License # 
 18  # along with this program; if not, write to the Free Software       # 
 19  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,        # 
 20  # MA  02110-1301, USA.                                              # 
 21  ##################################################################### 
 22   
 23  import os 
 24  from Queue import Queue, Empty 
 25  from threading import Thread, BoundedSemaphore 
 26  import time 
 27  import shutil 
 28  import stat 
 29   
 30  from Task import Task 
 31  import Log 
 32  import Version 
 33   
34 -class Loader(Thread):
35 - def __init__(self, target, name, function, resultQueue, loaderSemaphore, onLoad = None):
36 Thread.__init__(self) 37 self.semaphore = loaderSemaphore 38 self.target = target 39 self.name = name 40 self.function = function 41 self.resultQueue = resultQueue 42 self.result = None 43 self.onLoad = onLoad 44 self.exception = None 45 self.time = 0.0 46 self.canceled = False 47 if target and name: 48 setattr(target, name, None)
49
50 - def run(self):
51 self.semaphore.acquire() 52 # Reduce priority on posix 53 if os.name == "posix": 54 os.nice(5) 55 self.load() 56 self.semaphore.release() 57 self.resultQueue.put(self)
58
59 - def __str__(self):
60 return "%s(%s) %s" % (self.function.__name__, self.name, self.canceled and "(canceled)" or "")
61
62 - def cancel(self):
63 self.canceled = True
64
65 - def load(self):
66 try: 67 start = time.time() 68 self.result = self.function() 69 self.time = time.time() - start 70 except: 71 import sys 72 self.exception = sys.exc_info()
73
74 - def finish(self):
75 if self.canceled: 76 return 77 78 Log.notice("Loaded %s.%s in %.3f seconds" % (self.target.__class__.__name__, self.name, self.time)) 79 80 if self.exception: 81 raise self.exception[0], self.exception[1], self.exception[2] 82 if self.target and self.name: 83 setattr(self.target, self.name, self.result) 84 if self.onLoad: 85 self.onLoad(self.result) 86 return self.result
87
88 - def __call__(self):
89 self.join() 90 return self.result
91
92 -class Resource(Task):
93 - def __init__(self, dataPath = os.path.join("..", "data")):
94 self.resultQueue = Queue() 95 self.dataPaths = [dataPath] 96 self.loaderSemaphore = BoundedSemaphore(value = 1) 97 self.loaders = []
98
99 - def addDataPath(self, path):
100 if not path in self.dataPaths: 101 self.dataPaths = [path] + self.dataPaths
102
103 - def removeDataPath(self, path):
104 if path in self.dataPaths: 105 self.dataPaths.remove(path)
106
107 - def fileName(self, *name, **args):
108 if not args.get("writable", False): 109 for dataPath in self.dataPaths: 110 readOnlyPath = os.path.join(dataPath, *name) 111 # If the requested file is in the read-write path and not in the 112 # read-only path, use the existing read-write one. 113 if os.path.isfile(readOnlyPath): 114 return readOnlyPath 115 readWritePath = os.path.join(getWritableResourcePath(), *name) 116 if os.path.isfile(readWritePath): 117 return readWritePath 118 return readOnlyPath 119 else: 120 readOnlyPath = os.path.join(self.dataPaths[-1], *name) 121 try: 122 # First see if we can write to the original file 123 if os.access(readOnlyPath, os.W_OK): 124 return readOnlyPath 125 # If the original file does not exist, see if we can write to its directory 126 if not os.path.isfile(readOnlyPath) and os.access(os.path.dirname(readOnlyPath), os.W_OK): 127 return readOnlyPath 128 except: 129 raise 130 131 # If the resource exists in the read-only path, make a copy to the 132 # read-write path. 133 readWritePath = os.path.join(getWritableResourcePath(), *name) 134 if not os.path.isfile(readWritePath) and os.path.isfile(readOnlyPath): 135 Log.notice("Copying '%s' to writable data directory." % "/".join(name)) 136 try: 137 os.makedirs(os.path.dirname(readWritePath)) 138 except: 139 pass 140 shutil.copy(readOnlyPath, readWritePath) 141 self.makeWritable(readWritePath) 142 # Create directories if needed 143 if not os.path.isdir(readWritePath) and os.path.isdir(readOnlyPath): 144 Log.notice("Creating writable directory '%s'." % "/".join(name)) 145 os.makedirs(readWritePath) 146 self.makeWritable(readWritePath) 147 return readWritePath
148
149 - def makeWritable(self, path):
150 os.chmod(path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
151
152 - def load(self, target = None, name = None, function = lambda: None, synch = False, onLoad = None):
153 Log.notice("Loading %s.%s %s" % (target.__class__.__name__, name, synch and "synchronously" or "asynchronously")) 154 l = Loader(target, name, function, self.resultQueue, self.loaderSemaphore, onLoad = onLoad) 155 if synch: 156 l.load() 157 return l.finish() 158 else: 159 self.loaders.append(l) 160 l.start() 161 return l
162
163 - def run(self, ticks):
164 try: 165 loader = self.resultQueue.get_nowait() 166 loader.finish() 167 self.loaders.remove(loader) 168 except Empty: 169 pass
170
171 -def getWritableResourcePath():
172 """ 173 Returns a path that holds the configuration for the application. 174 """ 175 path = "." 176 appname = Version.appName() 177 if os.name == "posix": 178 path = os.path.expanduser("~/." + appname) 179 elif os.name == "nt": 180 try: 181 path = os.path.join(os.environ["APPDATA"], appname) 182 except: 183 pass 184 try: 185 os.mkdir(path) 186 except: 187 pass 188 return path
189