Edgewall Software

Ticket #9536: 9536-db-context-manager-r10102.patch

File 9536-db-context-manager-r10102.patch, 4.0 KB (added by cboos, 21 months ago)

db: introduce context managers for accessing Connection instances.

  • trac/db/api.py

    # HG changeset patch
    # Parent 6b65e6ec7bc777a8635ec1a19162e39c81b7c877
    db: introduce context managers for accessing `Connection` instances.
    
    This continues the work started in #8751.
    
    Part of #9636.
    
    diff -r 6b65e6ec7bc7 trac/db/api.py
    a b _transaction_local = ThreadLocal(db=None 
    3030def with_transaction(env, db=None): 
    3131    """Function decorator to emulate a context manager for database 
    3232    transactions. 
     33 
     34    :deprecated: use `env.db_{query|transaction}` 
    3335     
    3436    >>> def api_method(p1, p2): 
    3537    >>>     result[0] = value1 
    def get_read_db(env): 
    9092    return _transaction_local.db or DatabaseManager(env).get_connection() 
    9193 
    9294 
     95class TransactionContextMgr(object): 
     96    """Transactioned Database Context Manager 
     97 
     98    The outermost such context manager will either commit or rollback, 
     99    depending on the context being exited normally or after an exception. 
     100    """ 
     101    def __init__(self, env): 
     102        self._env = env 
     103 
     104    def __enter__(self): 
     105        db = _transaction_local.db 
     106        if not db: 
     107            _transaction_local.db = self._db = db = self._env.get_db_cnx() 
     108            self._env = None # we own the _transaction_local.db 
     109        return db 
     110 
     111    def __exit__(self, et, ev, tb):  
     112        if self._env is None:  
     113            _transaction_local.db = None 
     114            if et is None:  
     115                self._db.commit() 
     116            else:  
     117                self._db.rollback() 
     118 
     119class ReadOnlyConnection(object): 
     120    """Wrapper around connection objects suppressing commit/rollback methods. 
     121     
     122    :since 0.13: 
     123    """ 
     124    __slots__ = ('cnx', 'log') 
     125 
     126    def __init__(self, cnx, log=None): 
     127        self.cnx, self.log = cnx, log 
     128 
     129    def __getattr__(self, name): 
     130        if name in ('commit', 'rollback'): 
     131            raise AttributeError 
     132        return getattr(self.cnx, name) 
     133 
     134class QueryContextMgr(object): 
     135    """Database Context Manager for retrieving a Connection wrapper 
     136    with no commit or rollback""" 
     137    def __init__(self, env): 
     138        self._env = env 
     139 
     140    def __enter__(self): 
     141        db = _transaction_local.db 
     142        if not db: 
     143            db = self._env.get_db_cnx() 
     144        return ReadOnlyConnection(db) 
     145 
     146    def __exit__(self, et, ev, tb):  
     147        pass 
     148 
     149 
    93150class IDatabaseConnector(Interface): 
    94151    """Extension point interface for components that support the connection to 
    95152    relational databases.""" 
  • trac/env.py

    diff -r 6b65e6ec7bc7 trac/env.py
    a b from trac.cache import CacheManager 
    2525from trac.config import * 
    2626from trac.core import Component, ComponentManager, implements, Interface, \ 
    2727                      ExtensionPoint, TracError 
    28 from trac.db.api import DatabaseManager, get_read_db, with_transaction 
     28from trac.db.api import (DatabaseManager, QueryContextMgr,  
     29                         TransactionContextMgr, get_read_db, with_transaction) 
    2930from trac.util import copytree, create_file, get_pkginfo, makedirs 
    3031from trac.util.compat import any 
    3132from trac.util.concurrency import threading 
    class Environment(Component, ComponentMa 
    326327        return get_read_db(self) 
    327328 
    328329    def with_transaction(self, db=None): 
    329         """Decorator for transaction functions. 
    330  
    331         See `trac.db.api.with_transaction` for detailed documentation.""" 
     330        """Decorator for transaction functions (deprecated)""" 
    332331        return with_transaction(self, db) 
    333332 
    334333    def get_read_db(self): 
    class Environment(Component, ComponentMa 
    337336        See `trac.db.api.get_read_db` for detailed documentation.""" 
    338337        return get_read_db(self) 
    339338 
     339    @property 
     340    def db_query(self): 
     341        return QueryContextMgr(self) 
     342 
     343    @property 
     344    def db_transaction(self): 
     345        return TransactionContextMgr(self) 
     346 
    340347    def shutdown(self, tid=None): 
    341348        """Close the environment.""" 
    342349        RepositoryManager(self).shutdown(tid)