Ticket #89: pxd-cache.patch
| File pxd-cache.patch, 13.6 kB (added by robertwb, 3 months ago) |
|---|
-
a/Cython/Compiler/Code.py
old new 345 345 If name is provided, it is used as an identifier to avoid inserting 346 346 code twice. Otherwise, id(codetup) is used as such an identifier. 347 347 """ 348 if name is None: name = id(codetup)348 if name is None: name = codetup[0] 349 349 if self.check_utility_code_needed_and_register(name): 350 350 proto, _def = codetup 351 351 self.utilprotowriter.put(proto) -
a/Cython/Compiler/Main.py
old new 2 2 # Cython Top Level 3 3 # 4 4 5 import os, sys, re, codecs 5 import os, sys, re, codecs, copy 6 6 if sys.version_info[:2] < (2, 3): 7 7 sys.stderr.write("Sorry, Cython requires Python 2.3 or later\n") 8 8 sys.exit(1) 9 import cPickle as pickle 9 10 10 11 try: 11 12 set 12 13 except NameError: 13 14 # Python 2.3 14 15 from sets import Set as set 15 16 17 try: 18 import hashlib 19 except ImportError: 20 import md5 as hashlib 21 16 22 from time import time 17 23 import Code 18 24 import Errors … … 59 65 # include_directories [string] 60 66 # future_directives [object] 61 67 62 def __init__(self, include_directories, pragma_overrides ):68 def __init__(self, include_directories, pragma_overrides, pxd_cache=None): 63 69 #self.modules = {"__builtin__" : BuiltinScope()} 64 70 import Builtin, CythonScope 65 71 self.modules = {"__builtin__" : Builtin.builtin_scope} … … 73 79 standard_include_path = os.path.abspath( 74 80 os.path.join(os.path.dirname(__file__), '..', 'Includes')) 75 81 self.include_directories = include_directories + [standard_include_path] 82 self.pxd_cache = pxd_cache 83 76 84 77 85 def create_pipeline(self, pxd): 78 86 from Visitor import PrintTree … … 177 185 if Errors.num_errors == 0: 178 186 raise 179 187 return (err, data) 180 188 181 189 def find_module(self, module_name, 182 relative_to = None, pos = None, need_pxd = 1): 190 relative_to = None, pos = None, need_pxd = 1, 191 locate_only = 0): 183 192 # Finds and returns the module scope corresponding to 184 193 # the given relative or absolute module name. If this 185 194 # is the first time the module has been requested, finds … … 216 225 scope = scope.find_submodule(name) 217 226 if debug_find_module: 218 227 print("...scope =", scope) 219 if not scope.pxd_file_loaded :228 if not scope.pxd_file_loaded or locate_only: 220 229 if debug_find_module: 221 230 print("...pxd not loaded") 222 scope.pxd_file_loaded = 1223 231 if not pxd_pathname: 224 232 if debug_find_module: 225 233 print("...looking for pxd file") … … 228 236 print("......found ", pxd_pathname) 229 237 if not pxd_pathname and need_pxd: 230 238 error(pos, "'%s.pxd' not found" % module_name) 239 if locate_only: 240 # find_pxd_file is rather weak, so we pass this option to do 241 # a more extensive search. 242 return pxd_pathname 243 scope.pxd_file_loaded = 1 231 244 if pxd_pathname: 232 245 try: 233 246 if debug_find_module: 234 247 print("Context.find_module: Parsing %s" % pxd_pathname) 235 source_desc = FileSourceDescriptor(pxd_pathname) 236 err, result = self.process_pxd(source_desc, scope, module_name) 237 if err: 238 raise err 239 (pxd_codenodes, pxd_scope) = result 240 self.pxds[module_name] = (pxd_codenodes, pxd_scope) 248 result = None 249 if self.pxd_cache: 250 result = self.pxd_cache[pxd_pathname] 251 if result: 252 save = scope.module_cname, scope.method_table_cname 253 scope.__dict__ = result[1].__dict__ 254 scope.module_cname, scope.method_table_cname = save 255 if not result: 256 source_desc = FileSourceDescriptor(pxd_pathname) 257 err, result = self.process_pxd(source_desc, scope, module_name) 258 if err: 259 raise err 260 if self.pxd_cache: 261 pxd_codenodes, pxd_scope = result 262 result[1].cimported_module_filenames = [self.find_module(m.module_name, relative_to, pos = pos, locate_only = True) for m in result[1].cimported_modules] 263 result[1].cimported_module_filenames = [mf for mf in result[1].cimported_module_filenames if mf is not None] 264 self.pxd_cache.cache(pxd_pathname, result) 265 if not need_pxd or 1: 266 # We must be processing the corresponding pyx file, 267 # and don't want to contaminate the cached scope. 268 scope = copy.deepcopy(scope) 269 pxd_codenodes, pxd_scope = result 270 self.pxds[module_name] = result 241 271 except CompileError: 242 272 pass 243 273 return scope … … 490 520 return result 491 521 492 522 def run_pipeline(source, options, full_module_name = None): 523 524 options.pxd_cache_file = "pxd_cache.pickle" 525 if options.pxd_cache_file: 526 if os.path.exists(options.pxd_cache_file): 527 pxd_cache = pickle.load(open(options.pxd_cache_file)) 528 pxd_cache.clean() 529 pxd_cache.verify() 530 else: 531 pxd_cache = PxdCache() 532 else: 533 pxd_cache = None 534 493 535 # Set up context 494 context = Context(options.include_path, options.pragma_overrides )536 context = Context(options.include_path, options.pragma_overrides, pxd_cache) 495 537 496 538 # Set up source object 497 539 cwd = os.getcwd() … … 508 550 context.setup_errors(options) 509 551 err, enddata = context.run_pipeline(pipeline, source) 510 552 context.teardown_errors(err, options, result) 553 554 if pxd_cache and pxd_cache.is_dirty(): 555 pickle.dump(pxd_cache, open(options.pxd_cache_file, "w"), 2) 556 511 557 return result 512 558 513 559 #------------------------------------------------------------------------ … … 675 721 return compile_single(source, options, full_module_name) 676 722 else: 677 723 return compile_multiple(source, options) 724 725 726 727 class PxdDependancy: 728 729 _file_digest_cache = {} 730 731 def __init__(self, path): 732 self.path = path 733 self.digest = self.file_digest(path) 734 self.dependants = [] 735 self.valid = True 736 737 def file_digest(self, pathname): 738 digest = self._file_digest_cache.get(pathname) 739 if digest is None: 740 m = hashlib.md5() 741 m.update(open(pathname).read()) 742 digest = self._file_digest_cache[pathname] = m.hexdigest() 743 return digest 744 745 def verify(self): 746 if not self.valid: 747 return False 748 elif not os.path.exists(self.path): 749 return False 750 else: 751 return self.digest == self.file_digest(self.path) 752 753 754 class PxdCache: 755 """ 756 This class stores caches the parse results of .pxd files, verifying that 757 they are fresh using md5 hashes. 758 """ 759 _file_digest_cache = {} 760 761 def __init__(self): 762 self.dirty = False 763 self.pxds = {} 764 self.deps = {} 765 766 def clean(self): 767 self.dirty = False 768 769 def is_dirty(self): 770 return self.dirty 771 772 def __getitem__(self, module_filepath): 773 return self.pxds.get(module_filepath) 774 775 def cache(self, path, (pxd_codenodes, pxd_scope)): 776 self.dirty = True 777 pxd_scope.filepath = path 778 self.pxds[path] = pxd_codenodes, pxd_scope 779 dependancies = [path] + pxd_scope.included_files + pxd_scope.cimported_module_filenames 780 for dep in dependancies: 781 data = self.deps.get(dep) 782 if data is None: 783 data = self.deps[dep] = PxdDependancy(dep) 784 data.dependants.append(path) 785 786 def verify(self): 787 PxdDependancy._file_digest_cache = {} 788 bad = [] 789 for dep, dep_data in self.deps.items(): 790 if not dep_data.verify(): 791 bad.append((dep, dep_data)) 792 if bad: 793 self.dirty = True 794 while bad: 795 dep, dep_data = bad.pop() 796 del self.deps[dep] 797 if self.pxds.has_key(dep): 798 del self.pxds[dep] 799 for depend in dep_data.dependants: 800 depend_data = self.deps.get(depend) 801 if depend_data is not None and depend_data.valid: 802 depend_data.valid = False 803 bad.append((depend, depend_data)) 804 805 678 806 679 807 #------------------------------------------------------------------------ 680 808 # -
a/Cython/Compiler/ModuleNode.py
old new 64 64 if self.has_imported_c_functions(): 65 65 self.module_temp_cname = env.allocate_temp_pyobject() 66 66 env.release_temp(self.module_temp_cname) 67 if options.recursive :67 if options.recursive or 1: 68 68 self.generate_dep_file(env, result) 69 69 self.generate_c_code(env, options, result) 70 70 self.generate_h_code(env, options, result) -
a/Cython/Compiler/PyrexTypes.py
old new 7 7 import copy 8 8 9 9 10 class BaseType: 10 _type_cache = {} 11 12 class BaseTypeKey(object): 13 14 def __init__(self, cls, args, kwds): 15 self.data = cls, args, tuple(kwds.items()) 16 try: 17 hash(self.data) 18 except: 19 targs = [] 20 for a in self.data[1]: 21 if isinstance(a, list): 22 a = tuple(a) 23 targs.append(a) 24 self.data = self.data[0], tuple(targs), self.data[2] 25 26 def __hash__(self): 27 return hash(self.data) 28 29 def __cmp__(left, right): 30 c = cmp(type(left), type(right)) 31 if c == 0: 32 return cmp(left.data, right.data) 33 else: 34 return c 35 36 class BaseType(object): 11 37 # 12 38 # Base class for all Pyrex types including pseudo-types. 39 40 def __new__(cls, *args, **kwds): 41 if issubclass(cls, (PyExtensionType, BufferType)): 42 return super(BaseType, cls).__new__(cls, *args, **kwds) 43 else: 44 if len(args) == 1 and isinstance(args[0], BaseTypeKey): 45 key = args[0] 46 args, kwds = key.data[1], dict(key.data[2]) 47 else: 48 key = BaseTypeKey(cls, args, kwds) 49 t = _type_cache.get(key) 50 if t is None: 51 t = _type_cache[key] = super(BaseType, cls).__new__(cls, *args, **kwds) 52 t._key = key 53 return t 54 55 def __getnewargs__(self): 56 if isinstance(self, (PyExtensionType, BufferType)): 57 return () 58 else: 59 return (self._key,) 13 60 14 61 def cast_code(self, expr_code): 15 62 return "((%s)%s)" % (self.declaration_code(""), expr_code) … … 116 163 return self.same_as_resolved_type(other_type.resolve(), **kwds) 117 164 118 165 def same_as_resolved_type(self, other_type): 166 if str(self) == str(other_type) and self != other_type: 167 print self, id(self), type(self) 168 print other_type, id(other_type), type(other_type) 169 import cPickle as pickle 170 print id(pickle.loads(pickle.dumps(self, 2))) 171 print id(pickle.loads(pickle.dumps(other_type, 2))) 172 print self.has_attributes 173 # print self._key, other_type._key 119 174 return self == other_type or other_type is error_type 120 175 121 176 def subtype_of(self, other_type): … … 156 211 def __init__(self, cname, base_type): 157 212 self.typedef_cname = cname 158 213 self.typedef_base_type = base_type 214 215 def __setstate__(self, state): 216 self.__dict__ = state 159 217 160 218 def resolve(self): 161 219 return self.typedef_base_type.resolve() … … 207 265 208 266 is_buffer = 1 209 267 writable = True 268 210 269 def __init__(self, base, dtype, ndim, mode, negative_indices, cast): 211 270 self.base = base 212 271 self.dtype = dtype … … 215 274 self.mode = mode 216 275 self.negative_indices = negative_indices 217 276 self.cast = cast 277 278 def __setstate__(self, state): 279 self.__dict__ = state 218 280 219 281 def as_argument_type(self): 220 282 return self … … 238 300 pymemberdef_typecode = "T_OBJECT" 239 301 buffer_defaults = None 240 302 typestring = "O" 241 303 242 304 def __str__(self): 243 305 return "Python object" 244 306 -
a/Cython/Includes/python_version.pxd
old new 28 28 # 0xC (release candidate) 29 29 # 0xF (final) 30 30 31 char []PY_VERSION32 char []PY_PATCHLEVEL_REVISION31 char* PY_VERSION 32 char* PY_PATCHLEVEL_REVISION
