Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

Changeset 505

Show
Ignore:
Timestamp:
09/23/07 22:49:29
Author:
fumanchu
Message:

Woof. Major API change to all DDL SM functions: the addition of a 'conflicts' argument (similar to and replacing the 'conflict_mode' arg of map). This necessitated tightening up some of the stores (like RAM and shelve) which didn't completely follow the spec on conflict detection and response. There's also a ton of docstring compliance in this changeset, as well as various fixups to other stores. Big ball o mud.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/dejavu/errors.py

    r504 r505  
     1"""Exception classes for Dejavu.""" 
     2 
    13 
    24class DejavuError(Exception): 
     
    3335    """ 
    3436    pass 
     37 
     38 
     39 
     40# -------------------------- Conflict handling -------------------------- # 
     41 
     42 
     43class ConflictHandler(object): 
     44    """Dispatcher for behavior upon encountering a mapping conflict. 
     45     
     46    When MappingErrors are encountered, a singleton instance of this class 
     47    is used to respond to the error based upon a supplied mode. You can add 
     48    failure modes (and their corresponding behaviors) by adding methods to 
     49    this class. 
     50     
     51    In general, only leaf nodes need call this object; pure proxies and 
     52    storage mixers which simply pass calls through to (possibly multiple) 
     53    child storage nodes may safely rely on the behavior of their child 
     54    nodes by passing the 'conflicts' argument through to all children. 
     55    """ 
     56     
     57    def __call__(self, mode, msg): 
     58        """React to a conflict according to the given mode. 
     59         
     60        mode: This argument determines what happens when there are 
     61        discrepancies between the Dejavu model and the actual database. 
     62         
     63            If 'error' (the default), MappingError is raised for the 
     64            first issue and the call is aborted. 
     65             
     66            If 'warn', then a StorageWarning is raised (instead of an error) 
     67            for each issue, and the call is not aborted. This allows you to 
     68            see all errors at once, without having to stop and fix each one 
     69            and then execute the call again. 
     70             
     71            If 'repair', then each issue will be resolved by changing 
     72            the database to match the model. Not all calls support this 
     73            mode for all errors; any which do not support this mode will 
     74            error instead. 
     75             
     76            If 'ignore', any model conflicts are silently ignored. 
     77            Use of this mode causes mandelbugs. You have been warned. 
     78        """ 
     79        func = getattr(self, mode, None) 
     80        if func is None: 
     81            raise ValueError("Conflict mode %r not recognized." % mode) 
     82        return func(msg) 
     83     
     84    def warn(self, msg): 
     85        import warnings 
     86        warnings.warn(msg, StorageWarning) 
     87     
     88    def ignore(self, msg): 
     89        pass 
     90     
     91    def error(self, msg): 
     92        raise MappingError(msg) 
     93 
     94conflict = ConflictHandler() 
     95 
  • trunk/dejavu/schemas.py

    r504 r505  
    6565        """Retrieve our DeployedVersion unit, optionally creating it.""" 
    6666        if not self.store.has_storage(DeployedVersion): 
    67             self.store.create_storage(DeployedVersion
     67            self.store.map([DeployedVersion], conflicts='repair'
    6868         
    6969        if self._deployed_unit is None: 
     
    143143        easily if you run assert_storage before the upgrade methods. 
    144144        """ 
    145         self.store.map_all(conflict_mode='repair') 
     145        self.store.map_all(conflicts='repair') 
    146146     
    147147    def assert_version(self): 
  • trunk/dejavu/storage/__init__.py

    r504 r505  
    66except NameError: 
    77    from sets import Set as set 
    8 import warnings 
    98 
    109import dejavu 
     
    4847        self.logflags = logflags.ERROR + logflags.IO 
    4948     
    50     def shutdown(self): 
     49    def shutdown(self, conflicts='error'): 
     50        """Shut down all connections to internal storage. 
     51         
     52        conflicts: see errors.conflict. 
     53        """ 
    5154        pass 
    5255     
     
    97100        raise KeyError("No registered class found for '%s'." % classname) 
    98101     
    99     def map(self, classes, conflict_mode='error'): 
     102    def map(self, classes, conflicts='error'): 
    100103        """Map classes to internal storage. 
    101104         
    102         conflict_mode: This argument determines what happens when there are 
    103         discrepancies between the Dejavu model and the actual storage. 
    104              
    105             If 'error' (the default), MappingError is raised for the 
    106             first issue and the mapping process is aborted. 
    107              
    108             If 'warn', then a warning is raised (instead of an error) 
    109             for each issue, and the mapping process is not aborted. 
    110             This allows you to see all errors at once, without having 
    111             to stop and fix each one and then execute the process again. 
    112              
    113             If 'repair', then each issue will be resolved by changing 
    114             storage to match the model. 
     105        conflicts: see errors.conflict. 
    115106        """ 
    116107        for cls in classes: 
    117108            if not self.has_storage(cls): 
    118                 if conflict_mode == 'repair': 
     109                if conflicts == 'repair': 
    119110                    self.create_storage(cls) 
    120111                else: 
    121                     msg = "%s: no storage found." % cls.__name__ 
    122                     if conflict_mode == 'warn': 
    123                         warnings.warn(msg) 
    124                     else: 
    125                         raise errors.MappingError(msg) 
    126      
    127     def map_all(self, conflict_mode='error'): 
     112                    errors.conflict(conflicts, 
     113                                    "%s: no storage found." % cls.__name__) 
     114     
     115    def map_all(self, conflicts='error'): 
    128116        """Map all registered classes to internal storage structures. 
    129117         
     
    135123        to call it very often (once at app startup is usually enough). 
    136124         
    137         conflict_mode: This argument determines what happens when there are 
    138         discrepancies between the Dejavu model and the actual database. 
    139              
    140             If 'error' (the default), MappingError is raised for the 
    141             first issue and the sync process is aborted. 
    142              
    143             If 'warn', then a warning is raised (instead of an error) 
    144             for each issue, and the sync process is not aborted. This 
    145             allows you to see all errors at once, without having to stop 
    146             and fix each one and then execute the process again. 
    147              
    148             If 'repair', then each issue will be resolved by changing 
    149             the database to match the model. 
    150         """ 
    151         self.map(self.classes, conflict_mode=conflict_mode) 
    152      
    153     def create_database(self): 
     125        conflicts: see errors.conflict. 
     126        """ 
     127        self.map(self.classes, conflicts=conflicts) 
     128     
     129    def create_database(self, conflicts='error'): 
     130        """Create internal structures for the entire database. 
     131         
     132        conflicts: see errors.conflict. 
     133        """ 
    154134        raise NotImplementedError("%s has no create_database method." 
    155135                                  % self.__class__) 
    156136     
    157     def drop_database(self): 
     137    def drop_database(self, conflicts='error'): 
     138        """Destroy internal structures for the entire database. 
     139         
     140        conflicts: see errors.conflict. 
     141        """ 
    158142        raise NotImplementedError("%s has no drop_database method." 
    159143                                  % self.__class__) 
    160144     
    161     def create_storage(self, cls): 
     145    def create_storage(self, cls, conflicts='error'): 
     146        """Create internal structures for the given class. 
     147         
     148        conflicts: see errors.conflict. 
     149        """ 
    162150        raise NotImplementedError("%s has no create_storage method." 
    163151                                  % self.__class__) 
    164152     
    165153    def has_storage(self, cls): 
     154        """If storage structures exist for the given class, return True.""" 
    166155        raise NotImplementedError("%s has no has_storage method." 
    167156                                  % self.__class__) 
    168157     
    169     def drop_storage(self, cls): 
     158    def drop_storage(self, cls, conflicts='error'): 
     159        """Destroy internal structures for the given class. 
     160         
     161        conflicts: see errors.conflict. 
     162        """ 
    170163        raise NotImplementedError("%s has no drop_storage method." 
    171164                                  % self.__class__) 
    172165     
    173     def add_property(self, cls, name): 
     166    def add_property(self, cls, name, conflicts='error'): 
     167        """Add internal structures for the given property. 
     168         
     169        conflicts: see errors.conflict. 
     170        """ 
    174171        raise NotImplementedError("%s has no add_property method." 
    175172                                  % self.__class__) 
    176173     
    177174    def has_property(self, cls, name): 
     175        """If storage structures exist for the given property, return True.""" 
    178176        raise NotImplementedError("%s has no has_property method." 
    179177                                  % self.__class__) 
    180178     
    181     def drop_property(self, cls, name): 
     179    def drop_property(self, cls, name, conflicts='error'): 
     180        """Destroy internal structures for the given property. 
     181         
     182        conflicts: see errors.conflict. 
     183        """ 
    182184        raise NotImplementedError("%s has no drop_property method." 
    183185                                  % self.__class__) 
    184186     
    185     def rename_property(self, cls, oldname, newname): 
     187    def rename_property(self, cls, oldname, newname, conflicts='error'): 
     188        """Rename internal structures for the given property. 
     189         
     190        conflicts: see errors.conflict. 
     191        """ 
    186192        raise NotImplementedError("%s has no rename_property method." 
    187193                                  % self.__class__) 
    188194     
    189     def add_index(self, cls, name): 
     195    def add_index(self, cls, name, conflicts='error'): 
     196        """Add an index to the given property. 
     197         
     198        conflicts: see errors.conflict. 
     199        """ 
    190200        raise NotImplementedError("%s has no add_index method." 
    191201                                  % self.__class__) 
    192202     
    193203    def has_index(self, cls, name): 
     204        """If an index exists for the given property, return True.""" 
    194205        raise NotImplementedError("%s has no has_index method." 
    195206                                  % self.__class__) 
    196207     
    197     def drop_index(self, cls, name): 
     208    def drop_index(self, cls, name, conflicts='error'): 
     209        """Destroy any index on the given property. 
     210         
     211        conflicts: see errors.conflict. 
     212        """ 
    198213        raise NotImplementedError("%s has no drop_index method." 
    199214                                  % self.__class__) 
     
    558573     
    559574    def xrecall(self, classes, expr=None, order=None, limit=None, offset=None): 
     575        """Return an iterable of Units.""" 
    560576        if isinstance(classes, dejavu.UnitJoin): 
    561577            for unitrow in self._xmultirecall(classes, expr, order=order, 
     
    608624        return self.nextstore._xmultirecall(classes, expr, order, limit, offset) 
    609625     
    610     def count(self, cls, expr=None): 
    611         """Number of Units of the given cls which match the given expr.""" 
    612         if self.logflags & logflags.RECALL: 
    613             self.log(logflags.RECALL.message(cls, ('count', expr))) 
    614         return self.nextstore.count(cls, expr) 
    615      
    616626    #                               Schemas                               # 
    617627     
    618     def map(self, classes, conflict_mode='error'): 
     628    def map(self, classes, conflicts='error'): 
    619629        """Map classes to internal storage. 
    620630         
    621         conflict_mode: This argument determines what happens when there are 
    622         discrepancies between the Dejavu model and the actual database. 
    623              
    624             If 'error' (the default), MappingError is raised for the 
    625             first issue and the sync process is aborted. 
    626              
    627             If 'warn', then a warning is raised (instead of an error) 
    628             for each issue, and the sync process is not aborted. This 
    629             allows you to see all errors at once, without having to stop 
    630             and fix each one and then execute the process again. 
    631              
    632             If 'repair', then each issue will be resolved by changing 
    633             the database to match the model. 
    634         """ 
    635         self.nextstore.map(classes, conflict_mode) 
    636      
    637     def create_database(self): 
     631        conflict: see errors.conflict. 
     632        """ 
     633        self.nextstore.map(classes, conflicts=conflicts) 
     634     
     635    def create_database(self, conflicts='error'): 
     636        """Create internal structures for the entire database. 
     637         
     638        conflicts: see errors.conflict. 
     639        """ 
    638640        if self.logflags & logflags.DDL: 
    639641            self.log(logflags.DDL.message("create database")) 
    640         self.nextstore.create_database() 
    641      
    642     def drop_database(self): 
     642        self.nextstore.create_database(conflicts=conflicts) 
     643     
     644    def drop_database(self, conflicts='error'): 
     645        """Destroy internal structures for the entire database. 
     646         
     647        conflicts: see errors.conflict. 
     648        """ 
    643649        if self.logflags & logflags.DDL: 
    644650            self.log(logflags.DDL.message("drop database")) 
    645         self.nextstore.drop_database() 
    646      
    647     def create_storage(self, cls): 
     651        self.nextstore.drop_database(conflicts=conflicts) 
     652     
     653    def create_storage(self, cls, conflicts='error'): 
     654        """Create internal structures for the given class. 
     655         
     656        conflicts: see errors.conflict. 
     657        """ 
    648658        if self.logflags & logflags.DDL: 
    649659            self.log(logflags.DDL.message("create storage %r" % cls)) 
     
    651661     
    652662    def has_storage(self, cls): 
     663        """If storage structures exist for the given class, return True.""" 
    653664        return self.nextstore.has_storage(cls) 
    654665     
    655     def drop_storage(self, cls): 
     666    def drop_storage(self, cls, conflicts='error'): 
     667        """Destroy internal structures for the given class. 
     668         
     669        conflicts: see errors.conflict. 
     670        """ 
    656671        if self.logflags & logflags.DDL: 
    657672            self.log(logflags.DDL.message("drop storage %r" % cls)) 
    658         self.nextstore.drop_storage(cls) 
    659      
    660     def add_property(self, cls, name): 
     673        self.nextstore.drop_storage(cls, conflicts=conflicts) 
     674     
     675    def add_property(self, cls, name, conflicts='error'): 
     676        """Add internal structures for the given property. 
     677         
     678        conflicts: see errors.conflict. 
     679        """ 
    661680        if self.logflags & logflags.DDL: 
    662681            self.log(logflags.DDL.message("add property %r %r" % (cls, name))) 
    663         self.nextstore.add_property(cls, name) 
    664      
    665     def drop_property(self, cls, name): 
     682        self.nextstore.add_property(cls, name, conflicts=conflicts) 
     683     
     684    def has_property(self, cls, name): 
     685        """If storage structures exist for the given property, return True.""" 
     686        return self.nextstore.has_property(cls, name) 
     687     
     688    def drop_property(self, cls, name, conflicts='error'): 
     689        """Destroy internal structures for the given property. 
     690         
     691        conflicts: see errors.conflict. 
     692        """ 
    666693        if self.logflags & logflags.DDL: 
    667694            self.log(logflags.DDL.message("drop property %r %r" % (cls, name))) 
    668         self.nextstore.drop_property(cls, name) 
    669      
    670     def rename_property(self, cls, oldname, newname): 
     695        self.nextstore.drop_property(cls, name, conflicts=conflicts) 
     696     
     697    def rename_property(self, cls, oldname, newname, conflicts='error'): 
     698        """Rename internal structures for the given property. 
     699         
     700        conflicts: see errors.conflict. 
     701        """ 
    671702        if self.logflags & logflags.DDL: 
    672703            self.log(logflags.DDL.message("rename property %r from %r to %r" % 
    673704                                 (cls, oldname, newname))) 
    674         self.nextstore.rename_property(cls, oldname, newname) 
    675      
    676     def shutdown(self): 
    677         self.nextstore.shutdown() 
     705        self.nextstore.rename_property(cls, oldname, newname, conflicts=conflicts) 
     706     
     707    def shutdown(self, conflicts='error'): 
     708        """Shut down all connections to internal storage. 
     709         
     710        conflicts: see errors.conflict. 
     711        """ 
     712        self.nextstore.shutdown(conflicts=conflicts) 
     713     
     714    def add_index(self, cls, name, conflicts='error'): 
     715        """Add an index to the given property. 
     716         
     717        conflicts: see errors.conflict. 
     718        """ 
     719        self.nextstore.add_index(cls, name, conflicts=conflicts) 
     720     
     721    def has_index(self, cls, name): 
     722        """If an index exists for the given property, return True.""" 
     723        return self.nextstore.has_index(cls, name) 
     724     
     725    def drop_index(self, cls, name, conflicts='error'): 
     726        """Destroy any index on the given property. 
     727         
     728        conflicts: see errors.conflict. 
     729        """ 
     730        self.nextstore.drop_index(cls, name, conflicts=conflicts) 
    678731 
    679732 
     
    773826            self.cache.destroy(unit) 
    774827     
    775     def shutdown(self): 
    776         self.cache.shutdown() 
    777         self.nextstore.shutdown() 
    778      
    779     def add_property(self, cls, name): 
     828    def shutdown(self, conflicts='error'): 
     829        """Shut down all connections to internal storage. 
     830         
     831        conflicts: see errors.conflict. 
     832        """ 
     833        self.cache.shutdown(conflicts=conflicts) 
     834        self.nextstore.shutdown(conflicts=conflicts) 
     835     
     836    def add_property(self, cls, name, conflicts='error'): 
     837        """Add internal structures for the given property. 
     838         
     839        conflicts: see errors.conflict. 
     840        """ 
    780841        if self.logflags & logflags.DDL: 
    781842            self.log(logflags.DDL.message("add property %r %r" % (cls, name))) 
    782843        self.cache.dump(cls) 
    783         self.nextstore.add_property(cls, name) 
    784      
    785     def drop_property(self, cls, name): 
     844        self.nextstore.add_property(cls, name, conflicts=conflicts) 
     845     
     846    def drop_property(self, cls, name, conflicts='error'): 
     847        """Destroy internal structures for the given property. 
     848         
     849        conflicts: see errors.conflict. 
     850        """ 
    786851        if self.logflags & logflags.DDL: 
    787852            self.log(logflags.DDL.message("drop property %r %r" % (cls, name))) 
    788853        self.cache.dump(cls) 
    789         self.nextstore.drop_property(cls, name) 
    790      
    791     def rename_property(self, cls, oldname, newname): 
     854        self.nextstore.drop_property(cls, name, conflicts=conflicts) 
     855     
     856    def rename_property(self, cls, oldname, newname, conflicts='error'): 
     857        """Rename internal structures for the given property. 
     858         
     859        conflicts: see errors.conflict. 
     860        """ 
    792861        if self.logflags & logflags.DDL: 
    793862            self.log(logflags.DDL.message("rename property %r from %r to %r" 
    794863                                 % (cls, oldname, newname))) 
    795864        self.cache.dump(cls) 
    796         self.nextstore.rename_property(cls, oldname, newname) 
    797      
    798     def create_database(self): 
    799         ProxyStorage.create_database(self) 
    800         self.cache.create_database() 
    801      
    802     def drop_database(self): 
    803         ProxyStorage.drop_database(self) 
    804         self.cache.drop_database() 
    805      
    806     def create_storage(self, cls): 
    807         ProxyStorage.create_storage(self, cls) 
    808         self.cache.create_storage(cls) 
    809      
    810     def drop_storage(self, cls): 
    811         ProxyStorage.drop_storage(self, cls) 
    812         self.cache.drop_storage(cls) 
    813      
    814     def map(self, classes, conflict_mode='error'): 
     865        self.nextstore.rename_property(cls, oldname, newname, conflicts=conflicts) 
     866     
     867    def create_database(self, conflicts='error'): 
     868        """Create internal structures for the entire database. 
     869         
     870        conflicts: see errors.conflict. 
     871        """ 
     872        ProxyStorage.create_database(self, conflicts=conflicts) 
     873        self.cache.create_database(conflicts=conflicts) 
     874     
     875    def drop_database(self, conflicts='error'): 
     876        """Destroy internal structures for the entire database. 
     877         
     878        conflicts: see errors.conflict. 
     879        """ 
     880        ProxyStorage.drop_database(self, conflicts=conflicts) 
     881        self.cache.drop_database(conflicts=conflicts) 
     882     
     883    def create_storage(self, cls, conflicts='error'): 
     884        """Create internal structures for the given class. 
     885         
     886        conflicts: see errors.conflict. 
     887        """ 
     888        ProxyStorage.create_storage(self, cls, conflicts=conflicts) 
     889        self.cache.create_storage(cls, conflicts=conflicts) 
     890     
     891    def drop_storage(self, cls, conflicts='error'): 
     892        """Destroy internal structures for the given class. 
     893         
     894        conflicts: see errors.conflict. 
     895        """ 
     896        ProxyStorage.drop_storage(self, cls, conflicts=conflicts) 
     897        self.cache.drop_storage(cls, conflicts=conflicts) 
     898     
     899    def map(self, classes, conflicts='error'): 
    815900        """Map classes to internal storage. 
    816901         
    817         conflict_mode: This argument determines what happens when there are 
    818         discrepancies between the Dejavu model and the actual storage. 
    819              
    820             If 'error' (the default), MappingError is raised for the 
    821             first issue and the mapping process is aborted. 
    822              
    823             If 'warn', then a warning is raised (instead of an error) 
    824             for each issue, and the mapping process is not aborted. 
    825             This allows you to see all errors at once, without having 
    826             to stop and fix each one and then execute the process again. 
    827              
    828             If 'repair', then each issue will be resolved by changing 
    829             storage to match the model. 
    830         """ 
    831         ProxyStorage.map(self, classes, conflict_mode) 
     902        conflicts: see errors.conflict. 
     903        """ 
     904        ProxyStorage.map(self, classes, conflicts=conflicts) 
    832905        for cls in classes: 
    833906            if not self.cache.has_storage(cls): 
    834                 self.cache.create_storage(cls
     907                self.cache.create_storage(cls, conflicts=conflicts
    835908 
    836909 
  • trunk/dejavu/storage/db.py

    r504 r505  
    3838import dejavu 
    3939from dejavu import storage, logflags, xray 
    40 from dejavu.errors import StorageWarning, MappingError 
     40from dejavu.errors import StorageWarning, MappingError, conflict 
    4141 
    4242 
     
    8080        return self.db.version() 
    8181     
    82     def shutdown(self): 
     82    def shutdown(self, conflicts='error'): 
     83        """Shut down all connections to internal storage. 
     84         
     85        conflicts: see errors.conflict. 
     86        """ 
    8387        self.db.connections.shutdown() 
    8488     
     
    359363    #                               Schemas                               # 
    360364     
    361     def create_database(self): 
     365    def create_database(self, conflicts='error'): 
     366        """Create internal structures for the entire database. 
     367         
     368        conflicts: see errors.conflict. 
     369        """ 
    362370        if self.logflags & logflags.DDL: 
    363371            self.log(logflags.DDL.message("create database")) 
    364         self.db.create() 
    365         self.schema.create() 
    366      
    367     def drop_database(self): 
     372         
     373        try: 
     374            self.db.create() 
     375            self.schema.create() 
     376        except geniusql.errors.MappingError, x: 
     377            conflict(conflicts, str(x)) 
     378     
     379    def drop_database(self, conflicts='error'): 
     380        """Destroy internal structures for the entire database. 
     381         
     382        conflicts: see errors.conflict. 
     383        """ 
    368384        if self.logflags & logflags.DDL: 
    369385            self.log(logflags.DDL.message("drop database")) 
    370         self.schema.drop() 
    371         self.db.drop() 
     386         
     387        try: 
     388            self.schema.drop() 
     389        except geniusql.errors.MappingError, x: 
     390            conflict(conflicts, str(x)) 
     391         
     392        try: 
     393            self.db.drop() 
     394        except geniusql.errors.MappingError, x: 
     395            conflict(conflicts, str(x)) 
    372396     
    373397    def _make_table(self, cls): 
     
    388412        return t 
    389413     
    390     def create_storage(self, cls): 
    391         """Create storage for the given class.""" 
     414    def create_storage(self, cls, conflicts='error'): 
     415        """Create internal structures for the given class. 
     416         
     417        conflicts: see errors.conflict. 
     418        """ 
    392419        if self.logflags & logflags.DDL: 
    393420            self.log(logflags.DDL.message("create storage %s" % cls)) 
    394         # Attach to self.schema, which should call CREATE TABLE. 
    395         self.schema[cls.__name__] = self._make_table(cls) 
     421         
     422        try: 
     423            # Attach to self.schema, which should call CREATE TABLE. 
     424            self.schema[cls.__name__] = self._make_table(cls) 
     425        except geniusql.errors.MappingError, x: 
     426            conflict(conflicts, str(x)) 
    396427     
    397428    def _make_column(self, cls, key): 
     
    406437     
    407438    def has_storage(self, cls): 
     439        """If storage structures exist for the given class, return True.""" 
    408440        return cls.__name__ in self.schema 
    409441     
    410     def drop_storage(self, cls): 
     442    def drop_storage(self, cls, conflicts='error'): 
     443        """Destroy internal structures for the given class. 
     444         
     445        conflicts: see errors.conflict. 
     446        """ 
    411447        if self.logflags & logflags.DDL: 
    412448            self.log(logflags.DDL.message("drop storage %s" % cls)) 
    413         del self.schema[cls.__name__] 
    414      
    415     def rename_storage(self, oldname, newname): 
     449         
     450        try: 
     451            del self.schema[cls.__name__] 
     452        except geniusql.errors.MappingError, x: 
     453            conflict(conflicts, str(x)) 
     454     
     455    def rename_storage(self, oldname, newname, conflicts='error'): 
     456        """Rename internal structures for the given class. 
     457         
     458        conflicts: see errors.conflict. 
     459        """ 
    416460        if self.logflags & logflags.DDL: 
    417461            self.log(logflags.DDL.message("rename storage from %s to %s" 
    418462                                          % (oldname, newname))) 
    419         self.schema.rename(oldname, newname) 
    420      
    421     def add_property(self, cls, name): 
     463         
     464        try: 
     465            self.schema.rename(oldname, newname) 
     466        except geniusql.errors.MappingError, x: 
     467            conflict(conflicts, str(x)) 
     468     
     469    def add_property(self, cls, name, conflicts='error'): 
     470        """Add internal structures for the given property. 
     471         
     472        conflicts: see errors.conflict. 
     473        """ 
    422474        if self.logflags & logflags.DDL: 
    423475            self.log(logflags.DDL.message("add property %s %s" % 
    424476                                          (cls, name))) 
     477         
    425478        if not self.has_property(cls, name): 
    426             table = self.schema[cls.__name__] 
    427             table[name] = self._make_column(cls, name) 
     479            try: 
     480                table = self.schema[cls.__name__] 
     481                table[name] = self._make_column(cls, name) 
     482            except geniusql.errors.MappingError, x: 
     483                conflict(conflicts, str(x)) 
    428484     
    429485    def has_property(self, cls, name): 
     486        """If storage structures exist for the given property, return True.""" 
    430487        return name in self.schema[cls.__name__] 
    431488     
    432     def drop_property(self, cls, name): 
     489    def drop_property(self, cls, name, conflicts='error'): 
     490        """Destroy internal structures for the given property. 
     491         
     492        conflicts: see errors.conflict. 
     493        """ 
    433494        if self.logflags & logflags.DDL: 
    434495            self.log(logflags.DDL.message("drop property %s %s" % 
    435496                                          (cls, name))) 
    436497        if self.has_property(cls, name): 
    437             del self.schema[cls.__name__][name] 
    438      
    439     def rename_property(self, cls, oldname, newname): 
     498            try: 
     499                del self.schema[cls.__name__][name] 
     500            except geniusql.errors.MappingError, x: 
     501                conflict(conflicts, str(x)) 
     502     
     503    def rename_property(self, cls, oldname, newname, conflicts='error'): 
     504        """Rename internal structures for the given property. 
     505         
     506        conflicts: see errors.conflict. 
     507        """ 
    440508        if self.logflags & logflags.DDL: 
    441509            self.log(logflags.DDL.message( 
    442510                "rename property %s from %s to %s" % 
    443511                (cls, oldname, newname))) 
    444         t = self.schema[cls.__name__] 
    445          
    446         # Sometimes, a Dejavu Schema will change a code model first, and 
    447         # then change the database afterward. So it's possible that the 
    448         # column we're trying to rename hasn't been loaded, because the 
    449         # model layer no longer references it. So if table[oldname] 
    450         # raises a KeyError, try to find a column that matches oldkey. 
    451         tempcol = None 
    452         try: 
    453             t[oldname] 
    454         except KeyError: 
    455             c = [x for x in self.schema._get_columns(t.name) 
    456                  if x.name == self.schema._column_name(t.name, oldname)] 
    457             if not c: 
    458                 raise KeyError("Rename failed. Old column %r not found in %r." 
    459                                % (oldname, t.name)) 
    460             oldcol = c[0] 
    461             # Use the superclass call to avoid DROP COLUMN/ADD COLUMN. 
    462             dict.__setitem__(t, oldname, oldcol) 
    463          
    464         t.rename(oldname, newname) 
    465      
    466     def add_index(self, cls, name): 
    467         self.schema[cls.__name__].add_index(name) 
     512         
     513        try: 
     514            t = self.schema[cls.__name__] 
     515             
     516            # Sometimes, a Dejavu Schema will change a code model first, and 
     517            # then change the database afterward. So it's possible that the 
     518            # column we're trying to rename hasn't been loaded, because the 
     519            # model layer no longer references it. So if table[oldname] 
     520            # raises a KeyError, try to find a column that matches oldkey. 
     521            tempcol = None 
     522            try: 
     523                t[oldname] 
     524            except KeyError: 
     525                c = [x for x in self.schema._get_columns(t.name) 
     526                     if x.name == self.schema._column_name(t.name, oldname)] 
     527                if not c: 
     528                    raise KeyError("Rename failed. Old column %r not found in %r." 
     529                                   % (oldname, t.name)) 
     530                oldcol = c[0] 
     531                # Use the superclass call to avoid DROP COLUMN/ADD COLUMN. 
     532                dict.__setitem__(t, oldname, oldcol) 
     533             
     534            t.rename(oldname, newname) 
     535        except geniusql.errors.MappingError, x: 
     536            conflict(conflicts, str(x)) 
     537     
     538    def add_index(self, cls, name, conflicts='error'): 
     539        """Add an index for the given property. 
     540         
     541        conflicts: see errors.conflict. 
     542        """ 
     543        try: 
     544            self.schema[cls.__name__].add_index(name) 
     545        except geniusql.errors.MappingError, x: 
     546            conflict(conflicts, str(x)) 
    468547     
    469548    def has_index(self, cls, name): 
     549        """If an index exists for the given property, return True.""" 
    470550        return name in self.schema[cls.__name__].indices 
    471551     
    472     def drop_index(self, cls, name): 
    473         del self.schema[cls.__name__].indices[name] 
     552    def drop_index(self, cls, name, conflicts='error'): 
     553        """Destroy any index on the given property. 
     554         
     555        conflicts: see errors.conflict. 
     556        """ 
     557        try: 
     558            del self.schema[cls.__name__].indices[name] 
     559        except geniusql.errors.MappingError, x: 
     560            conflict(conflicts, str(x)) 
    474561     
    475562    auto_discover = True 
    476563     
    477     def map(self, classes, conflict_mode='error'): 
     564    def map(self, classes, conflicts='error'): 
    478565        """Map classes to internal storage. 
    479566         
     
    487574        and changes to the database are not expected outside the model. 
    488575         
    489         conflict_mode: This argument determines what happens when there are 
    490         discrepancies between the Dejavu model and the actual database. 
    491              
    492             If 'error' (the default), MappingError is raised for the 
    493             first issue and the sync process is aborted. 
    494              
    495             If 'warn', then a StorageWarning is raised (instead of an 
    496             error) for each issue, and the sync process is not aborted. 
    497             This allows you to see all errors at once, without having to 
    498             stop and fix each one and then execute the process again. 
    499              
    500             If 'repair', then each issue will be resolved by changing 
    501             the database to match the model. 
     576        conflicts: see errors.conflict. 
    502577        """ 
    503578        if self.auto_discover: 
    504579            self.db.discover_dbinfo() 
    505             self.sync(classes, conflict_mode
     580            self.sync(classes, conflicts
    506581        else: 
    507582            for cls in classes: 
     
    517592                dict.__setitem__(self.schema, cls.__name__, t) 
    518593     
    519     def sync(self, classes, conflict_mode='error'): 
     594    def sync(self, classes, conflicts='error'): 
    520595        """Map classes to existing Table objects (found via discovery). 
    521596         
    522         conflict_mode: This argument determines what happens when there are 
    523         discrepancies between the Dejavu model and the actual database. 
    524              
    525             If 'error' (the default), MappingError is raised for the 
    526             first issue and the sync process is aborted. 
    527              
    528             If 'warn', then a Storagewarning is raised (instead of an 
    529             error) for each issue, and the sync process is not aborted. 
    530             This allows you to see all errors at once, without having to 
    531             stop and fix each one and then execute the process again. 
    532              
    533             If 'repair', then each issue will be resolved by changing 
    534             the database to match the model. 
    535              
    536             If 'ignore', then each issue will be silently ignored. 
     597        conflicts: see errors.conflict. 
    537598        """ 
    538599        for cls in classes: 
     
    542603                # without calling the expensive discover() func each time. 
    543604                continue 
    544             self._find_table(self.schema, cls, conflict_mode
    545      
    546     def _find_table(self, schema, cls, conflict_mode='error'): 
     605            self._find_table(self.schema, cls, conflicts=conflicts
     606     
     607    def _find_table(self, schema, cls, conflicts='error'): 
    547608        # This is broken out to make multi-schema subclasses easier to write. 
    548          
    549         def notify(msg): 
    550             if conflict_mode == 'warn': 
    551                 warnings.warn(msg, StorageWarning) 
    552             elif conflict_mode == 'ignore': 
    553                 pass 
    554             else: 
    555                 raise MappingError(msg) 
    556609         
    557610        # Try to find a matching Table object using the DB-side key. 
     
    569622            except geniusql.errors.MappingError: 
    570623                msg = "%s: no such table %r." % (clsname, tablename) 
    571                 if conflict_mode == 'repair': 
     624                if conflicts == 'repair': 
    572625                    self.create_storage(cls) 
    573626                    table = schema[clsname] 
    574627                else: 
    575                     notify(msg) 
     628                    conflict(conflicts, msg) 
    576629                    return 
    577630         
     
    586639            except KeyError, x: 
    587640                msg = "%s: no column found for %r." % (clsname, pkey) 
    588                 if conflict_mode == 'repair': 
     641                if conflicts == 'repair': 
    589642                    self.add_property(cls, pkey) 
    590643                    if pkey in cls.indices() and pkey not in table.indices: 
     
    592645                    col = table[pkey] 
    593646                else: 
    594                     notify(msg) 
     647                    conflict(conflicts, msg) 
    595648                    continue 
    596649             
     
    603656                       "column is not marked as a primary key." 
    604657                       % (clsname, pkey)) 
    605                 if conflict_mode == 'repair': 
     658                if conflicts == 'repair': 
    606659                    col.key = True 
    607660                    table.set_primary() 
    608661                else: 
    609                     notify(msg) 
     662                    conflict(conflicts, msg) 
    610663                    continue 
    611664            elif col.key and not pkey in cls.identifiers: 
     
    613666                       "column is marked as a primary key." 
    614667                       % (clsname, pkey)) 
    615                 if conflict_mode == 'repair': 
     668                if conflicts == 'repair': 
    616669                    col.key = False 
    617670                    # Just because the current pkey is not an identifier 
     
    619672                    table.set_primary() 
    620673                else: 
    621                     notify(msg) 
     674                    c