Ticket #89: pxd-cache.patch

File pxd-cache.patch, 13.6 kB (added by robertwb, 3 months ago)

DO NOT APPLY

  • a/Cython/Compiler/Code.py

    old new  
    345345        If name is provided, it is used as an identifier to avoid inserting 
    346346        code twice. Otherwise, id(codetup) is used as such an identifier. 
    347347        """ 
    348         if name is None: name = id(codetup) 
     348        if name is None: name = codetup[0] 
    349349        if self.check_utility_code_needed_and_register(name): 
    350350            proto, _def = codetup 
    351351            self.utilprotowriter.put(proto) 
  • a/Cython/Compiler/Main.py

    old new  
    22#   Cython Top Level 
    33# 
    44 
    5 import os, sys, re, codecs 
     5import os, sys, re, codecs, copy 
    66if sys.version_info[:2] < (2, 3): 
    77    sys.stderr.write("Sorry, Cython requires Python 2.3 or later\n") 
    88    sys.exit(1) 
     9import cPickle as pickle 
    910 
    1011try: 
    1112    set 
    1213except NameError: 
    1314    # Python 2.3 
    1415    from sets import Set as set 
    15  
     16     
     17try: 
     18    import hashlib 
     19except ImportError: 
     20    import md5 as hashlib 
     21     
    1622from time import time 
    1723import Code 
    1824import Errors 
     
    5965    #  include_directories   [string] 
    6066    #  future_directives     [object] 
    6167     
    62     def __init__(self, include_directories, pragma_overrides): 
     68    def __init__(self, include_directories, pragma_overrides, pxd_cache=None): 
    6369        #self.modules = {"__builtin__" : BuiltinScope()} 
    6470        import Builtin, CythonScope 
    6571        self.modules = {"__builtin__" : Builtin.builtin_scope} 
     
    7379        standard_include_path = os.path.abspath( 
    7480            os.path.join(os.path.dirname(__file__), '..', 'Includes')) 
    7581        self.include_directories = include_directories + [standard_include_path] 
     82        self.pxd_cache = pxd_cache 
     83         
    7684 
    7785    def create_pipeline(self, pxd): 
    7886        from Visitor import PrintTree 
     
    177185            if Errors.num_errors == 0: 
    178186                raise 
    179187        return (err, data) 
    180  
     188         
    181189    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): 
    183192        # Finds and returns the module scope corresponding to 
    184193        # the given relative or absolute module name. If this 
    185194        # is the first time the module has been requested, finds 
     
    216225                scope = scope.find_submodule(name) 
    217226        if debug_find_module: 
    218227            print("...scope =", scope) 
    219         if not scope.pxd_file_loaded
     228        if not scope.pxd_file_loaded or locate_only
    220229            if debug_find_module: 
    221230                print("...pxd not loaded") 
    222             scope.pxd_file_loaded = 1 
    223231            if not pxd_pathname: 
    224232                if debug_find_module: 
    225233                    print("...looking for pxd file") 
     
    228236                    print("......found ", pxd_pathname) 
    229237                if not pxd_pathname and need_pxd: 
    230238                    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 
    231244            if pxd_pathname: 
    232245                try: 
    233246                    if debug_find_module: 
    234247                        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 
    241271                except CompileError: 
    242272                    pass 
    243273        return scope 
     
    490520    return result 
    491521 
    492522def 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 
    493535    # Set up context 
    494     context = Context(options.include_path, options.pragma_overrides
     536    context = Context(options.include_path, options.pragma_overrides, pxd_cache
    495537 
    496538    # Set up source object 
    497539    cwd = os.getcwd() 
     
    508550    context.setup_errors(options) 
    509551    err, enddata = context.run_pipeline(pipeline, source) 
    510552    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     
    511557    return result 
    512558 
    513559#------------------------------------------------------------------------ 
     
    675721        return compile_single(source, options, full_module_name) 
    676722    else: 
    677723        return compile_multiple(source, options) 
     724         
     725 
     726 
     727class 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 
     754class 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 
    678806 
    679807#------------------------------------------------------------------------ 
    680808# 
  • a/Cython/Compiler/ModuleNode.py

    old new  
    6464        if self.has_imported_c_functions(): 
    6565            self.module_temp_cname = env.allocate_temp_pyobject() 
    6666            env.release_temp(self.module_temp_cname) 
    67         if options.recursive
     67        if options.recursive or 1
    6868            self.generate_dep_file(env, result) 
    6969        self.generate_c_code(env, options, result) 
    7070        self.generate_h_code(env, options, result) 
  • a/Cython/Compiler/PyrexTypes.py

    old new  
    77import copy 
    88 
    99 
    10 class BaseType: 
     10_type_cache = {} 
     11 
     12class 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 
     36class BaseType(object): 
    1137    # 
    1238    #  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,) 
    1360 
    1461    def cast_code(self, expr_code): 
    1562        return "((%s)%s)" % (self.declaration_code(""), expr_code) 
     
    116163        return self.same_as_resolved_type(other_type.resolve(), **kwds) 
    117164     
    118165    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 
    119174        return self == other_type or other_type is error_type 
    120175     
    121176    def subtype_of(self, other_type): 
     
    156211    def __init__(self, cname, base_type): 
    157212        self.typedef_cname = cname 
    158213        self.typedef_base_type = base_type 
     214         
     215    def __setstate__(self, state): 
     216        self.__dict__ = state 
    159217     
    160218    def resolve(self): 
    161219        return self.typedef_base_type.resolve() 
     
    207265 
    208266    is_buffer = 1 
    209267    writable = True 
     268     
    210269    def __init__(self, base, dtype, ndim, mode, negative_indices, cast): 
    211270        self.base = base 
    212271        self.dtype = dtype 
     
    215274        self.mode = mode 
    216275        self.negative_indices = negative_indices 
    217276        self.cast = cast 
     277     
     278    def __setstate__(self, state): 
     279        self.__dict__ = state 
    218280     
    219281    def as_argument_type(self): 
    220282        return self 
     
    238300    pymemberdef_typecode = "T_OBJECT" 
    239301    buffer_defaults = None 
    240302    typestring = "O" 
    241      
     303 
    242304    def __str__(self): 
    243305        return "Python object" 
    244306     
  • a/Cython/Includes/python_version.pxd

    old new  
    2828    #    0xC (release candidate) 
    2929    #    0xF (final) 
    3030 
    31     char[] PY_VERSION 
    32     char[] PY_PATCHLEVEL_REVISION 
     31    char* PY_VERSION 
     32    char* PY_PATCHLEVEL_REVISION